diff --git a/Android/ActionBarSherlockTabBar/Example project/.classpath b/Android/ActionBarSherlockTabBar/Example project/.classpath new file mode 100644 index 0000000..d368bbd --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Android/ActionBarSherlockTabBar/Example project/.gitignore b/Android/ActionBarSherlockTabBar/Example project/.gitignore new file mode 100644 index 0000000..9902c93 --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/.gitignore @@ -0,0 +1,2 @@ +bin/ +gen/ \ No newline at end of file diff --git a/Android/ActionBarSherlockTabBar/Example project/.project b/Android/ActionBarSherlockTabBar/Example project/.project new file mode 100644 index 0000000..2a5afba --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/.project @@ -0,0 +1,33 @@ + + + PhoneGapAndroidTest + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/Android/ActionBarSherlockTabBar/Example project/.settings/org.eclipse.jdt.core.prefs b/Android/ActionBarSherlockTabBar/Example project/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..54e493c --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/Android/ActionBarSherlockTabBar/Example project/AndroidManifest.xml b/Android/ActionBarSherlockTabBar/Example project/AndroidManifest.xml new file mode 100644 index 0000000..3e20c4e --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/ActionBarSherlockTabBar/Example project/assets/www/ActionBarSherlockTabBar.js b/Android/ActionBarSherlockTabBar/Example project/assets/www/ActionBarSherlockTabBar.js new file mode 100644 index 0000000..f8ff4ed --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/assets/www/ActionBarSherlockTabBar.js @@ -0,0 +1,25 @@ +// Usage: actionBarSherlockTabBar = cordova.require('cordova/plugin/actionBarSherlockTabBar'); +cordova.define('cordova/plugin/actionBarSherlockTabBar', function(require, exports, module) { + var exec = require('cordova/exec'); + + var ActionBarSherlockTabBar = function() {} + + ActionBarSherlockTabBar.prototype.setTabSelectedListener = function(callback) { + if(typeof callback != 'function') + throw 'ActionBarSherlockTabBar.setTabSelectedListener: Callback not a function' + + exec(callback, + function() { /* log error here?! */ }, + 'ActionBarSherlockTabBar', + 'setTabSelectedListener', + []) + } + + module.exports = new ActionBarSherlockTabBar(); + + exec(null, + null, + 'ActionBarSherlockTabBar', + '_init', + []) +}); \ No newline at end of file diff --git a/Android/ActionBarSherlockTabBar/Example project/assets/www/cordova-2.1.0.js b/Android/ActionBarSherlockTabBar/Example project/assets/www/cordova-2.1.0.js new file mode 100644 index 0000000..9e7fa8e --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/assets/www/cordova-2.1.0.js @@ -0,0 +1,5814 @@ +// commit 143f5221a6251c9cbccdedc57005c61551b97f12 + +// File generated at :: Wed Sep 12 2012 12:51:58 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code + */ + fireDocumentEvent: function(type, data, bNoDetach) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + if( bNoDetach ) { + documentEventHandlers[type].fire(evt); + } + else { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'), + nextGuid = 1; + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + var headers = null; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + headers = options.headers; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + * @param headers {Object} Keys are header names, values are header values. Multiple + * headers of the same name are not supported. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; + this.headers = headers || null; +}; + +module.exports = FileUploadOptions; + +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger than file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(null, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param msgType The 'type' of update this is + * @param value Use of value is determined by the msgType + */ +Media.onStatus = function(id, msgType, value) { + + var media = mediaObjects[id]; + + if(media) { + switch(msgType) { + case Media.MEDIA_STATE : + media.statusCallback && media.statusCallback(value); + if(value == Media.MEDIA_STOPPED) { + media.successCallback && media.successCallback(); + } + break; + case Media.MEDIA_DURATION : + media._duration = value; + break; + case Media.MEDIA_ERROR : + media.errorCallback && media.errorCallback(value); + break; + case Media.MEDIA_POSITION : + media._position = Number(value); + break; + default : + console && console.error && console.error("Unhandled Media.onStatus :: " + msgType); + break; + } + } + else { + console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id); + } + +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. +*/ +/* + According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror + We should never be creating these objects, we should just implement the interface + which has 1 property for an instance, 'code' + + instead of doing : + errorCallbackFunction( new MediaError(3,'msg') ); +we should simply use a literal : + errorCallbackFunction( {'code':3} ); + */ + +if(!MediaError) { + var MediaError = function(code, msg) { + this.code = (typeof code != 'undefined') ? code : null; + this.message = msg || ""; // message is NON-standard! do not use! + }; +} + +MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0; +MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1; +MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2; +MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4; +// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below. +// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes +MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4; + +module.exports = MediaError; + +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +module.exports = MediaFile; + +}); + +// file: lib/common/plugin/MediaFileData.js +define("cordova/plugin/MediaFileData", function(require, exports, module) { +/** + * MediaFileData encapsulates format information of a media file. + * + * @param {DOMString} codecs + * @param {long} bitrate + * @param {long} height + * @param {long} width + * @param {float} duration + */ +var MediaFileData = function(codecs, bitrate, height, width, duration){ + this.codecs = codecs || null; + this.bitrate = bitrate || 0; + this.height = height || 0; + this.width = width || 0; + this.duration = duration || 0; +}; + +module.exports = MediaFileData; +}); + +// file: lib/common/plugin/Metadata.js +define("cordova/plugin/Metadata", function(require, exports, module) { +/** + * Information about the state of the file or directory + * + * {Date} modificationTime (readonly) + */ +var Metadata = function(time) { + this.modificationTime = (typeof time != 'undefined'?new Date(time):null); +}; + +module.exports = Metadata; +}); + +// file: lib/common/plugin/Position.js +define("cordova/plugin/Position", function(require, exports, module) { +var Coordinates = require('cordova/plugin/Coordinates'); + +var Position = function(coords, timestamp) { + if (coords) { + this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy); + } else { + this.coords = new Coordinates(); + } + this.timestamp = (timestamp !== undefined) ? timestamp : new Date(); +}; + +module.exports = Position; + +}); + +// file: lib/common/plugin/PositionError.js +define("cordova/plugin/PositionError", function(require, exports, module) { +/** + * Position error object + * + * @constructor + * @param code + * @param message + */ +var PositionError = function(code, message) { + this.code = code || null; + this.message = message || ''; +}; + +PositionError.PERMISSION_DENIED = 1; +PositionError.POSITION_UNAVAILABLE = 2; +PositionError.TIMEOUT = 3; + +module.exports = PositionError; +}); + +// file: lib/common/plugin/ProgressEvent.js +define("cordova/plugin/ProgressEvent", function(require, exports, module) { +// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill +// Feature test: See if we can instantiate a native ProgressEvent; +// if so, use that approach, +// otherwise fill-in with our own implementation. +// +// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview. +var ProgressEvent = (function() { + /* + var createEvent = function(data) { + var event = document.createEvent('Events'); + event.initEvent('ProgressEvent', false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + if (data.target) { + // TODO: cannot call .dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retrieved a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/android/plugin/android/app.js +define("cordova/plugin/android/app", function(require, exports, module) { +var exec = require('cordova/exec'); + +module.exports = { + /** + * Clear the resource cache. + */ + clearCache:function() { + exec(null, null, "App", "clearCache", []); + }, + + /** + * Load the url into the webview or into new browser instance. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * clearHistory: boolean => clear webview history (default=false) + * openExternal: boolean => open in a new browser (default=false) + * + * Example: + * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ + loadUrl:function(url, props) { + exec(null, null, "App", "loadUrl", [url, props]); + }, + + /** + * Cancel loadUrl that is waiting to be loaded. + */ + cancelLoadUrl:function() { + exec(null, null, "App", "cancelLoadUrl", []); + }, + + /** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ + clearHistory:function() { + exec(null, null, "App", "clearHistory", []); + }, + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + backHistory:function() { + exec(null, null, "App", "backHistory", []); + }, + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ + overrideBackbutton:function(override) { + exec(null, null, "App", "overrideBackbutton", [override]); + }, + + /** + * Exit and terminate the application. + */ + exitApp:function() { + return exec(null, null, "App", "exitApp", []); + } +}; +}); + +// file: lib/android/plugin/android/callback.js +define("cordova/plugin/android/callback", function(require, exports, module) { +var port = null, + token = null, + xmlhttp; + +function startXhr() { + // cordova/exec depends on this module, so we can't require cordova/exec on the module level. + var exec = require('cordova/exec'), + xmlhttp = new XMLHttpRequest(); + + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if (!xmlhttp) { + return; + } + if (xmlhttp.readyState === 4){ + // If callback has JavaScript statement to execute + if (xmlhttp.status === 200) { + + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); + setTimeout(function() { + try { + var t = eval(msg); + } + catch (e) { + // If we're getting an error here, seeing the message will help in debugging + console.log("JSCallback: Message from Server: " + msg); + console.log("JSCallback Error: "+e); + } + }, 1); + setTimeout(startXhr, 1); + } + + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(startXhr, 10); + } + + // 0 == Page is unloading. + // 400 == Bad request. + // 403 == invalid token. + // 503 == server stopped. + else { + console.log("JSCallback Error: Request failed with status " + xmlhttp.status); + exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING); + } + } + }; + + if (port === null) { + port = prompt("getPort", "gap_callbackServer:"); + } + if (token === null) { + token = prompt("getToken", "gap_callbackServer:"); + } + xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true); + xmlhttp.send(); +} + +module.exports = { + start: function() { + startXhr(); + }, + + stop: function() { + if (xmlhttp) { + var tmp = xmlhttp; + xmlhttp = null; + tmp.abort(); + } + }, + + isAvailable: function() { + return ("true" != prompt("usePolling", "gap_callbackServer:")); + } +}; + + +}); + +// file: lib/android/plugin/android/device.js +define("cordova/plugin/android/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + app = require('cordova/plugin/android/app'); + +module.exports = { + /* + * DEPRECATED + * This is only for Android. + * + * You must explicitly override the back button. + */ + overrideBackButton:function() { + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This resets the back button to the default behaviour + */ + resetBackButton:function() { + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This terminates the activity! + */ + exitApp:function() { + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); + } +}; + +}); + +// file: lib/android/plugin/android/notification.js +define("cordova/plugin/android/notification", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides Android enhanced notification API. + */ +module.exports = { + activityStart : function(title, message) { + // If title and message not specified then mimic Android behavior of + // using default strings. + if (typeof title === "undefined" && typeof message == "undefined") { + title = "Busy"; + message = 'Please wait...'; + } + + exec(null, null, 'Notification', 'activityStart', [ title, message ]); + }, + + /** + * Close an activity dialog + */ + activityStop : function() { + exec(null, null, 'Notification', 'activityStop', []); + }, + + /** + * Display a progress dialog with progress bar that goes from 0 to 100. + * + * @param {String} + * title Title of the progress dialog. + * @param {String} + * message Message to display in the dialog. + */ + progressStart : function(title, message) { + exec(null, null, 'Notification', 'progressStart', [ title, message ]); + }, + + /** + * Close the progress dialog. + */ + progressStop : function() { + exec(null, null, 'Notification', 'progressStop', []); + }, + + /** + * Set the progress dialog value. + * + * @param {Number} + * value 0-100 + */ + progressValue : function(value) { + exec(null, null, 'Notification', 'progressValue', [ value ]); + } +}; +}); + +// file: lib/android/plugin/android/polling.js +define("cordova/plugin/android/polling", function(require, exports, module) { +var cordova = require('cordova'), + POLL_INTERVAL = 50, + enabled = false; + +function pollOnce() { + var msg = prompt("", "gap_poll:"); + if (msg) { + try { + eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + return true; + } + return false; +} + +function doPoll() { + if (!enabled) { + return; + } + var nextDelay = POLL_INTERVAL; + if (pollOnce()) { + nextDelay = 0; + } + setTimeout(doPoll, nextDelay); +} + +module.exports = { + start: function() { + enabled = true; + setTimeout(doPoll, 1); + }, + stop: function() { + enabled = false; + }, + pollOnce: pollOnce +}; + + +}); + +// file: lib/android/plugin/android/storage.js +define("cordova/plugin/android/storage", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + channel = require('cordova/channel'); + +var queryQueue = {}; + +/** + * SQL result set object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Rows = function() { + this.resultSet = []; // results array + this.length = 0; // number of rows +}; + +/** + * Get item from SQL result set + * + * @param row The row number to return + * @return The row object + */ +DroidDB_Rows.prototype.item = function(row) { + return this.resultSet[row]; +}; + +/** + * SQL result set that is returned to user. + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Result = function() { + this.rows = new DroidDB_Rows(); +}; + +/** + * Callback from native code when query is complete. + * PRIVATE METHOD + * + * @param id Query id + */ +function completeQuery(id, data) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + + // Save query results + var r = new DroidDB_Result(); + r.rows.resultSet = data; + r.rows.length = data.length; + try { + if (typeof query.successCallback === 'function') { + query.successCallback(query.tx, r); + } + } catch (ex) { + console.log("executeSql error calling user success callback: "+ex); + } + + tx.queryComplete(id); + } + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * Callback from native code when query fails + * PRIVATE METHOD + * + * @param reason Error message + * @param id Query id + */ +function failQuery(reason, id) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + tx.queryList = {}; + + try { + if (typeof query.errorCallback === 'function') { + query.errorCallback(query.tx, reason); + } + } catch (ex) { + console.log("executeSql error calling user error callback: "+ex); + } + + tx.queryFailed(id, reason); + } + + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * SQL query object + * PRIVATE METHOD + * + * @constructor + * @param tx The transaction object that this query belongs to + */ +var DroidDB_Query = function(tx) { + + // Set the id of the query + this.id = utils.createUUID(); + + // Add this query to the queue + queryQueue[this.id] = this; + + // Init result + this.resultSet = []; + + // Set transaction that this query belongs to + this.tx = tx; + + // Add this query to transaction list + this.tx.queryList[this.id] = this; + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + +}; + +/** + * Transaction object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Tx = function() { + + // Set the id of the transaction + this.id = utils.createUUID(); + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + + // Query list + this.queryList = {}; +}; + +/** + * Mark query in transaction as complete. + * If all queries are complete, call the user's transaction success callback. + * + * @param id Query id + */ +DroidDB_Tx.prototype.queryComplete = function(id) { + delete this.queryList[id]; + + // If no more outstanding queries, then fire transaction success + if (this.successCallback) { + var count = 0; + var i; + for (i in this.queryList) { + if (this.queryList.hasOwnProperty(i)) { + count++; + } + } + if (count === 0) { + try { + this.successCallback(); + } catch(e) { + console.log("Transaction error calling user success callback: " + e); + } + } + } +}; + +/** + * Mark query in transaction as failed. + * + * @param id Query id + * @param reason Error message + */ +DroidDB_Tx.prototype.queryFailed = function(id, reason) { + + // The sql queries in this transaction have already been run, since + // we really don't have a real transaction implemented in native code. + // However, the user callbacks for the remaining sql queries in transaction + // will not be called. + this.queryList = {}; + + if (this.errorCallback) { + try { + this.errorCallback(reason); + } catch(e) { + console.log("Transaction error calling user error callback: " + e); + } + } +}; + +/** + * Execute SQL statement + * + * @param sql SQL statement to execute + * @param params Statement parameters + * @param successCallback Success callback + * @param errorCallback Error callback + */ +DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { + + // Init params array + if (typeof params === 'undefined') { + params = []; + } + + // Create query and add to queue + var query = new DroidDB_Query(this); + queryQueue[query.id] = query; + + // Save callbacks + query.successCallback = successCallback; + query.errorCallback = errorCallback; + + // Call native code + exec(null, null, "Storage", "executeSql", [sql, params, query.id]); +}; + +var DatabaseShell = function() { +}; + +/** + * Start a transaction. + * Does not support rollback in event of failure. + * + * @param process {Function} The transaction function + * @param successCallback {Function} + * @param errorCallback {Function} + */ +DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) { + var tx = new DroidDB_Tx(); + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + try { + process(tx); + } catch (e) { + console.log("Transaction error: "+e); + if (tx.errorCallback) { + try { + tx.errorCallback(e); + } catch (ex) { + console.log("Transaction error calling user error callback: "+e); + } + } + } +}; + +/** + * Open database + * + * @param name Database name + * @param version Database version + * @param display_name Database display name + * @param size Database size in bytes + * @return Database object + */ +var DroidDB_openDatabase = function(name, version, display_name, size) { + exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]); + var db = new DatabaseShell(); + return db; +}; + +/** + * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api. + * TODO: Do similar for sessionStorage. + * @constructor + */ +var CupcakeLocalStorage = function() { + channel.waitForInitialization("cupcakeStorage"); + + try { + + this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440); + var storage = {}; + this.length = 0; + function setLength (length) { + this.length = length; + localStorage.length = length; + } + this.db.transaction( + function (transaction) { + var i; + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('SELECT * FROM storage', [], function(tx, result) { + for(var i = 0; i < result.rows.length; i++) { + storage[result.rows.item(i).id] = result.rows.item(i).body; + } + setLength(result.rows.length); + channel.initializationComplete("cupcakeStorage"); + }); + + }, + function (err) { + utils.alert(err.message); + } + ); + this.setItem = function(key, val) { + if (typeof(storage[key])=='undefined') { + this.length++; + } + storage[key] = val; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]); + } + ); + }; + this.getItem = function(key) { + return storage[key]; + }; + this.removeItem = function(key) { + delete storage[key]; + this.length--; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage where id=?', [key]); + } + ); + }; + this.clear = function() { + storage = {}; + this.length = 0; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage', []); + } + ); + }; + this.key = function(index) { + var i = 0; + for (var j in storage) { + if (i==index) { + return j; + } else { + i++; + } + } + return null; + }; + + } catch(e) { + utils.alert("Database error "+e+"."); + return; + } +}; + +module.exports = { + openDatabase:DroidDB_openDatabase, + CupcakeLocalStorage:CupcakeLocalStorage, + failQuery:failQuery, + completeQuery:completeQuery +}; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object whose properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/echo.js +define("cordova/plugin/echo", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback. + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + * @param message The string to be echoed. + * @param forceAsync Whether to force an async return value (for testing native->js bridge). + */ +module.exports = function(successCallback, errorCallback, message, forceAsync) { + var action = forceAsync ? 'echoAsync' : 'echo'; + exec(successCallback, errorCallback, "Echo", action, [message]); +}; + + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + +Cordova + + + + + + +

Hello World

+

+ + diff --git a/Android/ActionBarSherlockTabBar/Example project/assets/www/jquery-1.8.2.min.js b/Android/ActionBarSherlockTabBar/Example project/assets/www/jquery-1.8.2.min.js new file mode 100644 index 0000000..f65cf1d --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/assets/www/jquery-1.8.2.min.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.2 jquery.com | jquery.org/license */ +(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
t
",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
","
"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/Android/ActionBarSherlockTabBar/Example project/libs/android-support-v4.jar b/Android/ActionBarSherlockTabBar/Example project/libs/android-support-v4.jar new file mode 100644 index 0000000..feaf44f Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/libs/android-support-v4.jar differ diff --git a/Android/ActionBarSherlockTabBar/Example project/libs/cordova-2.1.0.jar b/Android/ActionBarSherlockTabBar/Example project/libs/cordova-2.1.0.jar new file mode 100644 index 0000000..d3eecaa Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/libs/cordova-2.1.0.jar differ diff --git a/Android/ActionBarSherlockTabBar/Example project/proguard-project.txt b/Android/ActionBarSherlockTabBar/Example project/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/Android/ActionBarSherlockTabBar/Example project/project.properties b/Android/ActionBarSherlockTabBar/Example project/project.properties new file mode 100644 index 0000000..f25d702 --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-16 +android.library.reference.1=../dependencies/android/ActionBarSherlock/library diff --git a/Android/ActionBarSherlockTabBar/Example project/res/drawable-hdpi/ic_action_search.png b/Android/ActionBarSherlockTabBar/Example project/res/drawable-hdpi/ic_action_search.png new file mode 100644 index 0000000..67de12d Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/res/drawable-hdpi/ic_action_search.png differ diff --git a/Android/ActionBarSherlockTabBar/Example project/res/drawable-hdpi/ic_launcher.png b/Android/ActionBarSherlockTabBar/Example project/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/res/drawable-hdpi/ic_launcher.png differ diff --git a/Android/ActionBarSherlockTabBar/Example project/res/drawable-mdpi/ic_action_search.png b/Android/ActionBarSherlockTabBar/Example project/res/drawable-mdpi/ic_action_search.png new file mode 100644 index 0000000..134d549 Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/res/drawable-mdpi/ic_action_search.png differ diff --git a/Android/ActionBarSherlockTabBar/Example project/res/drawable-mdpi/ic_launcher.png b/Android/ActionBarSherlockTabBar/Example project/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/res/drawable-mdpi/ic_launcher.png differ diff --git a/Android/ActionBarSherlockTabBar/Example project/res/drawable-xhdpi/ic_action_search.png b/Android/ActionBarSherlockTabBar/Example project/res/drawable-xhdpi/ic_action_search.png new file mode 100644 index 0000000..d699c6b Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/res/drawable-xhdpi/ic_action_search.png differ diff --git a/Android/ActionBarSherlockTabBar/Example project/res/drawable-xhdpi/ic_launcher.png b/Android/ActionBarSherlockTabBar/Example project/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/Android/ActionBarSherlockTabBar/Example project/res/drawable-xhdpi/ic_launcher.png differ diff --git a/Android/ActionBarSherlockTabBar/Example project/res/layout/activity_main.xml b/Android/ActionBarSherlockTabBar/Example project/res/layout/activity_main.xml new file mode 100644 index 0000000..8f4c7f8 --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/Android/ActionBarSherlockTabBar/Example project/res/menu/activity_main.xml b/Android/ActionBarSherlockTabBar/Example project/res/menu/activity_main.xml new file mode 100644 index 0000000..cfc10fd --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/res/menu/activity_main.xml @@ -0,0 +1,6 @@ + + + diff --git a/Android/ActionBarSherlockTabBar/Example project/res/values-v11/styles.xml b/Android/ActionBarSherlockTabBar/Example project/res/values-v11/styles.xml new file mode 100644 index 0000000..fac93e5 --- /dev/null +++ b/Android/ActionBarSherlockTabBar/Example project/res/values-v11/styles.xml @@ -0,0 +1,5 @@ + + + + + + + + + + + + + +
Loading ...
+ + + + + \ No newline at end of file diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png new file mode 100644 index 0000000..f820ca0 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip new file mode 100644 index 0000000..34db5dc Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png new file mode 100644 index 0000000..5d2dfcd Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/cordova-2.0.0.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/cordova-2.0.0.js new file mode 100644 index 0000000..ba9e6a9 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/cordova-2.0.0.js @@ -0,0 +1,5724 @@ +// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0 + +// File generated at :: Wed Jul 18 2012 14:44:33 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + */ + fireDocumentEvent: function(type, data) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + // TODO: this is Android only; think about how to do this better + shuttingDown:false, + UsePolling:false, + // END TODO + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'); + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.guid = 1; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j 0) { + var v = JSON.parse(r); + + // If status is OK, then return value back to caller + if (v.status === cordova.callbackStatus.OK) { + + // If there is a success callback, then call it now with + // returned value + if (success) { + try { + success(v.message); + } catch (e) { + console.log("Error in success callback: " + callbackId + " = " + e); + } + + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + return v.message; + } + + // If no result + else if (v.status === cordova.callbackStatus.NO_RESULT) { + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + + // If error, then display error + else { + console.log("Error: Status="+v.status+" Message="+v.message); + + // If there is a fail callback, then call it now with returned value + if (fail) { + try { + fail(v.message); + } + catch (e1) { + console.log("Error in error callback: "+callbackId+" = "+e1); + } + + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + return null; + } + } + } catch (e2) { + console.log("Error: "+e2); + } +}; + +}); + +// file: lib/android/platform.js +define("cordova/platform", function(require, exports, module) { +module.exports = { + id: "android", + initialize:function() { + var channel = require("cordova/channel"), + cordova = require('cordova'), + callback = require('cordova/plugin/android/callback'), + polling = require('cordova/plugin/android/polling'), + exec = require('cordova/exec'); + + channel.onDestroy.subscribe(function() { + cordova.shuttingDown = true; + }); + + // Start listening for XHR callbacks + // Figure out which bridge approach will work on this Android + // device: polling or XHR-based callbacks + setTimeout(function() { + if (cordova.UsePolling) { + polling(); + } + else { + var isPolling = prompt("usePolling", "gap_callbackServer:"); + cordova.UsePolling = isPolling; + if (isPolling == "true") { + cordova.UsePolling = true; + polling(); + } else { + cordova.UsePolling = false; + callback(); + } + } + }, 1); + + // Inject a listener for the backbutton on the document. + var backButtonChannel = cordova.addDocumentEventHandler('backbutton', { + onSubscribe:function() { + // If we just attached the first handler, let native know we need to override the back button. + if (this.numHandlers === 1) { + exec(null, null, "App", "overrideBackbutton", [true]); + } + }, + onUnsubscribe:function() { + // If we just detached the last handler, let native know we no longer override the back button. + if (this.numHandlers === 0) { + exec(null, null, "App", "overrideBackbutton", [false]); + } + } + }); + + // Add hardware MENU and SEARCH button handlers + cordova.addDocumentEventHandler('menubutton'); + cordova.addDocumentEventHandler('searchbutton'); + + // Figure out if we need to shim-in localStorage and WebSQL + // support from the native side. + var storage = require('cordova/plugin/android/storage'); + + // First patch WebSQL if necessary + if (typeof window.openDatabase == 'undefined') { + // Not defined, create an openDatabase function for all to use! + window.openDatabase = storage.openDatabase; + } else { + // Defined, but some Android devices will throw a SECURITY_ERR - + // so we wrap the whole thing in a try-catch and shim in our own + // if the device has Android bug 16175. + var originalOpenDatabase = window.openDatabase; + window.openDatabase = function(name, version, desc, size) { + var db = null; + try { + db = originalOpenDatabase(name, version, desc, size); + } + catch (ex) { + if (ex.code === 18) { + db = null; + } else { + throw ex; + } + } + + if (db === null) { + return storage.openDatabase(name, version, desc, size); + } + else { + return db; + } + + }; + } + + // Patch localStorage if necessary + if (typeof window.localStorage == 'undefined' || window.localStorage === null) { + window.localStorage = new storage.CupcakeLocalStorage(); + } + + // Let native code know we are all done on the JS side. + // Native code will then un-hide the WebView. + channel.join(function() { + exec(null, null, "App", "show", []); + }, [channel.onCordovaReady]); + }, + objects: { + cordova: { + children: { + JSCallback:{ + path:"cordova/plugin/android/callback" + }, + JSCallbackPolling:{ + path:"cordova/plugin/android/polling" + } + } + }, + navigator: { + children: { + app:{ + path: "cordova/plugin/android/app" + } + } + }, + File: { // exists natively on Android WebView, override + path: "cordova/plugin/File" + }, + FileReader: { // exists natively on Android WebView, override + path: "cordova/plugin/FileReader" + }, + FileError: { //exists natively on Android WebView on Android 4.x + path: "cordova/plugin/FileError" + }, + MediaError: { // exists natively on Android WebView on Android 4.x + path: "cordova/plugin/MediaError" + } + }, + merges: { + device: { + path: 'cordova/plugin/android/device' + }, + navigator: { + children: { + notification: { + path: 'cordova/plugin/android/notification' + } + } + } + } +}; + +}); + +// file: lib/common/plugin/Acceleration.js +define("cordova/plugin/Acceleration", function(require, exports, module) { +var Acceleration = function(x, y, z, timestamp) { + this.x = x; + this.y = y; + this.z = z; + this.timestamp = timestamp || (new Date()).getTime(); +}; + +module.exports = Acceleration; + +}); + +// file: lib/common/plugin/Camera.js +define("cordova/plugin/Camera", function(require, exports, module) { +var exec = require('cordova/exec'), + Camera = require('cordova/plugin/CameraConstants'); + +var cameraExport = {}; + +// Tack on the Camera Constants to the base camera plugin. +for (var key in Camera) { + cameraExport[key] = Camera[key]; +} + +/** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=FILE_URI. + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ +cameraExport.getPicture = function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback != "function") { + console.log("Camera Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Camera Error: errorCallback is not a function"); + return; + } + + var quality = 50; + if (options && typeof options.quality == "number") { + quality = options.quality; + } else if (options && typeof options.quality == "string") { + var qlity = parseInt(options.quality, 10); + if (isNaN(qlity) === false) { + quality = qlity.valueOf(); + } + } + + var destinationType = Camera.DestinationType.FILE_URI; + if (typeof options.destinationType == "number") { + destinationType = options.destinationType; + } + + var sourceType = Camera.PictureSourceType.CAMERA; + if (typeof options.sourceType == "number") { + sourceType = options.sourceType; + } + + var targetWidth = -1; + if (typeof options.targetWidth == "number") { + targetWidth = options.targetWidth; + } else if (typeof options.targetWidth == "string") { + var width = parseInt(options.targetWidth, 10); + if (isNaN(width) === false) { + targetWidth = width.valueOf(); + } + } + + var targetHeight = -1; + if (typeof options.targetHeight == "number") { + targetHeight = options.targetHeight; + } else if (typeof options.targetHeight == "string") { + var height = parseInt(options.targetHeight, 10); + if (isNaN(height) === false) { + targetHeight = height.valueOf(); + } + } + + var encodingType = Camera.EncodingType.JPEG; + if (typeof options.encodingType == "number") { + encodingType = options.encodingType; + } + + var mediaType = Camera.MediaType.PICTURE; + if (typeof options.mediaType == "number") { + mediaType = options.mediaType; + } + var allowEdit = false; + if (typeof options.allowEdit == "boolean") { + allowEdit = options.allowEdit; + } else if (typeof options.allowEdit == "number") { + allowEdit = options.allowEdit <= 0 ? false : true; + } + var correctOrientation = false; + if (typeof options.correctOrientation == "boolean") { + correctOrientation = options.correctOrientation; + } else if (typeof options.correctOrientation == "number") { + correctOrientation = options.correctOrientation <=0 ? false : true; + } + var saveToPhotoAlbum = false; + if (typeof options.saveToPhotoAlbum == "boolean") { + saveToPhotoAlbum = options.saveToPhotoAlbum; + } else if (typeof options.saveToPhotoAlbum == "number") { + saveToPhotoAlbum = options.saveToPhotoAlbum <=0 ? false : true; + } + var popoverOptions = null; + if (typeof options.popoverOptions == "object") { + popoverOptions = options.popoverOptions; + } + + var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]; + + exec(successCallback, errorCallback, "Camera", "takePicture", args); +}; + +cameraExport.cleanup = function(successCallback, errorCallback) { + exec(successCallback, errorCallback, "Camera", "cleanup", []); +}; + +module.exports = cameraExport; +}); + +// file: lib/common/plugin/CameraConstants.js +define("cordova/plugin/CameraConstants", function(require, exports, module) { +module.exports = { + DestinationType:{ + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android) + }, + EncodingType:{ + JPEG: 0, // Return JPEG encoded image + PNG: 1 // Return PNG encoded image + }, + MediaType:{ + PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType + VIDEO: 1, // allow selection of video only, ONLY RETURNS URL + ALLMEDIA : 2 // allow selection from all media types + }, + PictureSourceType:{ + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) + }, + PopoverArrowDirection:{ + ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover + ARROW_DOWN : 2, + ARROW_LEFT : 4, + ARROW_RIGHT : 8, + ARROW_ANY : 15 + } +}; +}); + +// file: lib/common/plugin/CameraPopoverOptions.js +define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) { +var Camera = require('cordova/plugin/CameraConstants'); + +/** + * Encapsulates options for iOS Popover image picker + */ +var CameraPopoverOptions = function(x,y,width,height,arrowDir){ + // information of rectangle that popover should be anchored to + this.x = x || 0; + this.y = y || 32; + this.width = width || 320; + this.height = height || 480; + // The direction of the popover arrow + this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY; +}; + +module.exports = CameraPopoverOptions; +}); + +// file: lib/common/plugin/CaptureAudioOptions.js +define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) { +/** + * Encapsulates all audio capture operation configuration options. + */ +var CaptureAudioOptions = function(){ + // Upper limit of sound clips user can record. Value must be equal or greater than 1. + this.limit = 1; + // Maximum duration of a single sound clip in seconds. + this.duration = 0; + // The selected audio mode. Must match with one of the elements in supportedAudioModes array. + this.mode = null; +}; + +module.exports = CaptureAudioOptions; +}); + +// file: lib/common/plugin/CaptureError.js +define("cordova/plugin/CaptureError", function(require, exports, module) { +/** + * The CaptureError interface encapsulates all errors in the Capture API. + */ +var CaptureError = function(c) { + this.code = c || null; +}; + +// Camera or microphone failed to capture image or sound. +CaptureError.CAPTURE_INTERNAL_ERR = 0; +// Camera application or audio capture application is currently serving other capture request. +CaptureError.CAPTURE_APPLICATION_BUSY = 1; +// Invalid use of the API (e.g. limit parameter has value less than one). +CaptureError.CAPTURE_INVALID_ARGUMENT = 2; +// User exited camera application or audio capture application before capturing anything. +CaptureError.CAPTURE_NO_MEDIA_FILES = 3; +// The requested capture operation is not supported. +CaptureError.CAPTURE_NOT_SUPPORTED = 20; + +module.exports = CaptureError; +}); + +// file: lib/common/plugin/CaptureImageOptions.js +define("cordova/plugin/CaptureImageOptions", function(require, exports, module) { +/** + * Encapsulates all image capture operation configuration options. + */ +var CaptureImageOptions = function(){ + // Upper limit of images user can take. Value must be equal or greater than 1. + this.limit = 1; + // The selected image mode. Must match with one of the elements in supportedImageModes array. + this.mode = null; +}; + +module.exports = CaptureImageOptions; +}); + +// file: lib/common/plugin/CaptureVideoOptions.js +define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) { +/** + * Encapsulates all video capture operation configuration options. + */ +var CaptureVideoOptions = function(){ + // Upper limit of videos user can record. Value must be equal or greater than 1. + this.limit = 1; + // Maximum duration of a single video clip in seconds. + this.duration = 0; + // The selected video mode. Must match with one of the elements in supportedVideoModes array. + this.mode = null; +}; + +module.exports = CaptureVideoOptions; +}); + +// file: lib/common/plugin/CompassError.js +define("cordova/plugin/CompassError", function(require, exports, module) { +/** + * CompassError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var CompassError = function(err) { + this.code = (err !== undefined ? err : null); +}; + +CompassError.COMPASS_INTERNAL_ERR = 0; +CompassError.COMPASS_NOT_SUPPORTED = 20; + +module.exports = CompassError; +}); + +// file: lib/common/plugin/CompassHeading.js +define("cordova/plugin/CompassHeading", function(require, exports, module) { +var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) { + this.magneticHeading = (magneticHeading !== undefined ? magneticHeading : null); + this.trueHeading = (trueHeading !== undefined ? trueHeading : null); + this.headingAccuracy = (headingAccuracy !== undefined ? headingAccuracy : null); + this.timestamp = (timestamp !== undefined ? timestamp : new Date().getTime()); +}; + +module.exports = CompassHeading; +}); + +// file: lib/common/plugin/ConfigurationData.js +define("cordova/plugin/ConfigurationData", function(require, exports, module) { +/** + * Encapsulates a set of parameters that the capture device supports. + */ +function ConfigurationData() { + // The ASCII-encoded string in lower case representing the media type. + this.type = null; + // The height attribute represents height of the image or video in pixels. + // In the case of a sound clip this attribute has value 0. + this.height = 0; + // The width attribute represents width of the image or video in pixels. + // In the case of a sound clip this attribute has value 0 + this.width = 0; +} + +module.exports = ConfigurationData; +}); + +// file: lib/common/plugin/Connection.js +define("cordova/plugin/Connection", function(require, exports, module) { +/** + * Network status + */ +module.exports = { + UNKNOWN: "unknown", + ETHERNET: "ethernet", + WIFI: "wifi", + CELL_2G: "2g", + CELL_3G: "3g", + CELL_4G: "4g", + NONE: "none" +}; +}); + +// file: lib/common/plugin/Contact.js +define("cordova/plugin/Contact", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'); + +/** +* Converts primitives into Complex Object +* Currently only used for Date fields +*/ +function convertIn(contact) { + var value = contact.birthday; + try { + contact.birthday = new Date(parseFloat(value)); + } catch (exception){ + console.log("Cordova Contact convertIn error: exception creating date."); + } + return contact; +} + +/** +* Converts Complex objects into primitives +* Only conversion at present is for Dates. +**/ + +function convertOut(contact) { + var value = contact.birthday; + if (value !== null) { + // try to make it a Date object if it is not already + if (!utils.isDate(value)){ + try { + value = new Date(value); + } catch(exception){ + value = null; + } + } + if (utils.isDate(value)){ + value = value.valueOf(); // convert to milliseconds + } + contact.birthday = value; + } + return contact; +} + +/** +* Contains information about a single contact. +* @constructor +* @param {DOMString} id unique identifier +* @param {DOMString} displayName +* @param {ContactName} name +* @param {DOMString} nickname +* @param {Array.} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; +}; + +module.exports = FileUploadOptions; +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger then file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + me.successCallback(); + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param status The status code (int) + * @param msg The status message (string) + */ +Media.onStatus = function(id, msg, value) { + var media = mediaObjects[id]; + // If state update + if (msg === Media.MEDIA_STATE) { + if (value === Media.MEDIA_STOPPED) { + if (media.successCallback) { + media.successCallback(); + } + } + if (media.statusCallback) { + media.statusCallback(value); + } + } + else if (msg === Media.MEDIA_DURATION) { + media._duration = value; + } + else if (msg === Media.MEDIA_ERROR) { + if (media.errorCallback) { + // value should be a MediaError object when msg == MEDIA_ERROR + media.errorCallback(value); + } + } + else if (msg === Media.MEDIA_POSITION) { + media._position = value; + } +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. + * @constructor + */ +var MediaError = function(code, msg) { + this.code = (code !== undefined ? code : null); + this.message = msg || ""; +}; + +MediaError.MEDIA_ERR_NONE_ACTIVE = 0; +MediaError.MEDIA_ERR_ABORTED = 1; +MediaError.MEDIA_ERR_NETWORK = 2; +MediaError.MEDIA_ERR_DECODE = 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; + +module.exports = MediaError; +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +// TODO: can we axe this? +/** + * Casts a PluginResult message property (array of objects) to an array of MediaFile objects + * (used in Objective-C and Android) + * + * @param {PluginResult} pluginResult + */ +MediaFile.cast = function(pluginResult) { + var mediaFiles = []; + for (var i=0; i.dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retreived a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/android/plugin/android/app.js +define("cordova/plugin/android/app", function(require, exports, module) { +var exec = require('cordova/exec'); + +module.exports = { + /** + * Clear the resource cache. + */ + clearCache:function() { + exec(null, null, "App", "clearCache", []); + }, + + /** + * Load the url into the webview or into new browser instance. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * clearHistory: boolean => clear webview history (default=false) + * openExternal: boolean => open in a new browser (default=false) + * + * Example: + * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ + loadUrl:function(url, props) { + exec(null, null, "App", "loadUrl", [url, props]); + }, + + /** + * Cancel loadUrl that is waiting to be loaded. + */ + cancelLoadUrl:function() { + exec(null, null, "App", "cancelLoadUrl", []); + }, + + /** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ + clearHistory:function() { + exec(null, null, "App", "clearHistory", []); + }, + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + backHistory:function() { + exec(null, null, "App", "backHistory", []); + }, + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ + overrideBackbutton:function(override) { + exec(null, null, "App", "overrideBackbutton", [override]); + }, + + /** + * Exit and terminate the application. + */ + exitApp:function() { + return exec(null, null, "App", "exitApp", []); + } +}; +}); + +// file: lib/android/plugin/android/callback.js +define("cordova/plugin/android/callback", function(require, exports, module) { +var port = null, + token = null, + cordova = require('cordova'), + polling = require('cordova/plugin/android/polling'), + callback = function() { + // Exit if shutting down app + if (cordova.shuttingDown) { + return; + } + + // If polling flag was changed, start using polling from now on + if (cordova.UsePolling) { + polling(); + return; + } + + var xmlhttp = new XMLHttpRequest(); + + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if(xmlhttp.readyState === 4){ + + // Exit if shutting down app + if (cordova.shuttingDown) { + return; + } + + // If callback has JavaScript statement to execute + if (xmlhttp.status === 200) { + + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); + setTimeout(function() { + try { + var t = eval(msg); + } + catch (e) { + // If we're getting an error here, seeing the message will help in debugging + console.log("JSCallback: Message from Server: " + msg); + console.log("JSCallback Error: "+e); + } + }, 1); + setTimeout(callback, 1); + } + + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(callback, 10); + } + + // If security error + else if (xmlhttp.status === 403) { + console.log("JSCallback Error: Invalid token. Stopping callbacks."); + } + + // If server is stopping + else if (xmlhttp.status === 503) { + console.log("JSCallback Server Closed: Stopping callbacks."); + } + + // If request wasn't GET + else if (xmlhttp.status === 400) { + console.log("JSCallback Error: Bad request. Stopping callbacks."); + } + + // If error, revert to polling + else { + console.log("JSCallback Error: Request failed."); + cordova.UsePolling = true; + polling(); + } + } + }; + + if (port === null) { + port = prompt("getPort", "gap_callbackServer:"); + } + if (token === null) { + token = prompt("getToken", "gap_callbackServer:"); + } + xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true); + xmlhttp.send(); +}; + +module.exports = callback; +}); + +// file: lib/android/plugin/android/device.js +define("cordova/plugin/android/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + app = require('cordova/plugin/android/app'); + +module.exports = { + /* + * DEPRECATED + * This is only for Android. + * + * You must explicitly override the back button. + */ + overrideBackButton:function() { + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This resets the back button to the default behaviour + */ + resetBackButton:function() { + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This terminates the activity! + */ + exitApp:function() { + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); + } +}; + +}); + +// file: lib/android/plugin/android/notification.js +define("cordova/plugin/android/notification", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides Android enhanced notification API. + */ +module.exports = { + activityStart : function(title, message) { + // If title and message not specified then mimic Android behavior of + // using default strings. + if (typeof title === "undefined" && typeof message == "undefined") { + title = "Busy"; + message = 'Please wait...'; + } + + exec(null, null, 'Notification', 'activityStart', [ title, message ]); + }, + + /** + * Close an activity dialog + */ + activityStop : function() { + exec(null, null, 'Notification', 'activityStop', []); + }, + + /** + * Display a progress dialog with progress bar that goes from 0 to 100. + * + * @param {String} + * title Title of the progress dialog. + * @param {String} + * message Message to display in the dialog. + */ + progressStart : function(title, message) { + exec(null, null, 'Notification', 'progressStart', [ title, message ]); + }, + + /** + * Close the progress dialog. + */ + progressStop : function() { + exec(null, null, 'Notification', 'progressStop', []); + }, + + /** + * Set the progress dialog value. + * + * @param {Number} + * value 0-100 + */ + progressValue : function(value) { + exec(null, null, 'Notification', 'progressValue', [ value ]); + } +}; +}); + +// file: lib/android/plugin/android/polling.js +define("cordova/plugin/android/polling", function(require, exports, module) { +var cordova = require('cordova'), + period = 50, + polling = function() { + // Exit if shutting down app + if (cordova.shuttingDown) { + return; + } + + // If polling flag was changed, stop using polling from now on and switch to XHR server / callback + if (!cordova.UsePolling) { + require('cordova/plugin/android/callback')(); + return; + } + + var msg = prompt("", "gap_poll:"); + if (msg) { + setTimeout(function() { + try { + var t = eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + }, 1); + setTimeout(polling, 1); + } + else { + setTimeout(polling, period); + } +}; + +module.exports = polling; +}); + +// file: lib/android/plugin/android/storage.js +define("cordova/plugin/android/storage", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + channel = require('cordova/channel'); + +var queryQueue = {}; + +/** + * SQL result set object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Rows = function() { + this.resultSet = []; // results array + this.length = 0; // number of rows +}; + +/** + * Get item from SQL result set + * + * @param row The row number to return + * @return The row object + */ +DroidDB_Rows.prototype.item = function(row) { + return this.resultSet[row]; +}; + +/** + * SQL result set that is returned to user. + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Result = function() { + this.rows = new DroidDB_Rows(); +}; + +/** + * Callback from native code when query is complete. + * PRIVATE METHOD + * + * @param id Query id + */ +function completeQuery(id, data) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + + // Save query results + var r = new DroidDB_Result(); + r.rows.resultSet = data; + r.rows.length = data.length; + try { + if (typeof query.successCallback === 'function') { + query.successCallback(query.tx, r); + } + } catch (ex) { + console.log("executeSql error calling user success callback: "+ex); + } + + tx.queryComplete(id); + } + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * Callback from native code when query fails + * PRIVATE METHOD + * + * @param reason Error message + * @param id Query id + */ +function failQuery(reason, id) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + tx.queryList = {}; + + try { + if (typeof query.errorCallback === 'function') { + query.errorCallback(query.tx, reason); + } + } catch (ex) { + console.log("executeSql error calling user error callback: "+ex); + } + + tx.queryFailed(id, reason); + } + + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * SQL query object + * PRIVATE METHOD + * + * @constructor + * @param tx The transaction object that this query belongs to + */ +var DroidDB_Query = function(tx) { + + // Set the id of the query + this.id = utils.createUUID(); + + // Add this query to the queue + queryQueue[this.id] = this; + + // Init result + this.resultSet = []; + + // Set transaction that this query belongs to + this.tx = tx; + + // Add this query to transaction list + this.tx.queryList[this.id] = this; + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + +}; + +/** + * Transaction object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Tx = function() { + + // Set the id of the transaction + this.id = utils.createUUID(); + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + + // Query list + this.queryList = {}; +}; + +/** + * Mark query in transaction as complete. + * If all queries are complete, call the user's transaction success callback. + * + * @param id Query id + */ +DroidDB_Tx.prototype.queryComplete = function(id) { + delete this.queryList[id]; + + // If no more outstanding queries, then fire transaction success + if (this.successCallback) { + var count = 0; + var i; + for (i in this.queryList) { + if (this.queryList.hasOwnProperty(i)) { + count++; + } + } + if (count === 0) { + try { + this.successCallback(); + } catch(e) { + console.log("Transaction error calling user success callback: " + e); + } + } + } +}; + +/** + * Mark query in transaction as failed. + * + * @param id Query id + * @param reason Error message + */ +DroidDB_Tx.prototype.queryFailed = function(id, reason) { + + // The sql queries in this transaction have already been run, since + // we really don't have a real transaction implemented in native code. + // However, the user callbacks for the remaining sql queries in transaction + // will not be called. + this.queryList = {}; + + if (this.errorCallback) { + try { + this.errorCallback(reason); + } catch(e) { + console.log("Transaction error calling user error callback: " + e); + } + } +}; + +/** + * Execute SQL statement + * + * @param sql SQL statement to execute + * @param params Statement parameters + * @param successCallback Success callback + * @param errorCallback Error callback + */ +DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { + + // Init params array + if (typeof params === 'undefined') { + params = []; + } + + // Create query and add to queue + var query = new DroidDB_Query(this); + queryQueue[query.id] = query; + + // Save callbacks + query.successCallback = successCallback; + query.errorCallback = errorCallback; + + // Call native code + exec(null, null, "Storage", "executeSql", [sql, params, query.id]); +}; + +var DatabaseShell = function() { +}; + +/** + * Start a transaction. + * Does not support rollback in event of failure. + * + * @param process {Function} The transaction function + * @param successCallback {Function} + * @param errorCallback {Function} + */ +DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) { + var tx = new DroidDB_Tx(); + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + try { + process(tx); + } catch (e) { + console.log("Transaction error: "+e); + if (tx.errorCallback) { + try { + tx.errorCallback(e); + } catch (ex) { + console.log("Transaction error calling user error callback: "+e); + } + } + } +}; + +/** + * Open database + * + * @param name Database name + * @param version Database version + * @param display_name Database display name + * @param size Database size in bytes + * @return Database object + */ +var DroidDB_openDatabase = function(name, version, display_name, size) { + exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]); + var db = new DatabaseShell(); + return db; +}; + +/** + * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api. + * TODO: Do similar for sessionStorage. + * @constructor + */ +var CupcakeLocalStorage = function() { + channel.waitForInitialization("cupcakeStorage"); + + try { + + this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440); + var storage = {}; + this.length = 0; + function setLength (length) { + this.length = length; + localStorage.length = length; + } + this.db.transaction( + function (transaction) { + var i; + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('SELECT * FROM storage', [], function(tx, result) { + for(var i = 0; i < result.rows.length; i++) { + storage[result.rows.item(i).id] = result.rows.item(i).body; + } + setLength(result.rows.length); + channel.initializationComplete("cupcakeStorage"); + }); + + }, + function (err) { + utils.alert(err.message); + } + ); + this.setItem = function(key, val) { + if (typeof(storage[key])=='undefined') { + this.length++; + } + storage[key] = val; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]); + } + ); + }; + this.getItem = function(key) { + return storage[key]; + }; + this.removeItem = function(key) { + delete storage[key]; + this.length--; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage where id=?', [key]); + } + ); + }; + this.clear = function() { + storage = {}; + this.length = 0; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage', []); + } + ); + }; + this.key = function(index) { + var i = 0; + for (var j in storage) { + if (i==index) { + return j; + } else { + i++; + } + } + return null; + }; + + } catch(e) { + utils.alert("Database error "+e+"."); + return; + } +}; + +module.exports = { + openDatabase:DroidDB_openDatabase, + CupcakeLocalStorage:CupcakeLocalStorage, + failQuery:failQuery, + completeQuery:completeQuery +}; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object who's properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + PhoneGap and Wikitude + + + + + + + + Hello Cordova + + +
+

Apache Cordovaâ„¢

+
+ + +
+
+ + + + + + diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/WikitudePlugin.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/WikitudePlugin.js new file mode 100644 index 0000000..bd09eb2 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/WikitudePlugin.js @@ -0,0 +1,383 @@ +var WikitudePlugin = { + + /** + * + * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk + * If you're having a trial version, leave this string empty + * + */ + mySDKKey : "ENTER-YOUR-KEY-HERE", + + /** true if architectview is open */ + isOpened : false, + + /** + * + * called when user pressed back-button on Android device + * + */ + backButtonCallback : null, + + /** + * + * Change the value of this variable to modify the location update rate + * + */ + locationUpdateRate : 3000, + + /** + * + * This variable represents if the current device is capable of running the Wikitude SDK + * + */ + isDeviceSupported : false, + + /** + * + * This watchID is used to shedule location updates + * + */ + watchID : null, + + /** + * + * Callbacks to get device information if ARchitect Worlds can be launched + * + */ + onDeviceSupportedCallback : null, + onDeviceNotSupportedCallback : null, + + /** + * + * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch + * + */ + onARchitectWorldLaunchedCallback : null, + onARchitectWorldFailedLaunchingCallback : null, + + /** + * + * This function gets called if PhoneGap reports that it has finished loading successfully. + * + */ + isDeviceSupported: function(successCallback, errorCallback) + { + + WikitudePlugin.onDeviceSupportedCallback = successCallback; + WikitudePlugin.onDeviceNotSupportedCallback = errorCallback; + + + // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin + cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]); + + }, + + /** + * + * Declare what should happen when user pressed back-button while architect-view is opened. + * If not declared/called or null: ARchitect-View is closed + * + */ + onPressedBackButton : function(callback) + { + WikitudePlugin.backButtonCallback = callback; + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK + * + */ + deviceIsARchitectReady : function() + { + // We keep track of the device status + WikitudePlugin.isDeviceSupported = true; + + + if(WikitudePlugin.onDeviceSupportedCallback) + { + WikitudePlugin.onDeviceSupportedCallback(); + } + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK. + * + */ + deviceIsNotARchitectReady : function() + { + WikitudePlugin.isDeviceSupported = false; + + // In this case we notify the user that his device is not supported by the Wikitude SDK + if(WikitudePlugin.onDeviceNotSupportedCallback) + { + WikitudePlugin.onDeviceNotSupportedCallback(); + } + }, + + /* + * ============================================================================================================================= + * + * PUBLIC API + * + * ============================================================================================================================= + */ + + /* Managing ARchitect world loading */ + + /** + * + * Call this function if you want to load an ARchitect World + * + * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox + * + */ + loadARchitectWorld : function(worldPath) + { + + // before we actually call load, we check again if the device is able to open the world + if(WikitudePlugin.isDeviceSupported) + { + + // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys: + // @param {Object} options (required) + // @param {String} options.sdkKey License key for the Wikitude SDK + // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox + + cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]); + + + // We add an event listener on the resume and pause event of the application lifecycle + document.addEventListener("resume", WikitudePlugin.onResume, false); + document.addEventListener("pause", WikitudePlugin.onPause, false); + + WikitudePlugin.isOpened = true; + document.addEventListener("backbutton", WikitudePlugin.onBackButton, false); + + // After we started loading the world, we start location updates + WikitudePlugin.startLocationUpdates(); + + }else + { + // if the device is not able to start the Wikitude SDK, we notify the user again + WikitudePlugin.deviceNotARchitectReady(); + } + }, + + /* Managing the Wikitude SDK Lifecycle */ + + /** + * + * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen + * + */ + close : function() + { + document.removeEventListener("pause", WikitudePlugin.onPause, false); + document.removeEventListener("resume", WikitudePlugin.onResume, false); + + WikitudePlugin.stopLocationUpdates(); + + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]); + WikitudePlugin.isOpened = false; + }, + + /** + * + * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active + * + */ + hide : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]); + }, + + /** + * + * Use this function to show the Wikitude SDK if it was hidden before + * + */ + show : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]); + }, + + /* Interacting with the Wikitude SDK */ + + /** + * + * Use this function to call javascript which will be executed in the context of your ARchitect World + * + * + * @param js The JavaScript that gets evaluated in context of the ARchitect World + * + */ + callJavaScript : function(js) + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]); + }, + + /** + * + * Use this function to set a callback which will be invoked when the ARchitect World calls for example + * document.location = "architectsdk://opendetailpage?id=9"; + * + * + * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://" + */ + setOnUrlInvokeCallback : function(onUrlInvokeCallback) + { + cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]); + }, + + + /* + * ============================================================================================================================= + * + * Callbacks of public functions + * + * ============================================================================================================================= + */ + + /** + * + * Use this callback to get notified if the world loaded successfully + * + */ + worldLaunched : function() + { + if(WikitudePlugin.onARchitectWorldLaunchedCallback) + { + WikitudePlugin.onARchitectWorldLaunchedCallback(); + } + }, + + /** + * + * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World + * + */ + worldFailedLaunching : function(err) + { + if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback) + { + WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err); + } + }, + + /* Lifecycle updates */ + + /** + * + * This function actually starts the PhoneGap location updates + * + */ + startLocationUpdates : function() + { + + WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate }); + }, + + /** + * + * This callback gets called everytime the location did update + * + */ + onReceivedLocation : function(position) + { + + // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]); + }, + + /** + * + * Use this function to stop location updates + * + */ + stopLocationUpdates : function() + { + + // We clear the location update watch which was responsible for updating the location in a specific time interval + navigator.geolocation.clearWatch(WikitudePlugin.watchID); + WikitudePlugin.watchID = null; + }, + + /** + * + * This function gets called every time the application did become active. + * + */ + onResume : function() + { + + // Call the Wikitude SDK that the application did become active again + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]); + + // And start continuing updating the user location + WikitudePlugin.startLocationUpdates(); + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onPause : function() + { + + // Call the Wikitude SDK that the application did resign active + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]); + + // And stop all ongoing location updates + WikitudePlugin.stopLocationUpdates(); + }, + + /** + * + * Android specific! + * This function gets called if the user presses the back button. + * You may define your own implementation by using WikitudePlugin.onPressedBackButton(method) + * + */ + onBackButton : function() + { + if (WikitudePlugin.isOpened) { + if (WikitudePlugin.backButtonCallback==null) { + WikitudePlugin.close(); + document.removeEventListener("backbutton", WikitudePlugin.onBackButton, false); + } else { + WikitudePlugin.backButtonCallback(); + } + } + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeOK : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeError : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + report: function(id) + { + console.log("app report: " + id); + } +}; diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/index.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/index.js new file mode 100644 index 0000000..b82a058 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/index.js @@ -0,0 +1,37 @@ +var app = { + initialize: function() { + this.bind(); + }, + bind: function() { + document.addEventListener('deviceready', this.deviceready, false); + }, + + // A callback which gets called if the device is able to launch ARchitect Worlds + onDeviceSupportedCallback : function() + { + // The device is able to launch ARchitect World, so lets do so :) + WikitudePlugin.loadARchitectWorld("assets/world/SimpleImageRecognition/SimpleIRWorld.html"); + }, + + // A callback which gets called if the device is not able to start ARchitect Worlds + onDeviceNotSupportedCallback : function() + { + app.report("Unable to launch ARchitect Worlds on this device"); + }, + + deviceready: function() { + // note that this is an event handler so the scope is that of the event + // so we need to call app.report(), and not this.report() + app.report('deviceready'); + + // check if the current device is able to launch ARchitect Worlds + WikitudePlugin.isDeviceSupported(app.onDeviceSupportedCallback, app.onDeviceNotSupportedCallback); + }, + report: function(id) { + console.log("report:" + id); + // hide the .pending

and show the .complete

+ document.querySelector('#' + id + ' .pending').className += ' hide'; + var completeElem = document.querySelector('#' + id + ' .complete'); + completeElem.className = completeElem.className.split('hide').join(''); + } +}; diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_128.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_128.png new file mode 100644 index 0000000..3516df3 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_128.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_16.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_16.png new file mode 100644 index 0000000..54e19c5 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_16.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_24.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_24.png new file mode 100644 index 0000000..c7d43ad Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_24.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_256.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_256.png new file mode 100644 index 0000000..e1cd0e6 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_256.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_32.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_32.png new file mode 100644 index 0000000..734fffc Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_32.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_48.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_48.png new file mode 100644 index 0000000..8ad8bac Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_48.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_512.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_512.png new file mode 100644 index 0000000..c9465f3 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_512.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_64.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_64.png new file mode 100644 index 0000000..03b3849 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_64.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_36.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_36.png new file mode 100644 index 0000000..cd5032a Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_36.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_48.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_48.png new file mode 100644 index 0000000..e79c606 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_48.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_72.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_72.png new file mode 100644 index 0000000..4d27634 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_72.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_96.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_96.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_96.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_bb_80.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_bb_80.png new file mode 100644 index 0000000..f86a27a Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_bb_80.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_114.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_114.png new file mode 100644 index 0000000..efd9c37 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_114.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_144.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_144.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_144.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_57.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_57.png new file mode 100644 index 0000000..c795fc4 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_57.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_72.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_72.png new file mode 100644 index 0000000..b1cfde7 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_72.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_landscape.png new file mode 100644 index 0000000..a61e2b1 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_portrait.png new file mode 100644 index 0000000..5d6a28a Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_landscape.png new file mode 100644 index 0000000..f3934cd Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_portrait.png new file mode 100644 index 0000000..65ad163 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_landscape.png new file mode 100644 index 0000000..a1b697c Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_portrait.png new file mode 100644 index 0000000..ea15693 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_landscape.png new file mode 100644 index 0000000..79f2f09 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_portrait.png new file mode 100644 index 0000000..c2e8042 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_300.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_300.png new file mode 100644 index 0000000..b548bdc Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_300.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_400.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_400.png new file mode 100644 index 0000000..3facdf9 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_400.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_landscape.png new file mode 100644 index 0000000..04be5ac Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_portrait.png new file mode 100644 index 0000000..41e839d Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_landscape.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_portrait.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_landscape.png new file mode 100644 index 0000000..d154883 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_portrait.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_landscape.png new file mode 100644 index 0000000..0165669 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_landscape.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_portrait.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_portrait.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/windows_phone_portrait.jpg b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/windows_phone_portrait.jpg new file mode 100644 index 0000000..9f95387 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/windows_phone_portrait.jpg differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec.html b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec.html new file mode 100644 index 0000000..83d7d2e --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec.html @@ -0,0 +1,50 @@ + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + +

+ + diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec/helper.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec/helper.js new file mode 100644 index 0000000..9f99445 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec/helper.js @@ -0,0 +1,11 @@ +afterEach(function() { + document.getElementById('stage').innerHTML = ''; +}); + +var helper = { + trigger: function(obj, name) { + var e = document.createEvent('Event'); + e.initEvent(name, true, true); + obj.dispatchEvent(e); + } +}; diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec/index.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec/index.js new file mode 100644 index 0000000..121cf63 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec/index.js @@ -0,0 +1,49 @@ +describe('app', function() { + describe('initialize', function() { + it('should bind deviceready', function() { + runs(function() { + spyOn(app, 'deviceready'); + app.initialize(); + helper.trigger(window.document, 'deviceready'); + }); + + waitsFor(function() { + return (app.deviceready.calls.length > 0); + }, 'deviceready should be called once', 500); + + runs(function() { + expect(app.deviceready).toHaveBeenCalled(); + }); + }); + }); + + describe('deviceready', function() { + it('should report that it fired', function() { + spyOn(app, 'report'); + app.deviceready(); + expect(app.report).toHaveBeenCalledWith('deviceready'); + }); + }); + + describe('report', function() { + beforeEach(function() { + var el = document.getElementById('stage'); + el.innerHTML = ['
', + '

Pending

', + '

Complete

', + '
'].join('\n'); + }); + + it('should show the completion state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .complete:not(.hide)'); + expect(el).toBeTruthy(); + }); + + it('should hide the pending state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .pending.hide'); + expect(el).toBeTruthy(); + }); + }); +}); diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/BOOM b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/BOOM new file mode 100644 index 0000000..37c623c --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/BOOM @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) + +bash $CORDOVA_PATH/cordova BOOM diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/appinfo.jar b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/appinfo.jar new file mode 100644 index 0000000..37e00df Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/appinfo.jar differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/clean b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/clean new file mode 100644 index 0000000..daa8442 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/clean @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) + +bash $CORDOVA_PATH/cordova clean diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/cordova b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/cordova new file mode 100644 index 0000000..0bca03f --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/cordova @@ -0,0 +1,85 @@ +#!/bin/bash + +set -e + +PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd ) + +function check_devices { + local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}'` + if [ -z "$devices" ] ; then + echo "1" + else + echo "0" + fi +} + +function emulate { + declare -a avd_list=($(android list avd | grep "Name:" | cut -f 2 -d ":" | xargs)) + # we need to start adb-server + adb start-server 1>/dev/null + + # Do not launch an emulator if there is already one running or if a device is attached + if [ $(check_devices) == 0 ] ; then + echo "Device attached or emulator already running" + return + fi + + local avd_id="1000" #FIXME: hopefully user does not have 1000 AVDs + # User has no AVDs + if [ ${#avd_list[@]} == 0 ] + then + echo "You don't have any Android Virtual Devices. Please create at least one AVD." + echo "android" + fi + # User has only one AVD + if [ ${#avd_list[@]} == 1 ] + then + emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[0]} 1> /dev/null 2>&1 & + # User has more than 1 AVD + elif [ ${#avd_list[@]} -gt 1 ] + then + while [ -z ${avd_list[$avd_id]} ] + do + echo "Choose from one of the following Android Virtual Devices [0 to $((${#avd_list[@]}-1))]:" + for(( i = 0 ; i < ${#avd_list[@]} ; i++ )) + do + echo "$i) ${avd_list[$i]}" + done + echo -n "> " + read avd_id + done + emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 & + fi + +} + +function clean { + ant clean +} +# has to be used independently and not in conjuction with other commands +function log { + adb logcat +} + +function debug { + if [ $(check_devices) == 0 ] ; then + ant debug install + else + ant debug + echo "##################################################################" + echo "# Plug in your device or launch an emulator with cordova/emulate #" + echo "##################################################################" + fi +} + +function launch { + local launch_str=$(java -jar $PROJECT_PATH/cordova/appinfo.jar $PROJECT_PATH/AndroidManifest.xml) + adb shell am start -n $launch_str +} + +function BOOM { + clean && debug && launch +} + +# TODO parse arguments +(cd $PROJECT_PATH && $1) diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug new file mode 100644 index 0000000..5d63a39 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) + +bash $CORDOVA_PATH/cordova debug diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate new file mode 100644 index 0000000..6c4fab2 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) + +bash $CORDOVA_PATH/cordova emulate diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log new file mode 100644 index 0000000..ab3622e --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd ) + +bash $PROJECT_PATH/cordova/cordova log diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/libs/cordova-2.0.0.jar b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/libs/cordova-2.0.0.jar new file mode 100644 index 0000000..e01123b Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/libs/cordova-2.0.0.jar differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/proguard-project.txt b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/project.properties b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/project.properties new file mode 100644 index 0000000..0840b4a --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-15 diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-hdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-hdpi/ic_launcher.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-ldpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 0000000..9923872 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-ldpi/ic_launcher.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-mdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-mdpi/ic_launcher.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-xhdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-xhdpi/ic_launcher.png differ diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/layout/main.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/layout/main.xml new file mode 100644 index 0000000..991ac56 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/layout/main.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/values/strings.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/values/strings.xml new file mode 100644 index 0000000..3c5860a --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + Hello World, PhonegapSampleActivity! + Hello IR + + \ No newline at end of file diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/xml/config.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/xml/config.xml new file mode 100644 index 0000000..4682e93 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/xml/config.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/PhonegapSampleActivity.java b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/PhonegapSampleActivity.java new file mode 100644 index 0000000..435e9e4 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/PhonegapSampleActivity.java @@ -0,0 +1,17 @@ +package com.wikitude.phonegap; + +import org.apache.cordova.DroidGap; + +import android.os.Bundle; + + + +public class PhonegapSampleActivity extends DroidGap { + /** Called when the activity is first created. */ + @Override + public void onCreate( Bundle savedInstanceState ) { + super.onCreate( savedInstanceState ); + super.loadUrl( "file:///android_asset/www/index.html" ); + // super.loadUrl( "file:///android_asset/www/demo.html" ); + } +} \ No newline at end of file diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePlugin.java b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePlugin.java new file mode 100644 index 0000000..f88af1d --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePlugin.java @@ -0,0 +1,417 @@ +package com.wikitude.phonegap; + +import java.io.IOException; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.ViewManager; + +import com.phonegap.api.Plugin; +import com.phonegap.api.PluginResult; +import com.wikitude.architect.ArchitectUrlListener; +import com.wikitude.architect.ArchitectView; +import com.wikitude.architect.ArchitectView.ArchitectConfig; + + + +/** + * Basic PhoneGap Wikitude ARchitect Plugin + * + * @author Wikitude GmbH + */ +public class WikitudePlugin extends Plugin implements ArchitectUrlListener { + + /** PhoneGap-root to Android-App-assets folder */ + private static final String LOCAL_ASSETS_PATH_ROOT = "assets/"; + + /* various JSON-Object keys*/ + private static final String JSON_KEY_APIKEY = "sdkKey"; + private static final String JSON_KEY_FILE_PATH = "filePath"; + + private static final String JSON_KEY_LOCATION_ALTITUDE = "alt"; + private static final String JSON_KEY_LOCATION_ACCURACY = "acc"; + private static final String JSON_KEY_LOCATION_LATITUDE = "lat"; + private static final String JSON_KEY_LOCATION_LONGITUDE = "lon"; + + /* static action strings */ + + /** + * opens architect-view (add to view stack) + */ + private static final String ACTION_OPEN = "open"; + + /** + * closes architect-view (remove view stack) + */ + private static final String ACTION_CLOSE = "close"; + + /** + * set visibility of architectView to visible (of present) + */ + private static final String ACTION_SHOW = "show"; + + /** + * set visibility of architectView to invisible (of present) + */ + private static final String ACTION_HIDE = "hide"; + + /** + * inject location information + */ + private static final String ACTION_SET_LOCATION = "setLocation"; + + /** + * callback for uri-invocations + */ + private static final String ACTION_ON_URLINVOKE = "onUrlInvoke"; + + /** + * life-cycle notification for resume + */ + private static final String ACTION_ON_RESUME = "onResume"; + + /** + * life-cycle notification for pause + */ + private static final String ACTION_ON_PAUSE = "onPause"; + + /** + * check if view is on view-stack (no matter if visible or not) + */ + private static final String ACTION_STATE_ISOPEN = "isOpen"; + + /** + * opens architect-view (add to view stack) + */ + private static final String ACTION_IS_DEVICE_SUPPORTED = "isDeviceSupported"; + + /** + * check if view is on view-stack (no matter if visible or not) + */ + private static final String ACTION_CALL_JAVASCRIPT = "callJavascript"; + + /** + * the Wikitude ARchitectview + */ + private ArchitectView architectView; + + /** + * callback-Id of url-invocation method + */ + private String urlInvokeCallbackId = null; + + /** + * callback-id of "open"-action method + */ + private String openCallbackId = null; + + @Override + public PluginResult execute( final String action, final JSONArray args, final String callbackId ) { + + /* hide architect-view -> destroy and remove from activity */ + if ( WikitudePlugin.ACTION_CLOSE.equals( action ) ) { + if ( this.architectView != null ) { + this.cordova.getActivity().runOnUiThread( new Runnable() { + + @Override + public void run() { + removeArchitectView(); + } + } ); + return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" ); + } + else { + return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" ); + } + } + + /* return success only if view is opened (no matter if visible or not) */ + if ( WikitudePlugin.ACTION_STATE_ISOPEN.equals( action ) ) { + if ( this.architectView != null ) { + return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" ); + } else { + return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" ); + } + } + + /* return success only if view is opened (no matter if visible or not) */ + if ( WikitudePlugin.ACTION_IS_DEVICE_SUPPORTED.equals( action ) ) { + if ( ArchitectView.isDeviceSupported( this.cordova.getActivity() ) ) { + return new PluginResult( PluginResult.Status.OK, action + ": this device is ARchitect-ready" ); + } else { + return new PluginResult( PluginResult.Status.ERROR, action + ": Sorry, this device is NOT ARchitect-ready" ); + } + } + + + + /* life-cycle's RESUME */ + if ( WikitudePlugin.ACTION_ON_RESUME.equals( action ) ) { + + if ( this.architectView != null ) { + this.cordova.getActivity().runOnUiThread( new Runnable() { + + @Override + public void run() { + WikitudePlugin.this.architectView.onResume(); + } + } ); + + return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" ); + } + return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" ); + } + + /* life-cycle's PAUSE */ + if ( WikitudePlugin.ACTION_ON_PAUSE.equals( action ) ) { + if ( architectView != null ) { + this.cordova.getActivity().runOnUiThread( new Runnable() { + + @Override + public void run() { + WikitudePlugin.this.architectView.onPause(); + } + } ); + + return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" ); + } + return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" ); + } + + /* set visibility to "visible", return error if view is null */ + if ( WikitudePlugin.ACTION_SHOW.equals( action ) ) { + + if ( this.architectView != null ) { + this.architectView.setVisibility( View.VISIBLE ); + return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" ); + } else { + return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" ); + } + } + + /* set visibility to "invisible", return error if view is null */ + if ( WikitudePlugin.ACTION_HIDE.equals( action ) ) { + + if ( this.architectView != null ) { + this.architectView.setVisibility( View.INVISIBLE ); + return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" ); + } else { + return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" ); + } + } + + /* define call-back for url-invocations */ + if ( WikitudePlugin.ACTION_ON_URLINVOKE.equals( action ) ) { + this.urlInvokeCallbackId = callbackId; + PluginResult result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": registered callback" ); + result.setKeepCallback( true ); + return result; + } + + /* location update */ + if ( WikitudePlugin.ACTION_SET_LOCATION.equals( action ) ) { + if ( this.architectView != null ) { + try { + String arrStr = args.getString( 0 ); + JSONObject arr = new JSONObject( arrStr ); + final double lat = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LATITUDE ); + final double lon = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LONGITUDE ); + Object altObj = arr.get( WikitudePlugin.JSON_KEY_LOCATION_ALTITUDE ); + float alt = Float.MIN_VALUE; + + if ( altObj != null && altObj instanceof Double ) { + alt = ((Double)altObj).floatValue(); + } + + final float altitude = alt; + + final Double acc = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_ACCURACY ); + if ( this.cordova != null && this.cordova.getActivity() != null ) { + this.cordova.getActivity().runOnUiThread( new Runnable() { + + @Override + public void run() { + if ( acc != null ) { + WikitudePlugin.this.architectView.setLocation( lat, lon, altitude, acc.floatValue() ); + } else { + WikitudePlugin.this.architectView.setLocation( lat, lon, altitude ); + } + } + } ); + } + + } catch ( Exception e ) { + return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + e != null ? e.getMessage() : "(exception is NULL)" ); + } + return new PluginResult( PluginResult.Status.OK, action + ": updated location" ); + } + + /* return error if there is no architect-view active*/ + return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" ); + } + + if ( WikitudePlugin.ACTION_CALL_JAVASCRIPT.equals( action ) ) { + if ( this.architectView != null ) { + String logMsg = null; + try { + final String callJS = args.getString( 0 ); + logMsg = callJS; + this.cordova.getActivity().runOnUiThread( new Runnable() { + + @Override + public void run() { + WikitudePlugin.this.architectView.callJavascript( callJS ); + } + } ); + + } catch ( JSONException je ) { + return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + je != null ? je.getMessage() : "(exception is NULL)" ); + } + return new PluginResult( PluginResult.Status.OK, action + ": called js, '" + logMsg + "'" ); + } else { + return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" ); + } + } + + + /* initial set-up, show ArchitectView full-screen in current screen/activity */ + if ( WikitudePlugin.ACTION_OPEN.equals( action ) ) { + this.openCallbackId = callbackId; + PluginResult result = null; + String arrStr = null; + + try { + arrStr = args.getString( 0 ); + + // arrStr = arrStr.substring( 1, arrStr.length() - 1 ); + + JSONObject arr = new JSONObject( arrStr ); + + final String apiKey = arr.getString( WikitudePlugin.JSON_KEY_APIKEY ); + final String filePath = arr.getString( WikitudePlugin.JSON_KEY_FILE_PATH ); + + this.cordova.getActivity().runOnUiThread( new Runnable() { + + @Override + public void run() { + try { + WikitudePlugin.this.addArchitectView( apiKey, filePath ); + + /* call success method once architectView was added successfully */ + if ( WikitudePlugin.this.openCallbackId != null ) { + PluginResult result = new PluginResult( PluginResult.Status.OK ); + result.setKeepCallback( false ); + WikitudePlugin.this.success( result, WikitudePlugin.this.openCallbackId ); + } + } catch ( Exception e ) { + /* in case "addArchitectView" threw an exception -> notify callback method asynchronously */ + WikitudePlugin.this.error( e != null ? e.getMessage() : "Exception is 'null'", WikitudePlugin.this.openCallbackId ); + } + } + } ); + + } catch ( Exception e ) { + result = new PluginResult( PluginResult.Status.ERROR, action + ": exception thown, " + e != null ? e.getMessage() : "(exception is NULL)" ); + result.setKeepCallback( false ); + return result; + } + + /* adding architect-view is done in separate thread, ensure to setKeepCallback so one can call success-method properly later on */ + result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": no result required, just registered callback-method" ); + result.setKeepCallback( true ); + + return result; + } + + /* fall-back return value */ + return new PluginResult( PluginResult.Status.ERROR, "no such action: " + action ); + } + + /** + * called when url was invoked in architectView (by e.g. calling document.location = "myprotocoll://foo"; + * @param url the invoked url (e.g. "myprotocoll://foo") + * @return true if call was handled properly + */ + @Override + public boolean urlWasInvoked( String url ) { + + /* call callback-method if set*/ + if ( this.urlInvokeCallbackId != null ) { + try { + /* pass called url as String to callback-method */ + PluginResult res = new PluginResult( PluginResult.Status.OK, url ); + res.setKeepCallback( true ); + this.success( res, this.urlInvokeCallbackId ); + return true; + } catch ( Exception e ) { + this.error( "invalid url invoked: " + url, this.urlInvokeCallbackId ); + } + } + return false; + } + + /** + * hides/removes ARchitect-View completely + * @return true if successful, false otherwise + */ + private boolean removeArchitectView() { + if ( this.architectView != null ) { + /* fake life-cycle calls, because activity is already up and running */ + this.architectView.onPause(); + this.architectView.onDestroy(); + this.architectView.setVisibility( View.INVISIBLE ); + ((ViewManager)this.architectView.getParent()).removeView( this.architectView ); + this.architectView = null; + return true; + } + return false; + } + + /** + * Architect-Configuration required for proper set-up + * @param apiKey + * @return + */ + protected ArchitectConfig getArchitectConfig( final String apiKey ) { + /* no special set-up required in default Wikitude-Plugin, further things required in advanced usage (e.g. Vuforia Image Recognition) */ + return new ArchitectConfig( apiKey ); + } + + /** + * add architectView to current screen + * @param apiKey developers's api key to use (hides watermarking/intro-animation if it matches your package-name) + * @param filePath the url (starting with http:// for online use; starting with LOCAL_ASSETS_PATH_ROOT if oyu want to load assets within your app-assets folder) + * @throws IOException might be thrown from ARchitect-SDK + */ + @SuppressWarnings("deprecation") + private void addArchitectView( final String apiKey, String filePath ) throws IOException { + if ( this.architectView == null ) { + this.architectView = new ArchitectView( (Activity)this.ctx.getContext() ); + + /* add content view and fake initial life-cycle */ + ((Activity)this.ctx.getContext()).addContentView( this.architectView, new ViewGroup.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT ) ); + + /* fake life-cycle calls, because activity is already up and running */ + this.architectView.onCreate( getArchitectConfig( apiKey ) ); + this.architectView.onPostCreate(); + + /* register self as url listener to fwd these native calls to PhoneGap */ + this.architectView.registerUrlListener( WikitudePlugin.this ); + + /* load asset from local directory if prefix is used */ + if ( filePath.startsWith( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT ) ) { + filePath = filePath.substring( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT.length() ); + } + this.architectView.load( filePath ); + + /* also a fake-life-cycle call (the last one before it is really shown in UI */ + this.architectView.onResume(); + } + } +} diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePluginExtended.java b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePluginExtended.java new file mode 100644 index 0000000..fb62e58 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePluginExtended.java @@ -0,0 +1,89 @@ +package com.wikitude.phonegap; + +import android.app.Activity; + +import com.qualcomm.QCAR.QCAR; +import com.wikitude.architect.ArchitectView.ArchitectConfig; +import com.wikitude.architect.VuforiaInterface; + + + +/** + * Advanced Wikitude ARchitect Plugin (= Basic Plugin + Vuforia Image Recognition feature-set) + * + * You must add "wikitudesdk.jar" and "QCAR.lib" to your libs folder and build-path and have "libQCAR.so" in project's "libs/armeabi/" directory + * + * Also add "" + * (replace Basic-plugin entry if necessary) + * + * in config.xml to enable this plug-in in your project; Ensure your architectSDK key is Vuforia-ready + * + * Note: + * This plug-in is written under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.html + * + * @version 1.0.0 + * @author Wikitude GmbH; www.wikitude.com + */ +public class WikitudePluginExtended extends WikitudePlugin { + + + static { + System.loadLibrary( "QCAR" ); + } + + /** + * Architect-Configuration required for proper set-up + * @param apiKey + * @return + */ + @Override + protected ArchitectConfig getArchitectConfig( final String apiKey ) { + final ArchitectConfig config = super.getArchitectConfig( apiKey ); + + /* required for Vuforia */ + config.setVuforiaInterface( new VuforiaServiceImplementation() ); + return config; + } + + /* required for Vuforia */ + private class VuforiaServiceImplementation implements VuforiaInterface { + + @Override + public void deInit() { + QCAR.deinit(); + } + + @Override + public int init() { + return QCAR.init(); + } + + @Override + public void onPause() { + QCAR.onPause(); + } + + @Override + public void onResume() { + QCAR.onResume(); + } + + @Override + public void onSurfaceChanged( final int arg0, final int arg1 ) { + QCAR.onSurfaceChanged( arg0, arg1 ); + } + + @Override + public void onSurfaceCreated() { + QCAR.onSurfaceCreated(); + } + + @Override + public void setInitParameters( final Activity activity, final int nFlags ) { + QCAR.setInitParameters( activity, nFlags ); + } + + } + +} diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/README.md b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/README.md new file mode 100644 index 0000000..be5b0d5 --- /dev/null +++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/README.md @@ -0,0 +1,22 @@ + +# Hello Image Recognition Android + +This document describes all necessary steps to get the sample running. +Once set up, the app augments the [Wikitude Logo](http://www.flickr.com/photos/wikitude/5571196416/) in your camera. + + +####Prerequisites: +* You need to [register as developer at Wikitude](http://developer.wikitude.com) and downloaded the SDK, two files from the SDK's Android-folder are required, as described below. + +* Download the Vuforia Framework from [Qualcomm](https://ar.qualcomm.at/sdk/android) (You need to register yourself as a Qualcomm developer) + + +# SETUP + + +* [Import HelloImageRecognition-project in Eclipse as Android-Project](https://www.google.com/webhp?sourceid=chrome-instant&ie=UTF-8&ion=1#hl=de&output=search&sclient=psy-ab&q=import%20android%20project%20into%20eclipse&oq=&gs_l=&pbx=1&fp=531bf0abdc9ea0e7&ion=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&biw=1030&bih=550) +* Copy `wikitudesdk.jar` from the Android [WikitudeSDK](http://developer.wikitude.com) to project's `libs`-folder + +* Copy `libExtensionVuforia.so` from the Android [WikitudeSDK](http://developer.wikitude.com) to `libs/armeabi`-folder (create subfolder if necessary) + +* From [Qualcomm's Vuforia SDK](https://ar.qualcomm.at/sdk/android): Copy `QCAR.jar` to your projects `libs`-folder and add it to your project's build. Also copy Vuforia's `libQCAR.so` to your `libs/armeabi`-folder \ No newline at end of file diff --git a/Android/BackgroundService/1.8.1/MyService.java b/Android/BackgroundService/1.8.1/MyService.java new file mode 100644 index 0000000..cb39447 --- /dev/null +++ b/Android/BackgroundService/1.8.1/MyService.java @@ -0,0 +1,78 @@ +package com.yournamespace.yourappname; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.util.Log; + +import com.red_folder.phonegap.plugin.backgroundservice.BackgroundService; + +public class MyService extends BackgroundService { + + private final static String TAG = MyService.class.getSimpleName(); + + private String mHelloTo = "World"; + + @Override + protected JSONObject doWork() { + JSONObject result = new JSONObject(); + + try { + SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); + String now = df.format(new Date(System.currentTimeMillis())); + + String msg = "Hello " + this.mHelloTo + " - its currently " + now; + result.put("Message", msg); + + Log.d(TAG, msg); + } catch (JSONException e) { + } + + return result; + } + + @Override + protected JSONObject getConfig() { + JSONObject result = new JSONObject(); + + try { + result.put("HelloTo", this.mHelloTo); + } catch (JSONException e) { + } + + return result; + } + + @Override + protected void setConfig(JSONObject config) { + try { + if (config.has("HelloTo")) + this.mHelloTo = config.getString("HelloTo"); + } catch (JSONException e) { + } + + } + + @Override + protected JSONObject initialiseLatestResult() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected void onTimerEnabled() { + // TODO Auto-generated method stub + + } + + @Override + protected void onTimerDisabled() { + // TODO Auto-generated method stub + + } + + +} diff --git a/Android/BackgroundService/1.8.1/README.md b/Android/BackgroundService/1.8.1/README.md new file mode 100644 index 0000000..58ac0aa --- /dev/null +++ b/Android/BackgroundService/1.8.1/README.md @@ -0,0 +1,69 @@ +# Background Service Plugin for Phonegap # + +A plugin (and framework code) that allows the development and operation of an Android Background Service. + +The example MyService Background Service will write a Hello message to the LogCat every minute. The MyService is designed as sample code. + +## Adding the plugin to your project ## + +Copy the files to the following locations: + +* libs\backgroundserviceplugin.jar +* src\com\yournamespace\yourappname\MyService.java +* assets\www\backgroundService.js +* assets\www\myService.js +* assets\www\index.html + +Add the following to res\xml\plugins.xml + +``` + +``` + +Add the following to AndroidManifest.xml + +``` + + + + + + + + + + + + + + +``` + +## Further Information ## + +Further information on the plugin can be found at: + +* http://red-folder.blogspot.co.uk/2012/09/phonegap-android-background-service.html +* http://red-folder.blogspot.com/2012/09/phonegap-android-background-service_11.html + +The below is a tutorial to create your own Twitter service: + +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-1.html +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-2.html +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-3.html + +Please let me know your thoughts and comments. + +## Licence ## + +The MIT License + +Copyright (c) 2012 Red Folder Consultancy Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + diff --git a/Android/BackgroundService/1.8.1/backgroundService.js b/Android/BackgroundService/1.8.1/backgroundService.js new file mode 100644 index 0000000..e6be715 --- /dev/null +++ b/Android/BackgroundService/1.8.1/backgroundService.js @@ -0,0 +1,171 @@ +/* + * Copyright 2012 Red Folder Consultancy Ltd + * + * 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. + */ + +/* + * Constructor + */ +function BackgroundService(serviceName) { + var ServiceName = serviceName; + + this.getServiceName = function() { + return ServiceName; + }; +}; + +/** + * All methods attempt to return the following data in both the success and failure callbacks + * Front end development should take into account any all or all of these values may be null + * + * Following returned in the JSONObject: + * Boolean Success - was the call a success + * int ErrorCode - Error code if an error occurred, else will be zero + * String ErrorMessage - Text representation of the error code + * Boolean ServiceRunning - True if the Service is running + * Boolean TimerEnabled - True if the Timer is enabled + * Boolean RegisteredForBootStart - True if the Service is registered for boot start + * JSONObject Configuration - A JSONObject of the configuration of the service (contents dependant on the service) + * JSONObject LastestResult - A JSONObject of the last result of the service (contents dependant on the service) + */ + +/** + * Starts the Service + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.startService = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'startService', + [this.getServiceName()]); +}; + +/** + * Stops the Service + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.stopService = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'stopService', + [this.getServiceName()]); +}; + +/** + * Enables the Service Timer + * + * @param milliseconds The milliseconds used for the timer + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.enableTimer = function(milliseconds, successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'enableTimer', + [this.getServiceName(), milliseconds]); +}; + +/** + * Disabled the Service Timer + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.disableTimer = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'disableTimer', + [this.getServiceName()]); +}; + +/** + * Sets the configuration for the service + * + * @oaran configuration JSONObject to be sent to the service + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.setConfiguration = function(configuration, successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'setConfiguration', + [this.getServiceName(), configuration]); +}; + +/** + * Registers the service for Boot Start + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.registerForBootStart = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'registerForBootStart', + [this.getServiceName()]); +}; + +/** + * Deregisters the service for Boot Start + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.deregisterForBootStart = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'deregisterForBootStart', + [this.getServiceName()]); +}; + +/** + * Get the current status of the service. + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.isRegisteredForBootStart = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'isRegisteredForBootStart', + [this.getServiceName()]); +}; + + +/** + * Returns the status of the service + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ +BackgroundService.prototype.getStatus = function(successCallback, failureCallback) { + return cordova.exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'getStatus', + [this.getServiceName()]); +}; + diff --git a/Android/BackgroundService/1.8.1/backgroundserviceplugin.jar b/Android/BackgroundService/1.8.1/backgroundserviceplugin.jar new file mode 100644 index 0000000..f61c89d Binary files /dev/null and b/Android/BackgroundService/1.8.1/backgroundserviceplugin.jar differ diff --git a/Android/BackgroundService/1.8.1/index.html b/Android/BackgroundService/1.8.1/index.html new file mode 100644 index 0000000..d50a914 --- /dev/null +++ b/Android/BackgroundService/1.8.1/index.html @@ -0,0 +1,219 @@ + + + + + MyService + + + + + + + + + + +

MyService

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service
Timer
Boot
Configuration
Hello To
Latest Result
+ + + \ No newline at end of file diff --git a/Android/BackgroundService/1.8.1/myService.js b/Android/BackgroundService/1.8.1/myService.js new file mode 100644 index 0000000..3849f36 --- /dev/null +++ b/Android/BackgroundService/1.8.1/myService.js @@ -0,0 +1,31 @@ +/* + * Copyright 2012 Red Folder Consultancy Ltd + * + * 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. + */ + +// /////////////////// +(function(){ +// /////////////////// + +// get local ref to global PhoneGap/Cordova/cordova object for exec function +var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + + +cordovaRef.addConstructor(function() { + cordovaRef.addPlugin("myService", new BackgroundService('com.yournamespace.yourappname.MyService')); +}); + +// /////////////////// +})(); +// /////////////////// diff --git a/Android/BackgroundService/2.0.0/MyService.java b/Android/BackgroundService/2.0.0/MyService.java new file mode 100644 index 0000000..cb39447 --- /dev/null +++ b/Android/BackgroundService/2.0.0/MyService.java @@ -0,0 +1,78 @@ +package com.yournamespace.yourappname; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.util.Log; + +import com.red_folder.phonegap.plugin.backgroundservice.BackgroundService; + +public class MyService extends BackgroundService { + + private final static String TAG = MyService.class.getSimpleName(); + + private String mHelloTo = "World"; + + @Override + protected JSONObject doWork() { + JSONObject result = new JSONObject(); + + try { + SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); + String now = df.format(new Date(System.currentTimeMillis())); + + String msg = "Hello " + this.mHelloTo + " - its currently " + now; + result.put("Message", msg); + + Log.d(TAG, msg); + } catch (JSONException e) { + } + + return result; + } + + @Override + protected JSONObject getConfig() { + JSONObject result = new JSONObject(); + + try { + result.put("HelloTo", this.mHelloTo); + } catch (JSONException e) { + } + + return result; + } + + @Override + protected void setConfig(JSONObject config) { + try { + if (config.has("HelloTo")) + this.mHelloTo = config.getString("HelloTo"); + } catch (JSONException e) { + } + + } + + @Override + protected JSONObject initialiseLatestResult() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected void onTimerEnabled() { + // TODO Auto-generated method stub + + } + + @Override + protected void onTimerDisabled() { + // TODO Auto-generated method stub + + } + + +} diff --git a/Android/BackgroundService/2.0.0/README.md b/Android/BackgroundService/2.0.0/README.md new file mode 100644 index 0000000..a415bcd --- /dev/null +++ b/Android/BackgroundService/2.0.0/README.md @@ -0,0 +1,69 @@ +# Background Service Plugin for Phonegap # + +A plugin (and framework code) that allows the development and operation of an Android Background Service. + +The example MyService Background Service will write a Hello message to the LogCat every minute. The MyService is designed as sample code. + +## Adding the plugin to your project ## + +Copy the files to the following locations: + +* libs\backgroundserviceplugin-2.0.0.jar +* src\com\yournamespace\yourappname\MyService.java +* assets\www\backgroundService-2.0.0.js +* assets\www\myService-2.0.0.js +* assets\www\index-2.0.0.html + +Add the following to res\xml\config.xml + +``` + +``` + +Add the following to AndroidManifest.xml + +``` + + + + + + + + + + + + + + +``` + +## Further Information ## + +Further information on the plugin can be found at: + +* http://red-folder.blogspot.co.uk/2012/09/phonegap-android-background-service.html +* http://red-folder.blogspot.com/2012/09/phonegap-android-background-service_11.html + +The below is a tutorial to create your own Twitter service: + +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-1.html +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-2.html +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-3.html + +Please let me know your thoughts and comments. + +## Licence ## + +The MIT License + +Copyright (c) 2012 Red Folder Consultancy Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + diff --git a/Android/BackgroundService/2.0.0/backgroundService-2.0.0.js b/Android/BackgroundService/2.0.0/backgroundService-2.0.0.js new file mode 100644 index 0000000..ae70e2c --- /dev/null +++ b/Android/BackgroundService/2.0.0/backgroundService-2.0.0.js @@ -0,0 +1,181 @@ +/* + * Copyright 2012 Red Folder Consultancy Ltd + * + * 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. + */ + +/* + * Constructor + */ +function CreateBackgroundService(serviceName, require, exports, module) { + var exec = require("cordova/exec"); + + var BackgroundService = function (serviceName) { + var ServiceName = serviceName + this.getServiceName = function() { + return ServiceName; + }; + }; + + var BackgroundServiceError = function (code, message) { + this.code = code || null; + this.message = message || null; + }; + + /** + * All methods attempt to return the following data in both the success and failure callbacks + * Front end development should take into account any all or all of these values may be null + * + * Following returned in the JSONObject: + * Boolean Success - was the call a success + * int ErrorCode - Error code if an error occurred, else will be zero + * String ErrorMessage - Text representation of the error code + * Boolean ServiceRunning - True if the Service is running + * Boolean TimerEnabled - True if the Timer is enabled + * Boolean RegisteredForBootStart - True if the Service is registered for boot start + * JSONObject Configuration - A JSONObject of the configuration of the service (contents dependant on the service) + * JSONObject LastestResult - A JSONObject of the last result of the service (contents dependant on the service) + */ + + /** + * Starts the Service + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.startService = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'startService', + [this.getServiceName()]); + }; + + /** + * Stops the Service + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.stopService = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'stopService', + [this.getServiceName()]); + }; + + /** + * Enables the Service Timer + * + * @param milliseconds The milliseconds used for the timer + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.enableTimer = function(milliseconds, successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'enableTimer', + [this.getServiceName(), milliseconds]); + }; + + /** + * Disabled the Service Timer + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.disableTimer = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'disableTimer', + [this.getServiceName()]); + }; + + /** + * Sets the configuration for the service + * + * @oaran configuration JSONObject to be sent to the service + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.setConfiguration = function(configuration, successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'setConfiguration', + [this.getServiceName(), configuration]); + }; + + /** + * Registers the service for Boot Start + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.registerForBootStart = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'registerForBootStart', + [this.getServiceName()]); + }; + + /** + * Deregisters the service for Boot Start + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.deregisterForBootStart = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'deregisterForBootStart', + [this.getServiceName()]); + }; + + /** + * Get the current status of the service. + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.isRegisteredForBootStart = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'isRegisteredForBootStart', + [this.getServiceName()]); + }; + + + /** + * Returns the status of the service + * + * @param successCallback The callback which will be called if the method is successful + * @param failureCallback The callback which will be called if the method encounters an error + */ + BackgroundService.prototype.getStatus = function(successCallback, failureCallback) { + return exec( successCallback, + failureCallback, + 'BackgroundServicePlugin', + 'getStatus', + [this.getServiceName()]); + }; + + var backgroundService = new BackgroundService(serviceName); + module.exports = backgroundService; +}; diff --git a/Android/BackgroundService/2.0.0/backgroundserviceplugin-2.0.0.jar b/Android/BackgroundService/2.0.0/backgroundserviceplugin-2.0.0.jar new file mode 100644 index 0000000..0bfceb2 Binary files /dev/null and b/Android/BackgroundService/2.0.0/backgroundserviceplugin-2.0.0.jar differ diff --git a/Android/BackgroundService/2.0.0/index-2.0.0.html b/Android/BackgroundService/2.0.0/index-2.0.0.html new file mode 100644 index 0000000..e49998d --- /dev/null +++ b/Android/BackgroundService/2.0.0/index-2.0.0.html @@ -0,0 +1,215 @@ + + + + + MyService + + + + + + + + + + +

MyService

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Service
Timer
Boot
Configuration
Hello To
Latest Result
+ + + \ No newline at end of file diff --git a/Android/BackgroundService/2.0.0/myService-2.0.0.js b/Android/BackgroundService/2.0.0/myService-2.0.0.js new file mode 100644 index 0000000..02341b1 --- /dev/null +++ b/Android/BackgroundService/2.0.0/myService-2.0.0.js @@ -0,0 +1,19 @@ +/* + * Copyright 2012 Red Folder Consultancy Ltd + * + * 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. + */ + +cordova.define( 'cordova/plugin/myService', function(require, exports, module) { + CreateBackgroundService('com.yournamespace.yourappname.MyService', require, exports, module); + }); diff --git a/Android/BackgroundService/README.md b/Android/BackgroundService/README.md new file mode 100644 index 0000000..e7f0b0a --- /dev/null +++ b/Android/BackgroundService/README.md @@ -0,0 +1,40 @@ +# Background Service Plugin for Phonegap # + +A plugin (and framework code) that allows the development and operation of an Android Background Service. + +The example MyService Background Service will write a Hello message to the LogCat every minute. The MyService is designed as sample code. + +## Versions ## + +Folders used for different Cordova versions: + +* /1.8.1 - For use with Cordova 1.8.1 +* /2.0.0 - Coming soon + +## Further Information ## + +Further information on the plugin can be found at: + +* http://red-folder.blogspot.co.uk/2012/09/phonegap-android-background-service.html +* http://red-folder.blogspot.com/2012/09/phonegap-android-background-service_11.html + +The below is a tutorial to create your own Twitter service: + +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-1.html +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-2.html +* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-3.html + +Please let me know your thoughts and comments. + +## Licence ## + +The MIT License + +Copyright (c) 2012 Red Folder Consultancy Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/Android/BarcodeScanner/assets/www/barcodescanner.js b/Android/BarcodeScanner/1.8.1/assets/www/barcodescanner.js similarity index 100% rename from Android/BarcodeScanner/assets/www/barcodescanner.js rename to Android/BarcodeScanner/1.8.1/assets/www/barcodescanner.js diff --git a/Android/BarcodeScanner/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java b/Android/BarcodeScanner/1.8.1/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java similarity index 100% rename from Android/BarcodeScanner/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java rename to Android/BarcodeScanner/1.8.1/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java diff --git a/Android/BarcodeScanner/2.0.0/assets/www/barcodescanner.js b/Android/BarcodeScanner/2.0.0/assets/www/barcodescanner.js new file mode 100644 index 0000000..52615a3 --- /dev/null +++ b/Android/BarcodeScanner/2.0.0/assets/www/barcodescanner.js @@ -0,0 +1,63 @@ +/** + * cordova is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) Matt Kane 2010 + * Copyright (c) 2011, IBM Corporation + */ + +var BarcodeScanner = function() { +}; + +//------------------------------------------------------------------- +BarcodeScanner.Encode = { + TEXT_TYPE: "TEXT_TYPE", + EMAIL_TYPE: "EMAIL_TYPE", + PHONE_TYPE: "PHONE_TYPE", + SMS_TYPE: "SMS_TYPE", + // CONTACT_TYPE: "CONTACT_TYPE", // TODO: not implemented, requires passing a Bundle class from Javascriopt to Java + // LOCATION_TYPE: "LOCATION_TYPE" // TODO: not implemented, requires passing a Bundle class from Javascriopt to Java +}; + +//------------------------------------------------------------------- +BarcodeScanner.prototype.scan = function(successCallback, errorCallback) { + if (errorCallback == null) { errorCallback = function() {}} + + if (typeof errorCallback != "function") { + console.log("BarcodeScanner.scan failure: failure parameter not a function"); + return + } + + if (typeof successCallback != "function") { + console.log("BarcodeScanner.scan failure: success callback parameter must be a function"); + return + } + + cordova.exec(successCallback, errorCallback, 'BarcodeScanner', 'scan', []); +}; + +//------------------------------------------------------------------- +BarcodeScanner.prototype.encode = function(type, data, successCallback, errorCallback, options) { + if (errorCallback == null) { errorCallback = function() {}} + + if (typeof errorCallback != "function") { + console.log("BarcodeScanner.scan failure: failure parameter not a function"); + return + } + + if (typeof successCallback != "function") { + console.log("BarcodeScanner.scan failure: success callback parameter must be a function"); + return + } + + cordova.exec(successCallback, errorCallback, 'BarcodeScanner', 'encode', [{"type": type, "data": data, "options": options}]); +}; + +//------------------------------------------------------------------- + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.barcodeScanner) { + window.plugins.barcodeScanner = new BarcodeScanner(); +} diff --git a/Android/BarcodeScanner/2.0.0/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java b/Android/BarcodeScanner/2.0.0/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java new file mode 100644 index 0000000..b18c0cb --- /dev/null +++ b/Android/BarcodeScanner/2.0.0/src/com/phonegap/plugins/barcodescanner/BarcodeScanner.java @@ -0,0 +1,152 @@ +/** + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) Matt Kane 2010 + * Copyright (c) 2011, IBM Corporation + */ + +package com.phonegap.plugins.barcodescanner; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.content.Intent; +import android.util.Log; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; + +/** + * This calls out to the ZXing barcode reader and returns the result. + */ +public class BarcodeScanner extends Plugin { + private static final String SCAN = "scan"; + private static final String ENCODE = "encode"; + private static final String CANCELLED = "cancelled"; + private static final String FORMAT = "format"; + private static final String TEXT = "text"; + private static final String DATA = "data"; + private static final String TYPE = "type"; + private static final String SCAN_INTENT = "com.phonegap.plugins.barcodescanner.SCAN"; + private static final String ENCODE_DATA = "ENCODE_DATA"; + private static final String ENCODE_TYPE = "ENCODE_TYPE"; + private static final String ENCODE_INTENT = "com.phonegap.plugins.barcodescanner.ENCODE"; + private static final String TEXT_TYPE = "TEXT_TYPE"; + private static final String EMAIL_TYPE = "EMAIL_TYPE"; + private static final String PHONE_TYPE = "PHONE_TYPE"; + private static final String SMS_TYPE = "SMS_TYPE"; + + public static final int REQUEST_CODE = 0x0ba7c0de; + + public String callback; + + /** + * Constructor. + */ + public BarcodeScanner() { + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArray of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + this.callback = callbackId; + + if (action.equals(ENCODE)) { + JSONObject obj = args.optJSONObject(0); + if (obj != null) { + String type = obj.optString(TYPE); + String data = obj.optString(DATA); + + // If the type is null then force the type to text + if (type == null) { + type = TEXT_TYPE; + } + + if (data == null) { + return new PluginResult(PluginResult.Status.ERROR, "User did not specify data to encode"); + } + + encode(type, data); + } else { + return new PluginResult(PluginResult.Status.ERROR, "User did not specify data to encode"); + } + } + else if (action.equals(SCAN)) { + scan(); + } else { + return new PluginResult(PluginResult.Status.INVALID_ACTION); + } + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + return r; + } + + + /** + * Starts an intent to scan and decode a barcode. + */ + public void scan() { + Intent intentScan = new Intent(SCAN_INTENT); + intentScan.addCategory(Intent.CATEGORY_DEFAULT); + + this.cordova.startActivityForResult((Plugin) this, intentScan, REQUEST_CODE); + } + + /** + * Called when the barcode scanner intent completes + * + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + if (requestCode == REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + JSONObject obj = new JSONObject(); + try { + obj.put(TEXT, intent.getStringExtra("SCAN_RESULT")); + obj.put(FORMAT, intent.getStringExtra("SCAN_RESULT_FORMAT")); + obj.put(CANCELLED, false); + } catch(JSONException e) { + //Log.d(LOG_TAG, "This should never happen"); + } + this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback); + } if (resultCode == Activity.RESULT_CANCELED) { + JSONObject obj = new JSONObject(); + try { + obj.put(TEXT, ""); + obj.put(FORMAT, ""); + obj.put(CANCELLED, true); + } catch(JSONException e) { + //Log.d(LOG_TAG, "This should never happen"); + } + this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback); + } else { + this.error(new PluginResult(PluginResult.Status.ERROR), this.callback); + } + } + } + + /** + * Initiates a barcode encode. + * @param data The data to encode in the bar code + * @param data2 + */ + public void encode(String type, String data) { + Intent intentEncode = new Intent(ENCODE_INTENT); + intentEncode.putExtra(ENCODE_TYPE, type); + intentEncode.putExtra(ENCODE_DATA, data); + + this.cordova.getActivity().startActivity(intentEncode); + } +} \ No newline at end of file diff --git a/Android/BarcodeScanner/README.md b/Android/BarcodeScanner/README.md index 063a4f4..006f1f5 100644 --- a/Android/BarcodeScanner/README.md +++ b/Android/BarcodeScanner/README.md @@ -2,6 +2,11 @@ Originally by Matt Kane Updates by Simon MacDonald +## Choosing the right source directory ## + +If you are using PhoneGap 1.5.0 to 1.8.1 please use the 1.8.1 directory. If you have upgraded to PhoneGap version 2.0.0 or better please use the 2.0.0 directory. + + ## Adding the Plugin to your project ## 1. Add the 'LibraryProject' into Eclipse. File -> New Android Project -> create project from existing source. @@ -21,27 +26,29 @@ Updates by Simon MacDonald 8. Add the following activity to your AndroidManifest.xml file. It should be added inside the <application/> tag. - ` - - - - - - - - - - - - ` + `` + `` + `` + `` + `` + `` + `` + `` + `` + `` + `` + `` + `` -9. And make sure you have the following permission in your AndroidManifest.xml file: +9. And make sure you have the following permissions in your AndroidManifest.xml file: `` + `` ## Using the plugin ## diff --git a/Android/ChildBrowser/src/com/phonegap/plugins/childBrowser/ChildBrowser.java b/Android/ChildBrowser/1.8.1/src/com/phonegap/plugins/childBrowser/ChildBrowser.java similarity index 100% rename from Android/ChildBrowser/src/com/phonegap/plugins/childBrowser/ChildBrowser.java rename to Android/ChildBrowser/1.8.1/src/com/phonegap/plugins/childBrowser/ChildBrowser.java diff --git a/Android/ChildBrowser/www/childbrowser.js b/Android/ChildBrowser/1.8.1/www/childbrowser.js similarity index 94% rename from Android/ChildBrowser/www/childbrowser.js rename to Android/ChildBrowser/1.8.1/www/childbrowser.js index c80b631..cc95942 100644 --- a/Android/ChildBrowser/www/childbrowser.js +++ b/Android/ChildBrowser/1.8.1/www/childbrowser.js @@ -23,10 +23,9 @@ ChildBrowser.LOCATION_CHANGED_EVENT = 1; * @param options An object that specifies additional options */ ChildBrowser.prototype.showWebPage = function(url, options) { - if (options === null || options === "undefined") { - var options = new Object(); - options.showLocationBar = true; - } + options = options || { + showLocationBar: true + }; cordova.exec(this._onEvent, this._onError, "ChildBrowser", "showWebPage", [url, options]); }; diff --git a/Android/ChildBrowser/www/childbrowser/icon_arrow_left.png b/Android/ChildBrowser/1.8.1/www/childbrowser/icon_arrow_left.png similarity index 100% rename from Android/ChildBrowser/www/childbrowser/icon_arrow_left.png rename to Android/ChildBrowser/1.8.1/www/childbrowser/icon_arrow_left.png diff --git a/Android/ChildBrowser/www/childbrowser/icon_arrow_right.png b/Android/ChildBrowser/1.8.1/www/childbrowser/icon_arrow_right.png similarity index 100% rename from Android/ChildBrowser/www/childbrowser/icon_arrow_right.png rename to Android/ChildBrowser/1.8.1/www/childbrowser/icon_arrow_right.png diff --git a/Android/ChildBrowser/www/childbrowser/icon_close.png b/Android/ChildBrowser/1.8.1/www/childbrowser/icon_close.png similarity index 100% rename from Android/ChildBrowser/www/childbrowser/icon_close.png rename to Android/ChildBrowser/1.8.1/www/childbrowser/icon_close.png diff --git a/Android/ChildBrowser/2.0.0/src/com/phonegap/plugins/childBrowser/ChildBrowser.java b/Android/ChildBrowser/2.0.0/src/com/phonegap/plugins/childBrowser/ChildBrowser.java new file mode 100644 index 0000000..ca50fe3 --- /dev/null +++ b/Android/ChildBrowser/2.0.0/src/com/phonegap/plugins/childBrowser/ChildBrowser.java @@ -0,0 +1,462 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2011, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ +package com.phonegap.plugins.childBrowser; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.text.InputType; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +public class ChildBrowser extends Plugin { + + protected static final String LOG_TAG = "ChildBrowser"; + private static int CLOSE_EVENT = 0; + private static int LOCATION_CHANGED_EVENT = 1; + + private String browserCallbackId = null; + + private Dialog dialog; + private WebView webview; + private EditText edittext; + private boolean showLocationBar = true; + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("showWebPage")) { + this.browserCallbackId = callbackId; + + // If the ChildBrowser is already open then throw an error + if (dialog != null && dialog.isShowing()) { + return new PluginResult(PluginResult.Status.ERROR, "ChildBrowser is already open"); + } + + result = this.showWebPage(args.getString(0), args.optJSONObject(1)); + + if (result.length() > 0) { + status = PluginResult.Status.ERROR; + return new PluginResult(status, result); + } else { + PluginResult pluginResult = new PluginResult(status, result); + pluginResult.setKeepCallback(true); + return pluginResult; + } + } + else if (action.equals("close")) { + closeDialog(); + + JSONObject obj = new JSONObject(); + obj.put("type", CLOSE_EVENT); + + PluginResult pluginResult = new PluginResult(status, obj); + pluginResult.setKeepCallback(false); + return pluginResult; + } + else if (action.equals("openExternal")) { + result = this.openExternal(args.getString(0), args.optBoolean(1)); + if (result.length() > 0) { + status = PluginResult.Status.ERROR; + } + } + else { + status = PluginResult.Status.INVALID_ACTION; + } + return new PluginResult(status, result); + } catch (JSONException e) { + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + /** + * Display a new browser with the specified URL. + * + * @param url The url to load. + * @param usePhoneGap Load url in PhoneGap webview + * @return "" if ok, or error message. + */ + public String openExternal(String url, boolean usePhoneGap) { + try { + Intent intent = null; + if (usePhoneGap) { + intent = new Intent().setClass(this.cordova.getActivity(), org.apache.cordova.DroidGap.class); + intent.setData(Uri.parse(url)); // This line will be removed in future. + intent.putExtra("url", url); + + // Timeout parameter: 60 sec max - May be less if http device timeout is less. + intent.putExtra("loadUrlTimeoutValue", 60000); + + // These parameters can be configured if you want to show the loading dialog + intent.putExtra("loadingDialog", "Wait,Loading web page..."); // show loading dialog + intent.putExtra("hideLoadingDialogOnPageLoad", true); // hide it once page has completely loaded + } + else { + intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + } + this.cordova.getActivity().startActivity(intent); + return ""; + } catch (android.content.ActivityNotFoundException e) { + Log.d(LOG_TAG, "ChildBrowser: Error loading url "+url+":"+ e.toString()); + return e.toString(); + } + } + + /** + * Closes the dialog + */ + private void closeDialog() { + if (dialog != null) { + dialog.dismiss(); + } + } + + /** + * Checks to see if it is possible to go back one page in history, then does so. + */ + private void goBack() { + if (this.webview.canGoBack()) { + this.webview.goBack(); + } + } + + /** + * Checks to see if it is possible to go forward one page in history, then does so. + */ + private void goForward() { + if (this.webview.canGoForward()) { + this.webview.goForward(); + } + } + + /** + * Navigate to the new page + * + * @param url to load + */ + private void navigate(String url) { + InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0); + + if (!url.startsWith("http") && !url.startsWith("file:")) { + this.webview.loadUrl("http://" + url); + } else { + this.webview.loadUrl(url); + } + this.webview.requestFocus(); + } + + + /** + * Should we show the location bar? + * + * @return boolean + */ + private boolean getShowLocationBar() { + return this.showLocationBar; + } + + /** + * Display a new browser with the specified URL. + * + * @param url The url to load. + * @param jsonObject + */ + public String showWebPage(final String url, JSONObject options) { + // Determine if we should hide the location bar. + if (options != null) { + showLocationBar = options.optBoolean("showLocationBar", true); + } + + // Create dialog in new thread + Runnable runnable = new Runnable() { + /** + * Convert our DIP units to Pixels + * + * @return int + */ + private int dpToPixels(int dipValue) { + int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, + (float) dipValue, + cordova.getActivity().getResources().getDisplayMetrics() + ); + + return value; + } + + public void run() { + // Let's create the main dialog + dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar); + dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setCancelable(true); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + public void onDismiss(DialogInterface dialog) { + try { + JSONObject obj = new JSONObject(); + obj.put("type", CLOSE_EVENT); + + sendUpdate(obj, false); + } catch (JSONException e) { + Log.d(LOG_TAG, "Should never happen"); + } + } + }); + + // Main container layout + LinearLayout main = new LinearLayout(cordova.getActivity()); + main.setOrientation(LinearLayout.VERTICAL); + + // Toolbar layout + RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); + toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, this.dpToPixels(44))); + toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2)); + toolbar.setHorizontalGravity(Gravity.LEFT); + toolbar.setVerticalGravity(Gravity.TOP); + + // Action Button Container layout + RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity()); + actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + actionButtonContainer.setHorizontalGravity(Gravity.LEFT); + actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL); + actionButtonContainer.setId(1); + + // Back button + ImageButton back = new ImageButton(cordova.getActivity()); + RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); + backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT); + back.setLayoutParams(backLayoutParams); + back.setContentDescription("Back Button"); + back.setId(2); + try { + back.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_left.png")); + } catch (IOException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + back.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + goBack(); + } + }); + + // Forward button + ImageButton forward = new ImageButton(cordova.getActivity()); + RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); + forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2); + forward.setLayoutParams(forwardLayoutParams); + forward.setContentDescription("Forward Button"); + forward.setId(3); + try { + forward.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_right.png")); + } catch (IOException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + forward.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + goForward(); + } + }); + + // Edit Text Box + edittext = new EditText(cordova.getActivity()); + RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1); + textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5); + edittext.setLayoutParams(textLayoutParams); + edittext.setId(4); + edittext.setSingleLine(true); + edittext.setText(url); + edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI); + edittext.setImeOptions(EditorInfo.IME_ACTION_GO); + edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE + edittext.setOnKeyListener(new View.OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + // If the event is a key-down event on the "enter" button + if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { + navigate(edittext.getText().toString()); + return true; + } + return false; + } + }); + + // Close button + ImageButton close = new ImageButton(cordova.getActivity()); + RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); + closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + close.setLayoutParams(closeLayoutParams); + forward.setContentDescription("Close Button"); + close.setId(5); + try { + close.setImageBitmap(loadDrawable("www/childbrowser/icon_close.png")); + } catch (IOException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + close.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + closeDialog(); + } + }); + + // WebView + webview = new WebView(cordova.getActivity()); + webview.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); + webview.setWebChromeClient(new WebChromeClient()); + WebViewClient client = new ChildBrowserClient(edittext); + webview.setWebViewClient(client); + WebSettings settings = webview.getSettings(); + settings.setJavaScriptEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setBuiltInZoomControls(true); + settings.setPluginsEnabled(true); + settings.setDomStorageEnabled(true); + webview.loadUrl(url); + webview.setId(6); + webview.getSettings().setLoadWithOverviewMode(true); + webview.getSettings().setUseWideViewPort(true); + webview.requestFocus(); + webview.requestFocusFromTouch(); + + // Add the back and forward buttons to our action button container layout + actionButtonContainer.addView(back); + actionButtonContainer.addView(forward); + + // Add the views to our toolbar + toolbar.addView(actionButtonContainer); + toolbar.addView(edittext); + toolbar.addView(close); + + // Don't add the toolbar if its been disabled + if (getShowLocationBar()) { + // Add our toolbar to our main view/layout + main.addView(toolbar); + } + + // Add our webview to our main view/layout + main.addView(webview); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); + lp.copyFrom(dialog.getWindow().getAttributes()); + lp.width = WindowManager.LayoutParams.FILL_PARENT; + lp.height = WindowManager.LayoutParams.FILL_PARENT; + + dialog.setContentView(main); + dialog.show(); + dialog.getWindow().setAttributes(lp); + } + + private Bitmap loadDrawable(String filename) throws java.io.IOException { + InputStream input = cordova.getActivity().getAssets().open(filename); + return BitmapFactory.decodeStream(input); + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + return ""; + } + + /** + * Create a new plugin result and send it back to JavaScript + * + * @param obj a JSONObject contain event payload information + */ + private void sendUpdate(JSONObject obj, boolean keepCallback) { + if (this.browserCallbackId != null) { + PluginResult result = new PluginResult(PluginResult.Status.OK, obj); + result.setKeepCallback(keepCallback); + this.success(result, this.browserCallbackId); + } + } + + /** + * The webview client receives notifications about appView + */ + public class ChildBrowserClient extends WebViewClient { + EditText edittext; + + /** + * Constructor. + * + * @param mContext + * @param edittext + */ + public ChildBrowserClient(EditText mEditText) { + this.edittext = mEditText; + } + + /** + * Notify the host application that a page has started loading. + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + String newloc; + if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) { + newloc = url; + } else { + newloc = "http://" + url; + } + + if (!newloc.equals(edittext.getText().toString())) { + edittext.setText(newloc); + } + + try { + JSONObject obj = new JSONObject(); + obj.put("type", LOCATION_CHANGED_EVENT); + obj.put("location", url); + + sendUpdate(obj, true); + } catch (JSONException e) { + Log.d("ChildBrowser", "This should never happen"); + } + } + } +} diff --git a/Android/ChildBrowser/2.0.0/www/childbrowser.js b/Android/ChildBrowser/2.0.0/www/childbrowser.js new file mode 100644 index 0000000..10226cc --- /dev/null +++ b/Android/ChildBrowser/2.0.0/www/childbrowser.js @@ -0,0 +1,92 @@ +/* + * cordova is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2011, IBM Corporation + */ + +/** + * Constructor + */ +function ChildBrowser() { +}; + +ChildBrowser.CLOSE_EVENT = 0; +ChildBrowser.LOCATION_CHANGED_EVENT = 1; + +/** + * Display a new browser with the specified URL. + * This method loads up a new web view in a dialog. + * + * @param url The url to load + * @param options An object that specifies additional options + */ +ChildBrowser.prototype.showWebPage = function(url, options) { + options = options || { + showLocationBar: true, + locationBarAlign: "top" + }; + cordova.exec(this._onEvent, this._onError, "ChildBrowser", "showWebPage", [url, options]); +}; + +/** + * Close the browser opened by showWebPage. + */ +ChildBrowser.prototype.close = function() { + cordova.exec(null, null, "ChildBrowser", "close", []); +}; + +/** + * Display a new browser with the specified URL. + * This method starts a new web browser activity. + * + * @param url The url to load + * @param usecordova Load url in cordova webview [optional] + */ +ChildBrowser.prototype.openExternal = function(url, usecordova) { + if (usecordova === true) { + navigator.app.loadUrl(url); + } + else { + cordova.exec(null, null, "ChildBrowser", "openExternal", [url, usecordova]); + } +}; + +/** + * Method called when the child browser has an event. + */ +ChildBrowser.prototype._onEvent = function(data) { + if (data.type == ChildBrowser.CLOSE_EVENT && typeof window.plugins.childBrowser.onClose === "function") { + window.plugins.childBrowser.onClose(); + } + if (data.type == ChildBrowser.LOCATION_CHANGED_EVENT && typeof window.plugins.childBrowser.onLocationChange === "function") { + window.plugins.childBrowser.onLocationChange(data.location); + } +}; + +/** + * Method called when the child browser has an error. + */ +ChildBrowser.prototype._onError = function(data) { + if (typeof window.plugins.childBrowser.onError === "function") { + window.plugins.childBrowser.onError(data); + } +}; + +/** + * Maintain API consistency with iOS + */ +ChildBrowser.prototype.install = function(){ +}; + +/** + * Load ChildBrowser + */ + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.childBrowser) { + window.plugins.childBrowser = new ChildBrowser(); +} diff --git a/Android/ChildBrowser/2.0.0/www/childbrowser/icon_arrow_left.png b/Android/ChildBrowser/2.0.0/www/childbrowser/icon_arrow_left.png new file mode 100644 index 0000000..f557c40 Binary files /dev/null and b/Android/ChildBrowser/2.0.0/www/childbrowser/icon_arrow_left.png differ diff --git a/Android/ChildBrowser/2.0.0/www/childbrowser/icon_arrow_right.png b/Android/ChildBrowser/2.0.0/www/childbrowser/icon_arrow_right.png new file mode 100644 index 0000000..6e5a0f7 Binary files /dev/null and b/Android/ChildBrowser/2.0.0/www/childbrowser/icon_arrow_right.png differ diff --git a/Android/ChildBrowser/2.0.0/www/childbrowser/icon_close.png b/Android/ChildBrowser/2.0.0/www/childbrowser/icon_close.png new file mode 100644 index 0000000..1b60f09 Binary files /dev/null and b/Android/ChildBrowser/2.0.0/www/childbrowser/icon_close.png differ diff --git a/Android/DatePicker/DatePickerPlugin.java b/Android/DatePicker/DatePickerPlugin.java index 66287ad..a7b87bc 100644 --- a/Android/DatePicker/DatePickerPlugin.java +++ b/Android/DatePicker/DatePickerPlugin.java @@ -159,7 +159,7 @@ public class DatePickerPlugin extends Plugin { date.setHours(hourOfDay); date.setMinutes(minute); - datePickerPlugin.success(new PluginResult(PluginResult.Status.OK, date.toString()), callBackId); + datePickerPlugin.success(new PluginResult(PluginResult.Status.OK, date.toLocaleString()), callBackId); } } diff --git a/Android/DatePicker/README.md b/Android/DatePicker/README.md index 364e63e..ee3e38f 100644 --- a/Android/DatePicker/README.md +++ b/Android/DatePicker/README.md @@ -9,7 +9,7 @@ 6. Add the following jQuery fragment to handle the click on these input elements: ``` - $('.nativedatepicker').click(function(event) { + $('.nativedatepicker').focus(function(event) { var currentField = $(this); var myNewDate = Date.parse(currentField.val()) || new Date(); @@ -21,13 +21,13 @@ }, function(returnDate) { var newDate = new Date(returnDate); currentField.val(newDate.toString("dd/MMM/yyyy")); - + // This fixes the problem you mention at the bottom of this script with it not working a second/third time around, because it is in focus. currentField.blur(); }); }); - $('.nativetimepicker').click(function(event) { + $('.nativetimepicker').focus(function(event) { var currentField = $(this); var time = currentField.val(); var myNewTime = new Date(); @@ -41,8 +41,11 @@ mode : 'time', // date or time or blank for both allowOldDates : true }, function(returnDate) { + // returnDate is generated by .toLocaleString() in Java so it will be relative to the current time zone var newDate = new Date(returnDate); currentField.val(newDate.toString("HH:mm")); + + currentField.blur(); }); }); ``` diff --git a/Android/Downloader/Downloader.java b/Android/Downloader/Downloader.java index fbe9648..6196100 100644 --- a/Android/Downloader/Downloader.java +++ b/Android/Downloader/Downloader.java @@ -15,8 +15,8 @@ import org.json.JSONObject; import android.util.Log; import android.os.Environment; -import com.phonegap.api.Plugin; -import com.phonegap.api.PluginResult; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; public class Downloader extends Plugin { diff --git a/Android/ForegroundCamera/CameraActivity.java b/Android/ForegroundCamera/CameraActivity.java new file mode 100644 index 0000000..27b9923 --- /dev/null +++ b/Android/ForegroundCamera/CameraActivity.java @@ -0,0 +1,136 @@ +/* + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. + */ + +package com.foregroundcameraplugin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import android.app.Activity; +import android.hardware.Camera; +import android.hardware.Camera.AutoFocusCallback; +import android.hardware.Camera.PictureCallback; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; + +/** + * Camera Activity Class. Configures Android camera to take picture and show it. + */ +public class CameraActivity extends Activity { + + private static final String TAG = "CameraActivity"; + + private Camera mCamera; + private ForegroundCameraPreview mPreview; + private boolean pressed = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.foregroundcameraplugin); + + // Create an instance of Camera + mCamera = getCameraInstance(); + + // Create a Preview and set it as the content of activity. + mPreview = new ForegroundCameraPreview(this, mCamera); + FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); + preview.addView(mPreview); + + // Add a listener to the Capture button + Button captureButton = (Button) findViewById(R.id.button_capture); + captureButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + + if (pressed) + return; + + // Set pressed = true to prevent freezing. + // Issue 1 at + // http://code.google.com/p/foreground-camera-plugin/issues/detail?id=1 + pressed = true; + + // get an image from the camera + mCamera.autoFocus(new AutoFocusCallback() { + + public void onAutoFocus(boolean success, Camera camera) { + mCamera.takePicture(null, null, mPicture); + } + }); + } + }); + + Button cancelButton = (Button) findViewById(R.id.button_cancel); + cancelButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + pressed = false; + setResult(RESULT_CANCELED); + finish(); + } + }); + } + + @Override + protected void onPause() { + if (mCamera != null) { + mCamera.release(); // release the camera for other applications + mCamera = null; + } + super.onPause(); + } + + /** A safe way to get an instance of the Camera object. */ + public static Camera getCameraInstance() { + Camera c = null; + try { + c = Camera.open(); // attempt to get a Camera instance + } catch (Exception e) { + // Camera is not available (in use or does not exist) + } + return c; // returns null if camera is unavailable + } + + private PictureCallback mPicture = new PictureCallback() { + + public void onPictureTaken(byte[] data, Camera camera) { + + Uri fileUri = (Uri) getIntent().getExtras().get( + MediaStore.EXTRA_OUTPUT); + + File pictureFile = new File(fileUri.getPath()); + + try { + FileOutputStream fos = new FileOutputStream(pictureFile); + fos.write(data); + fos.close(); + } catch (FileNotFoundException e) { + Log.d(TAG, "File not found: " + e.getMessage()); + } catch (IOException e) { + Log.d(TAG, "Error accessing file: " + e.getMessage()); + } + setResult(RESULT_OK); + pressed = false; + finish(); + } + }; +} \ No newline at end of file diff --git a/Android/ForegroundCamera/ForegroundCameraLauncher.java b/Android/ForegroundCamera/ForegroundCameraLauncher.java new file mode 100644 index 0000000..62b3a4d --- /dev/null +++ b/Android/ForegroundCamera/ForegroundCameraLauncher.java @@ -0,0 +1,380 @@ +/* + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vin�cius Fonseca. + + 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. + */ + +package com.foregroundcameraplugin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.cordova.CameraLauncher; +import org.apache.cordova.ExifHelper; +import org.apache.cordova.api.CordovaInterface; +import org.apache.cordova.api.LOG; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; + +/** + * This class launches the camera view, allows the user to take a picture, + * closes the camera view, and returns the captured image. When the camera view + * is closed, the screen displayed before the camera view was shown is + * redisplayed. + */ +public class ForegroundCameraLauncher extends CameraLauncher { + + private static final String LOG_TAG = "ForegroundCameraLauncher"; + + private int mQuality; + private int targetWidth; + private int targetHeight; + + private Uri imageUri; + private File photo; + + public String callbackId; + private int numPics; + + private static final String _DATA = "_data"; + + /** + * Constructor. + */ + public ForegroundCameraLauncher() { + } + + /** + * Executes the request and returns PluginResult. + * + * @param action + * The action to execute. + * @param args + * JSONArry of arguments for the plugin. + * @param callbackId + * The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + this.callbackId = callbackId; + + try { + if (action.equals("takePicture")) { + this.targetHeight = 0; + this.targetWidth = 0; + this.mQuality = 80; + + JSONObject options = args.optJSONObject(0); + if (options != null) { + this.targetHeight = options.getInt("targetHeight"); + this.targetWidth = options.getInt("targetWidth"); + this.mQuality = options.getInt("quality"); + } + + this.takePicture(); + + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + return r; + } + return new PluginResult(status, result); + } catch (JSONException e) { + e.printStackTrace(); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + // -------------------------------------------------------------------------- + // LOCAL METHODS + // -------------------------------------------------------------------------- + + /** + * Take a picture with the camera. When an image is captured or the camera + * view is cancelled, the result is returned in + * CordovaActivity.onActivityResult, which forwards the result to + * this.onActivityResult. + * + * The image can either be returned as a base64 string or a URI that points + * to the file. To display base64 string in an img tag, set the source to: + * img.src="data:image/jpeg;base64,"+result; or to display URI in an img tag + * img.src=result; + * + */ + public void takePicture() { + // Save the number of images currently on disk for later + this.numPics = queryImgDB().getCount(); + + Intent intent = new Intent(this.cordova.getActivity().getApplicationContext(), CameraActivity.class); + this.photo = createCaptureFile(); + this.imageUri = Uri.fromFile(photo); + intent.putExtra(MediaStore.EXTRA_OUTPUT, this.imageUri); + + this.cordova.startActivityForResult((Plugin) this, intent, 1); + } + + /** + * Create a file in the applications temporary directory based upon the + * supplied encoding. + * + * @return a File object pointing to the temporary picture + */ + private File createCaptureFile() { + File photo = new File(getTempDirectoryPath(this.cordova.getActivity().getApplicationContext()), "Pic.jpg"); + return photo; + } + + /** + * Called when the camera view exits. + * + * @param requestCode + * The request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode + * The integer result code returned by the child activity through + * its setResult(). + * @param intent + * An Intent, which can return result data to the caller (various + * data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + + // If image available + if (resultCode == Activity.RESULT_OK) { + try { + // Create an ExifHelper to save the exif data that is lost + // during compression + ExifHelper exif = new ExifHelper(); + exif.createInFile(getTempDirectoryPath(this.cordova.getActivity().getApplicationContext()) + + "/Pic.jpg"); + exif.readExifData(); + + // Read in bitmap of captured image + Bitmap bitmap; + try { + bitmap = android.provider.MediaStore.Images.Media + .getBitmap(this.cordova.getActivity().getContentResolver(), imageUri); + } catch (FileNotFoundException e) { + Uri uri = intent.getData(); + android.content.ContentResolver resolver = this.cordova.getActivity().getContentResolver(); + bitmap = android.graphics.BitmapFactory + .decodeStream(resolver.openInputStream(uri)); + } + + bitmap = scaleBitmap(bitmap); + + // Create entry in media store for image + // (Don't use insertImage() because it uses default compression + // setting of 50 - no way to change it) + ContentValues values = new ContentValues(); + values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, + "image/jpeg"); + Uri uri = null; + try { + uri = this.cordova.getActivity().getContentResolver() + .insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + values); + } catch (UnsupportedOperationException e) { + LOG.d(LOG_TAG, "Can't write to external media storage."); + try { + uri = this.cordova.getActivity().getContentResolver() + .insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, + values); + } catch (UnsupportedOperationException ex) { + LOG.d(LOG_TAG, "Can't write to internal media storage."); + this.failPicture("Error capturing image - no media storage found."); + return; + } + } + + // Add compressed version of captured image to returned media + // store Uri + OutputStream os = this.cordova.getActivity().getContentResolver() + .openOutputStream(uri); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + exif.createOutFile(getRealPathFromURI(uri, this.ctx)); + exif.writeExifData(); + + // Send Uri back to JavaScript for viewing image + this.success(new PluginResult(PluginResult.Status.OK, + getRealPathFromURI(uri, this.ctx)), this.callbackId); + + bitmap.recycle(); + bitmap = null; + System.gc(); + + checkForDuplicateImage(); + } catch (IOException e) { + e.printStackTrace(); + this.failPicture("Error capturing image."); + } + } + + // If cancelled + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Camera cancelled."); + } + + // If something else + else { + this.failPicture("Did not complete!"); + } + } + + /** + * Scales the bitmap according to the requested size. + * + * @param bitmap + * The bitmap to scale. + * @return Bitmap A new Bitmap object of the same bitmap after scaling. + */ + public Bitmap scaleBitmap(Bitmap bitmap) { + int newWidth = this.targetWidth; + int newHeight = this.targetHeight; + int origWidth = bitmap.getWidth(); + int origHeight = bitmap.getHeight(); + + // If no new width or height were specified return the original bitmap + if (newWidth <= 0 && newHeight <= 0) { + return bitmap; + } + // Only the width was specified + else if (newWidth > 0 && newHeight <= 0) { + newHeight = (newWidth * origHeight) / origWidth; + } + // only the height was specified + else if (newWidth <= 0 && newHeight > 0) { + newWidth = (newHeight * origWidth) / origHeight; + } + // If the user specified both a positive width and height + // (potentially different aspect ratio) then the width or height is + // scaled so that the image fits while maintaining aspect ratio. + // Alternatively, the specified width and height could have been + // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this + // would result in whitespace in the new image. + else { + double newRatio = newWidth / (double) newHeight; + double origRatio = origWidth / (double) origHeight; + + if (origRatio > newRatio) { + newHeight = (newWidth * origHeight) / origWidth; + } else if (origRatio < newRatio) { + newWidth = (newHeight * origWidth) / origHeight; + } + } + + return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); + } + + /** + * Creates a cursor that can be used to determine how many images we have. + * + * @return a cursor + */ + private Cursor queryImgDB() { + return this.cordova.getActivity().getContentResolver().query( + android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + new String[] { MediaStore.Images.Media._ID }, null, null, null); + } + + /** + * Used to find out if we are in a situation where the Camera Intent adds to + * images to the content store. If we are using a FILE_URI and the number of + * images in the DB increases by 2 we have a duplicate, when using a + * DATA_URL the number is 1. + */ + private void checkForDuplicateImage() { + int diff = 2; + Cursor cursor = queryImgDB(); + int currentNumOfImages = cursor.getCount(); + + // delete the duplicate file if the difference is 2 for file URI or 1 + // for Data URL + if ((currentNumOfImages - numPics) == diff) { + cursor.moveToLast(); + int id = Integer.valueOf(cursor.getString(cursor + .getColumnIndex(MediaStore.Images.Media._ID))) - 1; + Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + + "/" + id); + this.cordova.getActivity().getContentResolver().delete(uri, null, null); + } + } + + /** + * Determine if we can use the SD Card to store the temporary file. If not + * then use the internal cache directory. + * + * @return the absolute path of where to store the file + */ + private String getTempDirectoryPath(Context ctx) { + File cache = null; + + // SD Card Mounted + if (Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED)) { + cache = new File(Environment.getExternalStorageDirectory() + .getAbsolutePath() + + "/Android/data/" + + ctx.getPackageName() + "/cache/"); + } + // Use internal storage + else { + cache = ctx.getCacheDir(); + } + + // Create the cache directory if it doesn't exist + if (!cache.exists()) { + cache.mkdirs(); + } + + return cache.getAbsolutePath(); + } + + /** + * Queries the media store to find out what the file path is for the Uri we + * supply + * + * @param contentUri + * the Uri of the audio/image/video + * @param ctx + * the current applicaiton context + * @return the full path to the file + */ + private String getRealPathFromURI(Uri contentUri, CordovaInterface ctx) { + String[] proj = { _DATA }; + Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null); + int column_index = cursor.getColumnIndexOrThrow(_DATA); + cursor.moveToFirst(); + return cursor.getString(column_index); + } +} diff --git a/Android/ForegroundCamera/ForegroundCameraPreview.java b/Android/ForegroundCamera/ForegroundCameraPreview.java new file mode 100644 index 0000000..994b946 --- /dev/null +++ b/Android/ForegroundCamera/ForegroundCameraPreview.java @@ -0,0 +1,73 @@ +/* + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. +*/ + +package com.foregroundcameraplugin; + +import java.io.IOException; + +import android.content.Context; +import android.hardware.Camera; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class ForegroundCameraPreview extends SurfaceView implements SurfaceHolder.Callback { + private SurfaceHolder mHolder; + private Camera mCamera; + private static final String TAG = "CameraPreview"; + + public ForegroundCameraPreview(Context context, Camera camera) { + super(context); + mCamera = camera; + + mHolder = getHolder(); + mHolder.addCallback(this); + mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + } + + public void surfaceCreated(SurfaceHolder holder) { + try { + mCamera.setPreviewDisplay(holder); + mCamera.startPreview(); + } catch (IOException e) { + Log.d(TAG, "Error setting camera preview: " + e.getMessage()); + } + } + + public void surfaceDestroyed(SurfaceHolder holder) { + } + + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + + if (mHolder.getSurface() == null){ + return; + } + + try { + mCamera.stopPreview(); + } catch (Exception e){ + } + + try { + mCamera.setPreviewDisplay(mHolder); + mCamera.startPreview(); + + } catch (Exception e){ + Log.d(TAG, "Error starting camera preview: " + e.getMessage()); + } + } +} diff --git a/Android/ForegroundCamera/LICENSE b/Android/ForegroundCamera/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/Android/ForegroundCamera/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Android/ForegroundCamera/NOTICE b/Android/ForegroundCamera/NOTICE new file mode 100644 index 0000000..999a0b1 --- /dev/null +++ b/Android/ForegroundCamera/NOTICE @@ -0,0 +1,9 @@ +Foreground Camera Plugin for Phonegap (Cordova) +Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org) + +- src/com/foregroundcameraplugin/ForegroundCameraLauncher.java was based on CameraLauncher.java, found in Phonegap (Apache Cordova) version 1.8.0. +- assets/www/js/camera.js was based on cordova-1.8.0.js, found in Phonegap (Apache Cordova) version 1.8.0. +- res/xml/plugins.xml was adapted from plugins.xml, found in Phonegap (Apache Cordova) version 1.8.0. \ No newline at end of file diff --git a/Android/ForegroundCamera/README.txt b/Android/ForegroundCamera/README.txt new file mode 100644 index 0000000..72e0668 --- /dev/null +++ b/Android/ForegroundCamera/README.txt @@ -0,0 +1,43 @@ + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. + +Foreground Camera Plugin for Phonegap (Cordova). + +Originally by: - Bruno Carreira + - Lucas Farias + - Rafael Luna + - Vinicius Fonseca + +The default Phonegap (Cordova) Camera Plugin calls the native camera and this makes Android Garbage Collector to kill background applications. This plugin avoid your application to go background and be killed by Garbage Collector with other applications. We used the Phonegap source code and modified it to avoid this problem. This plugin works only with File URI. + +Adding the plugin to your project + + 1) To install the plugin, move the file camera.js to your project's www folder and include a reference to it in your html files. + 2) Put the Java files in your src/ folder. + 3) Change the default Camera Plugin into res/xml/plugins.xml file to . + 4) Put the strings.xml in your res/values folder. + 5) Put the foregroundcameraplugin.xml in your res/layout folder. + 6) In you AndroidManifest.xml, put this permissions: + + + And declare the Camera Activity: + + + +Using the plugin + + See the index.xhtml. \ No newline at end of file diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/.classpath b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/.classpath new file mode 100644 index 0000000..e088a5d --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/.project b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/.project new file mode 100644 index 0000000..ac1c057 --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/.project @@ -0,0 +1,33 @@ + + + ForegroundCameraPlugin + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/AndroidManifest.xml b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/AndroidManifest.xml new file mode 100644 index 0000000..e5a4ead --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/AndroidManifest.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/LICENSE b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/NOTICE b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/NOTICE new file mode 100644 index 0000000..999a0b1 --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/NOTICE @@ -0,0 +1,9 @@ +Foreground Camera Plugin for Phonegap (Cordova) +Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org) + +- src/com/foregroundcameraplugin/ForegroundCameraLauncher.java was based on CameraLauncher.java, found in Phonegap (Apache Cordova) version 1.8.0. +- assets/www/js/camera.js was based on cordova-1.8.0.js, found in Phonegap (Apache Cordova) version 1.8.0. +- res/xml/plugins.xml was adapted from plugins.xml, found in Phonegap (Apache Cordova) version 1.8.0. \ No newline at end of file diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/README.txt b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/README.txt new file mode 100644 index 0000000..00b8b68 --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/README.txt @@ -0,0 +1,49 @@ + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. + +Foreground Camera Plugin for Phonegap (Cordova). + +Originally by: - Bruno Carreira + - Lucas Farias + - Rafael Luna + - Vinicius Fonseca + +The default Phonegap (Cordova) Camera Plugin calls the native camera and this makes Android Garbage Collector to kill background applications. This plugin avoid your application to go background and be killed by Garbage Collector with other applications. We used the Phonegap source code and modified it to avoid this problem. This plugin works only with File URI. + +Adding the plugin to your project + + 1) To install the plugin, move the file camera.js to your project's www folder and include a reference to it in your html files. + 2) Put the Java files in your src/ folder. + 3) Change the default Camera Plugin into res/xml/config.xml file to . + 4) Put the strings.xml in your res/values folder. + 5) Put the foregroundcameraplugin.xml in your res/layout folder. + 6) In you AndroidManifest.xml, put this permissions: + + + + + + + + + And declare the Camera Activity: + + + +Using the plugin + + See the index.xhtml. \ No newline at end of file diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/index.html b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/index.html new file mode 100644 index 0000000..672d577 --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/index.html @@ -0,0 +1,83 @@ + + + + + Capture Photo + + + + + + +
+ + + \ No newline at end of file diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/js/camera.js b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/js/camera.js new file mode 100644 index 0000000..1c246f4 --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/js/camera.js @@ -0,0 +1,91 @@ +/** + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vin�cius Fonseca. + + 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. +*/ + +/** + * This class provides access to the device camera. + * + * @constructor + */ +var Camera = function() { + this.successCallback = null; + this.errorCallback = null; + this.options = null; +}; + +/** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=DATA_URL. + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ +Camera.prototype.getPicture = function(successCallback, errorCallback, + options) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Camera Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Camera Error: errorCallback is not a function"); + return; + } + + if (options === null || typeof options === "undefined") { + options = {}; + } + if (options.quality === null || typeof options.quality === "undefined") { + options.quality = 80; + } + if (options.maxResolution === null + || typeof options.maxResolution === "undefined") { + options.maxResolution = 0; + } + + if (options.targetWidth === null + || typeof options.targetWidth === "undefined") { + options.targetWidth = -1; + } else if (typeof options.targetWidth === "string") { + var width = new Number(options.targetWidth); + if (isNaN(width) === false) { + options.targetWidth = width.valueOf(); + } + } + if (options.targetHeight === null + || typeof options.targetHeight === "undefined") { + options.targetHeight = -1; + } else if (typeof options.targetHeight === "string") { + var height = new Number(options.targetHeight); + if (isNaN(height) === false) { + options.targetHeight = height.valueOf(); + } + } + + cordova.exec(successCallback, errorCallback, "Camera", "takePicture", + [ options ]); +}; + +cordova.addConstructor(function() { + if (typeof navigator.camera === "undefined") { + navigator.camera = new Camera(); + } +}); \ No newline at end of file diff --git a/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/js/cordova-2.1.0.js b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/js/cordova-2.1.0.js new file mode 100644 index 0000000..9e7fa8e --- /dev/null +++ b/Android/ForegroundCamera/Sample/ForegroundCameraPlugin/assets/www/js/cordova-2.1.0.js @@ -0,0 +1,5814 @@ +// commit 143f5221a6251c9cbccdedc57005c61551b97f12 + +// File generated at :: Wed Sep 12 2012 12:51:58 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code + */ + fireDocumentEvent: function(type, data, bNoDetach) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + if( bNoDetach ) { + documentEventHandlers[type].fire(evt); + } + else { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'), + nextGuid = 1; + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + var headers = null; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + headers = options.headers; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + * @param headers {Object} Keys are header names, values are header values. Multiple + * headers of the same name are not supported. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; + this.headers = headers || null; +}; + +module.exports = FileUploadOptions; + +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger than file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(null, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param msgType The 'type' of update this is + * @param value Use of value is determined by the msgType + */ +Media.onStatus = function(id, msgType, value) { + + var media = mediaObjects[id]; + + if(media) { + switch(msgType) { + case Media.MEDIA_STATE : + media.statusCallback && media.statusCallback(value); + if(value == Media.MEDIA_STOPPED) { + media.successCallback && media.successCallback(); + } + break; + case Media.MEDIA_DURATION : + media._duration = value; + break; + case Media.MEDIA_ERROR : + media.errorCallback && media.errorCallback(value); + break; + case Media.MEDIA_POSITION : + media._position = Number(value); + break; + default : + console && console.error && console.error("Unhandled Media.onStatus :: " + msgType); + break; + } + } + else { + console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id); + } + +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. +*/ +/* + According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror + We should never be creating these objects, we should just implement the interface + which has 1 property for an instance, 'code' + + instead of doing : + errorCallbackFunction( new MediaError(3,'msg') ); +we should simply use a literal : + errorCallbackFunction( {'code':3} ); + */ + +if(!MediaError) { + var MediaError = function(code, msg) { + this.code = (typeof code != 'undefined') ? code : null; + this.message = msg || ""; // message is NON-standard! do not use! + }; +} + +MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0; +MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1; +MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2; +MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4; +// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below. +// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes +MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4; + +module.exports = MediaError; + +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +module.exports = MediaFile; + +}); + +// file: lib/common/plugin/MediaFileData.js +define("cordova/plugin/MediaFileData", function(require, exports, module) { +/** + * MediaFileData encapsulates format information of a media file. + * + * @param {DOMString} codecs + * @param {long} bitrate + * @param {long} height + * @param {long} width + * @param {float} duration + */ +var MediaFileData = function(codecs, bitrate, height, width, duration){ + this.codecs = codecs || null; + this.bitrate = bitrate || 0; + this.height = height || 0; + this.width = width || 0; + this.duration = duration || 0; +}; + +module.exports = MediaFileData; +}); + +// file: lib/common/plugin/Metadata.js +define("cordova/plugin/Metadata", function(require, exports, module) { +/** + * Information about the state of the file or directory + * + * {Date} modificationTime (readonly) + */ +var Metadata = function(time) { + this.modificationTime = (typeof time != 'undefined'?new Date(time):null); +}; + +module.exports = Metadata; +}); + +// file: lib/common/plugin/Position.js +define("cordova/plugin/Position", function(require, exports, module) { +var Coordinates = require('cordova/plugin/Coordinates'); + +var Position = function(coords, timestamp) { + if (coords) { + this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy); + } else { + this.coords = new Coordinates(); + } + this.timestamp = (timestamp !== undefined) ? timestamp : new Date(); +}; + +module.exports = Position; + +}); + +// file: lib/common/plugin/PositionError.js +define("cordova/plugin/PositionError", function(require, exports, module) { +/** + * Position error object + * + * @constructor + * @param code + * @param message + */ +var PositionError = function(code, message) { + this.code = code || null; + this.message = message || ''; +}; + +PositionError.PERMISSION_DENIED = 1; +PositionError.POSITION_UNAVAILABLE = 2; +PositionError.TIMEOUT = 3; + +module.exports = PositionError; +}); + +// file: lib/common/plugin/ProgressEvent.js +define("cordova/plugin/ProgressEvent", function(require, exports, module) { +// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill +// Feature test: See if we can instantiate a native ProgressEvent; +// if so, use that approach, +// otherwise fill-in with our own implementation. +// +// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview. +var ProgressEvent = (function() { + /* + var createEvent = function(data) { + var event = document.createEvent('Events'); + event.initEvent('ProgressEvent', false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + if (data.target) { + // TODO: cannot call .dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retrieved a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/android/plugin/android/app.js +define("cordova/plugin/android/app", function(require, exports, module) { +var exec = require('cordova/exec'); + +module.exports = { + /** + * Clear the resource cache. + */ + clearCache:function() { + exec(null, null, "App", "clearCache", []); + }, + + /** + * Load the url into the webview or into new browser instance. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * clearHistory: boolean => clear webview history (default=false) + * openExternal: boolean => open in a new browser (default=false) + * + * Example: + * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ + loadUrl:function(url, props) { + exec(null, null, "App", "loadUrl", [url, props]); + }, + + /** + * Cancel loadUrl that is waiting to be loaded. + */ + cancelLoadUrl:function() { + exec(null, null, "App", "cancelLoadUrl", []); + }, + + /** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ + clearHistory:function() { + exec(null, null, "App", "clearHistory", []); + }, + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + backHistory:function() { + exec(null, null, "App", "backHistory", []); + }, + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ + overrideBackbutton:function(override) { + exec(null, null, "App", "overrideBackbutton", [override]); + }, + + /** + * Exit and terminate the application. + */ + exitApp:function() { + return exec(null, null, "App", "exitApp", []); + } +}; +}); + +// file: lib/android/plugin/android/callback.js +define("cordova/plugin/android/callback", function(require, exports, module) { +var port = null, + token = null, + xmlhttp; + +function startXhr() { + // cordova/exec depends on this module, so we can't require cordova/exec on the module level. + var exec = require('cordova/exec'), + xmlhttp = new XMLHttpRequest(); + + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if (!xmlhttp) { + return; + } + if (xmlhttp.readyState === 4){ + // If callback has JavaScript statement to execute + if (xmlhttp.status === 200) { + + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); + setTimeout(function() { + try { + var t = eval(msg); + } + catch (e) { + // If we're getting an error here, seeing the message will help in debugging + console.log("JSCallback: Message from Server: " + msg); + console.log("JSCallback Error: "+e); + } + }, 1); + setTimeout(startXhr, 1); + } + + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(startXhr, 10); + } + + // 0 == Page is unloading. + // 400 == Bad request. + // 403 == invalid token. + // 503 == server stopped. + else { + console.log("JSCallback Error: Request failed with status " + xmlhttp.status); + exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING); + } + } + }; + + if (port === null) { + port = prompt("getPort", "gap_callbackServer:"); + } + if (token === null) { + token = prompt("getToken", "gap_callbackServer:"); + } + xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true); + xmlhttp.send(); +} + +module.exports = { + start: function() { + startXhr(); + }, + + stop: function() { + if (xmlhttp) { + var tmp = xmlhttp; + xmlhttp = null; + tmp.abort(); + } + }, + + isAvailable: function() { + return ("true" != prompt("usePolling", "gap_callbackServer:")); + } +}; + + +}); + +// file: lib/android/plugin/android/device.js +define("cordova/plugin/android/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + app = require('cordova/plugin/android/app'); + +module.exports = { + /* + * DEPRECATED + * This is only for Android. + * + * You must explicitly override the back button. + */ + overrideBackButton:function() { + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This resets the back button to the default behaviour + */ + resetBackButton:function() { + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This terminates the activity! + */ + exitApp:function() { + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); + } +}; + +}); + +// file: lib/android/plugin/android/notification.js +define("cordova/plugin/android/notification", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides Android enhanced notification API. + */ +module.exports = { + activityStart : function(title, message) { + // If title and message not specified then mimic Android behavior of + // using default strings. + if (typeof title === "undefined" && typeof message == "undefined") { + title = "Busy"; + message = 'Please wait...'; + } + + exec(null, null, 'Notification', 'activityStart', [ title, message ]); + }, + + /** + * Close an activity dialog + */ + activityStop : function() { + exec(null, null, 'Notification', 'activityStop', []); + }, + + /** + * Display a progress dialog with progress bar that goes from 0 to 100. + * + * @param {String} + * title Title of the progress dialog. + * @param {String} + * message Message to display in the dialog. + */ + progressStart : function(title, message) { + exec(null, null, 'Notification', 'progressStart', [ title, message ]); + }, + + /** + * Close the progress dialog. + */ + progressStop : function() { + exec(null, null, 'Notification', 'progressStop', []); + }, + + /** + * Set the progress dialog value. + * + * @param {Number} + * value 0-100 + */ + progressValue : function(value) { + exec(null, null, 'Notification', 'progressValue', [ value ]); + } +}; +}); + +// file: lib/android/plugin/android/polling.js +define("cordova/plugin/android/polling", function(require, exports, module) { +var cordova = require('cordova'), + POLL_INTERVAL = 50, + enabled = false; + +function pollOnce() { + var msg = prompt("", "gap_poll:"); + if (msg) { + try { + eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + return true; + } + return false; +} + +function doPoll() { + if (!enabled) { + return; + } + var nextDelay = POLL_INTERVAL; + if (pollOnce()) { + nextDelay = 0; + } + setTimeout(doPoll, nextDelay); +} + +module.exports = { + start: function() { + enabled = true; + setTimeout(doPoll, 1); + }, + stop: function() { + enabled = false; + }, + pollOnce: pollOnce +}; + + +}); + +// file: lib/android/plugin/android/storage.js +define("cordova/plugin/android/storage", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + channel = require('cordova/channel'); + +var queryQueue = {}; + +/** + * SQL result set object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Rows = function() { + this.resultSet = []; // results array + this.length = 0; // number of rows +}; + +/** + * Get item from SQL result set + * + * @param row The row number to return + * @return The row object + */ +DroidDB_Rows.prototype.item = function(row) { + return this.resultSet[row]; +}; + +/** + * SQL result set that is returned to user. + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Result = function() { + this.rows = new DroidDB_Rows(); +}; + +/** + * Callback from native code when query is complete. + * PRIVATE METHOD + * + * @param id Query id + */ +function completeQuery(id, data) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + + // Save query results + var r = new DroidDB_Result(); + r.rows.resultSet = data; + r.rows.length = data.length; + try { + if (typeof query.successCallback === 'function') { + query.successCallback(query.tx, r); + } + } catch (ex) { + console.log("executeSql error calling user success callback: "+ex); + } + + tx.queryComplete(id); + } + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * Callback from native code when query fails + * PRIVATE METHOD + * + * @param reason Error message + * @param id Query id + */ +function failQuery(reason, id) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + tx.queryList = {}; + + try { + if (typeof query.errorCallback === 'function') { + query.errorCallback(query.tx, reason); + } + } catch (ex) { + console.log("executeSql error calling user error callback: "+ex); + } + + tx.queryFailed(id, reason); + } + + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * SQL query object + * PRIVATE METHOD + * + * @constructor + * @param tx The transaction object that this query belongs to + */ +var DroidDB_Query = function(tx) { + + // Set the id of the query + this.id = utils.createUUID(); + + // Add this query to the queue + queryQueue[this.id] = this; + + // Init result + this.resultSet = []; + + // Set transaction that this query belongs to + this.tx = tx; + + // Add this query to transaction list + this.tx.queryList[this.id] = this; + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + +}; + +/** + * Transaction object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Tx = function() { + + // Set the id of the transaction + this.id = utils.createUUID(); + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + + // Query list + this.queryList = {}; +}; + +/** + * Mark query in transaction as complete. + * If all queries are complete, call the user's transaction success callback. + * + * @param id Query id + */ +DroidDB_Tx.prototype.queryComplete = function(id) { + delete this.queryList[id]; + + // If no more outstanding queries, then fire transaction success + if (this.successCallback) { + var count = 0; + var i; + for (i in this.queryList) { + if (this.queryList.hasOwnProperty(i)) { + count++; + } + } + if (count === 0) { + try { + this.successCallback(); + } catch(e) { + console.log("Transaction error calling user success callback: " + e); + } + } + } +}; + +/** + * Mark query in transaction as failed. + * + * @param id Query id + * @param reason Error message + */ +DroidDB_Tx.prototype.queryFailed = function(id, reason) { + + // The sql queries in this transaction have already been run, since + // we really don't have a real transaction implemented in native code. + // However, the user callbacks for the remaining sql queries in transaction + // will not be called. + this.queryList = {}; + + if (this.errorCallback) { + try { + this.errorCallback(reason); + } catch(e) { + console.log("Transaction error calling user error callback: " + e); + } + } +}; + +/** + * Execute SQL statement + * + * @param sql SQL statement to execute + * @param params Statement parameters + * @param successCallback Success callback + * @param errorCallback Error callback + */ +DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { + + // Init params array + if (typeof params === 'undefined') { + params = []; + } + + // Create query and add to queue + var query = new DroidDB_Query(this); + queryQueue[query.id] = query; + + // Save callbacks + query.successCallback = successCallback; + query.errorCallback = errorCallback; + + // Call native code + exec(null, null, "Storage", "executeSql", [sql, params, query.id]); +}; + +var DatabaseShell = function() { +}; + +/** + * Start a transaction. + * Does not support rollback in event of failure. + * + * @param process {Function} The transaction function + * @param successCallback {Function} + * @param errorCallback {Function} + */ +DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) { + var tx = new DroidDB_Tx(); + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + try { + process(tx); + } catch (e) { + console.log("Transaction error: "+e); + if (tx.errorCallback) { + try { + tx.errorCallback(e); + } catch (ex) { + console.log("Transaction error calling user error callback: "+e); + } + } + } +}; + +/** + * Open database + * + * @param name Database name + * @param version Database version + * @param display_name Database display name + * @param size Database size in bytes + * @return Database object + */ +var DroidDB_openDatabase = function(name, version, display_name, size) { + exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]); + var db = new DatabaseShell(); + return db; +}; + +/** + * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api. + * TODO: Do similar for sessionStorage. + * @constructor + */ +var CupcakeLocalStorage = function() { + channel.waitForInitialization("cupcakeStorage"); + + try { + + this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440); + var storage = {}; + this.length = 0; + function setLength (length) { + this.length = length; + localStorage.length = length; + } + this.db.transaction( + function (transaction) { + var i; + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('SELECT * FROM storage', [], function(tx, result) { + for(var i = 0; i < result.rows.length; i++) { + storage[result.rows.item(i).id] = result.rows.item(i).body; + } + setLength(result.rows.length); + channel.initializationComplete("cupcakeStorage"); + }); + + }, + function (err) { + utils.alert(err.message); + } + ); + this.setItem = function(key, val) { + if (typeof(storage[key])=='undefined') { + this.length++; + } + storage[key] = val; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]); + } + ); + }; + this.getItem = function(key) { + return storage[key]; + }; + this.removeItem = function(key) { + delete storage[key]; + this.length--; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage where id=?', [key]); + } + ); + }; + this.clear = function() { + storage = {}; + this.length = 0; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage', []); + } + ); + }; + this.key = function(index) { + var i = 0; + for (var j in storage) { + if (i==index) { + return j; + } else { + i++; + } + } + return null; + }; + + } catch(e) { + utils.alert("Database error "+e+"."); + return; + } +}; + +module.exports = { + openDatabase:DroidDB_openDatabase, + CupcakeLocalStorage:CupcakeLocalStorage, + failQuery:failQuery, + completeQuery:completeQuery +}; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object whose properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/echo.js +define("cordova/plugin/echo", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback. + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + * @param message The string to be echoed. + * @param forceAsync Whether to force an async return value (for testing native->js bridge). + */ +module.exports = function(successCallback, errorCallback, message, forceAsync) { + var action = forceAsync ? 'echoAsync' : 'echo'; + exec(successCallback, errorCallback, "Echo", action, [message]); +}; + + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + +
+ + + \ No newline at end of file diff --git a/Android/ForegroundCamera/strings.xml b/Android/ForegroundCamera/strings.xml new file mode 100644 index 0000000..f5d85c5 --- /dev/null +++ b/Android/ForegroundCamera/strings.xml @@ -0,0 +1,22 @@ + + + + Hello Foreground Camera Plugin! + Foreground Camera Plugin + Capture + Cancel + diff --git a/Android/ForegroundGallery/CameraActivity.java b/Android/ForegroundGallery/CameraActivity.java new file mode 100644 index 0000000..27b9923 --- /dev/null +++ b/Android/ForegroundGallery/CameraActivity.java @@ -0,0 +1,136 @@ +/* + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. + */ + +package com.foregroundcameraplugin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import android.app.Activity; +import android.hardware.Camera; +import android.hardware.Camera.AutoFocusCallback; +import android.hardware.Camera.PictureCallback; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; + +/** + * Camera Activity Class. Configures Android camera to take picture and show it. + */ +public class CameraActivity extends Activity { + + private static final String TAG = "CameraActivity"; + + private Camera mCamera; + private ForegroundCameraPreview mPreview; + private boolean pressed = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.foregroundcameraplugin); + + // Create an instance of Camera + mCamera = getCameraInstance(); + + // Create a Preview and set it as the content of activity. + mPreview = new ForegroundCameraPreview(this, mCamera); + FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); + preview.addView(mPreview); + + // Add a listener to the Capture button + Button captureButton = (Button) findViewById(R.id.button_capture); + captureButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + + if (pressed) + return; + + // Set pressed = true to prevent freezing. + // Issue 1 at + // http://code.google.com/p/foreground-camera-plugin/issues/detail?id=1 + pressed = true; + + // get an image from the camera + mCamera.autoFocus(new AutoFocusCallback() { + + public void onAutoFocus(boolean success, Camera camera) { + mCamera.takePicture(null, null, mPicture); + } + }); + } + }); + + Button cancelButton = (Button) findViewById(R.id.button_cancel); + cancelButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + pressed = false; + setResult(RESULT_CANCELED); + finish(); + } + }); + } + + @Override + protected void onPause() { + if (mCamera != null) { + mCamera.release(); // release the camera for other applications + mCamera = null; + } + super.onPause(); + } + + /** A safe way to get an instance of the Camera object. */ + public static Camera getCameraInstance() { + Camera c = null; + try { + c = Camera.open(); // attempt to get a Camera instance + } catch (Exception e) { + // Camera is not available (in use or does not exist) + } + return c; // returns null if camera is unavailable + } + + private PictureCallback mPicture = new PictureCallback() { + + public void onPictureTaken(byte[] data, Camera camera) { + + Uri fileUri = (Uri) getIntent().getExtras().get( + MediaStore.EXTRA_OUTPUT); + + File pictureFile = new File(fileUri.getPath()); + + try { + FileOutputStream fos = new FileOutputStream(pictureFile); + fos.write(data); + fos.close(); + } catch (FileNotFoundException e) { + Log.d(TAG, "File not found: " + e.getMessage()); + } catch (IOException e) { + Log.d(TAG, "Error accessing file: " + e.getMessage()); + } + setResult(RESULT_OK); + pressed = false; + finish(); + } + }; +} \ No newline at end of file diff --git a/Android/ForegroundGallery/ForegroundCameraLauncher.java b/Android/ForegroundGallery/ForegroundCameraLauncher.java new file mode 100644 index 0000000..62b3a4d --- /dev/null +++ b/Android/ForegroundGallery/ForegroundCameraLauncher.java @@ -0,0 +1,380 @@ +/* + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vin�cius Fonseca. + + 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. + */ + +package com.foregroundcameraplugin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.cordova.CameraLauncher; +import org.apache.cordova.ExifHelper; +import org.apache.cordova.api.CordovaInterface; +import org.apache.cordova.api.LOG; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; + +/** + * This class launches the camera view, allows the user to take a picture, + * closes the camera view, and returns the captured image. When the camera view + * is closed, the screen displayed before the camera view was shown is + * redisplayed. + */ +public class ForegroundCameraLauncher extends CameraLauncher { + + private static final String LOG_TAG = "ForegroundCameraLauncher"; + + private int mQuality; + private int targetWidth; + private int targetHeight; + + private Uri imageUri; + private File photo; + + public String callbackId; + private int numPics; + + private static final String _DATA = "_data"; + + /** + * Constructor. + */ + public ForegroundCameraLauncher() { + } + + /** + * Executes the request and returns PluginResult. + * + * @param action + * The action to execute. + * @param args + * JSONArry of arguments for the plugin. + * @param callbackId + * The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + this.callbackId = callbackId; + + try { + if (action.equals("takePicture")) { + this.targetHeight = 0; + this.targetWidth = 0; + this.mQuality = 80; + + JSONObject options = args.optJSONObject(0); + if (options != null) { + this.targetHeight = options.getInt("targetHeight"); + this.targetWidth = options.getInt("targetWidth"); + this.mQuality = options.getInt("quality"); + } + + this.takePicture(); + + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + return r; + } + return new PluginResult(status, result); + } catch (JSONException e) { + e.printStackTrace(); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + // -------------------------------------------------------------------------- + // LOCAL METHODS + // -------------------------------------------------------------------------- + + /** + * Take a picture with the camera. When an image is captured or the camera + * view is cancelled, the result is returned in + * CordovaActivity.onActivityResult, which forwards the result to + * this.onActivityResult. + * + * The image can either be returned as a base64 string or a URI that points + * to the file. To display base64 string in an img tag, set the source to: + * img.src="data:image/jpeg;base64,"+result; or to display URI in an img tag + * img.src=result; + * + */ + public void takePicture() { + // Save the number of images currently on disk for later + this.numPics = queryImgDB().getCount(); + + Intent intent = new Intent(this.cordova.getActivity().getApplicationContext(), CameraActivity.class); + this.photo = createCaptureFile(); + this.imageUri = Uri.fromFile(photo); + intent.putExtra(MediaStore.EXTRA_OUTPUT, this.imageUri); + + this.cordova.startActivityForResult((Plugin) this, intent, 1); + } + + /** + * Create a file in the applications temporary directory based upon the + * supplied encoding. + * + * @return a File object pointing to the temporary picture + */ + private File createCaptureFile() { + File photo = new File(getTempDirectoryPath(this.cordova.getActivity().getApplicationContext()), "Pic.jpg"); + return photo; + } + + /** + * Called when the camera view exits. + * + * @param requestCode + * The request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode + * The integer result code returned by the child activity through + * its setResult(). + * @param intent + * An Intent, which can return result data to the caller (various + * data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + + // If image available + if (resultCode == Activity.RESULT_OK) { + try { + // Create an ExifHelper to save the exif data that is lost + // during compression + ExifHelper exif = new ExifHelper(); + exif.createInFile(getTempDirectoryPath(this.cordova.getActivity().getApplicationContext()) + + "/Pic.jpg"); + exif.readExifData(); + + // Read in bitmap of captured image + Bitmap bitmap; + try { + bitmap = android.provider.MediaStore.Images.Media + .getBitmap(this.cordova.getActivity().getContentResolver(), imageUri); + } catch (FileNotFoundException e) { + Uri uri = intent.getData(); + android.content.ContentResolver resolver = this.cordova.getActivity().getContentResolver(); + bitmap = android.graphics.BitmapFactory + .decodeStream(resolver.openInputStream(uri)); + } + + bitmap = scaleBitmap(bitmap); + + // Create entry in media store for image + // (Don't use insertImage() because it uses default compression + // setting of 50 - no way to change it) + ContentValues values = new ContentValues(); + values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, + "image/jpeg"); + Uri uri = null; + try { + uri = this.cordova.getActivity().getContentResolver() + .insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + values); + } catch (UnsupportedOperationException e) { + LOG.d(LOG_TAG, "Can't write to external media storage."); + try { + uri = this.cordova.getActivity().getContentResolver() + .insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, + values); + } catch (UnsupportedOperationException ex) { + LOG.d(LOG_TAG, "Can't write to internal media storage."); + this.failPicture("Error capturing image - no media storage found."); + return; + } + } + + // Add compressed version of captured image to returned media + // store Uri + OutputStream os = this.cordova.getActivity().getContentResolver() + .openOutputStream(uri); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + exif.createOutFile(getRealPathFromURI(uri, this.ctx)); + exif.writeExifData(); + + // Send Uri back to JavaScript for viewing image + this.success(new PluginResult(PluginResult.Status.OK, + getRealPathFromURI(uri, this.ctx)), this.callbackId); + + bitmap.recycle(); + bitmap = null; + System.gc(); + + checkForDuplicateImage(); + } catch (IOException e) { + e.printStackTrace(); + this.failPicture("Error capturing image."); + } + } + + // If cancelled + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Camera cancelled."); + } + + // If something else + else { + this.failPicture("Did not complete!"); + } + } + + /** + * Scales the bitmap according to the requested size. + * + * @param bitmap + * The bitmap to scale. + * @return Bitmap A new Bitmap object of the same bitmap after scaling. + */ + public Bitmap scaleBitmap(Bitmap bitmap) { + int newWidth = this.targetWidth; + int newHeight = this.targetHeight; + int origWidth = bitmap.getWidth(); + int origHeight = bitmap.getHeight(); + + // If no new width or height were specified return the original bitmap + if (newWidth <= 0 && newHeight <= 0) { + return bitmap; + } + // Only the width was specified + else if (newWidth > 0 && newHeight <= 0) { + newHeight = (newWidth * origHeight) / origWidth; + } + // only the height was specified + else if (newWidth <= 0 && newHeight > 0) { + newWidth = (newHeight * origWidth) / origHeight; + } + // If the user specified both a positive width and height + // (potentially different aspect ratio) then the width or height is + // scaled so that the image fits while maintaining aspect ratio. + // Alternatively, the specified width and height could have been + // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this + // would result in whitespace in the new image. + else { + double newRatio = newWidth / (double) newHeight; + double origRatio = origWidth / (double) origHeight; + + if (origRatio > newRatio) { + newHeight = (newWidth * origHeight) / origWidth; + } else if (origRatio < newRatio) { + newWidth = (newHeight * origWidth) / origHeight; + } + } + + return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); + } + + /** + * Creates a cursor that can be used to determine how many images we have. + * + * @return a cursor + */ + private Cursor queryImgDB() { + return this.cordova.getActivity().getContentResolver().query( + android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + new String[] { MediaStore.Images.Media._ID }, null, null, null); + } + + /** + * Used to find out if we are in a situation where the Camera Intent adds to + * images to the content store. If we are using a FILE_URI and the number of + * images in the DB increases by 2 we have a duplicate, when using a + * DATA_URL the number is 1. + */ + private void checkForDuplicateImage() { + int diff = 2; + Cursor cursor = queryImgDB(); + int currentNumOfImages = cursor.getCount(); + + // delete the duplicate file if the difference is 2 for file URI or 1 + // for Data URL + if ((currentNumOfImages - numPics) == diff) { + cursor.moveToLast(); + int id = Integer.valueOf(cursor.getString(cursor + .getColumnIndex(MediaStore.Images.Media._ID))) - 1; + Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + + "/" + id); + this.cordova.getActivity().getContentResolver().delete(uri, null, null); + } + } + + /** + * Determine if we can use the SD Card to store the temporary file. If not + * then use the internal cache directory. + * + * @return the absolute path of where to store the file + */ + private String getTempDirectoryPath(Context ctx) { + File cache = null; + + // SD Card Mounted + if (Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED)) { + cache = new File(Environment.getExternalStorageDirectory() + .getAbsolutePath() + + "/Android/data/" + + ctx.getPackageName() + "/cache/"); + } + // Use internal storage + else { + cache = ctx.getCacheDir(); + } + + // Create the cache directory if it doesn't exist + if (!cache.exists()) { + cache.mkdirs(); + } + + return cache.getAbsolutePath(); + } + + /** + * Queries the media store to find out what the file path is for the Uri we + * supply + * + * @param contentUri + * the Uri of the audio/image/video + * @param ctx + * the current applicaiton context + * @return the full path to the file + */ + private String getRealPathFromURI(Uri contentUri, CordovaInterface ctx) { + String[] proj = { _DATA }; + Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null); + int column_index = cursor.getColumnIndexOrThrow(_DATA); + cursor.moveToFirst(); + return cursor.getString(column_index); + } +} diff --git a/Android/ForegroundGallery/ForegroundCameraPreview.java b/Android/ForegroundGallery/ForegroundCameraPreview.java new file mode 100644 index 0000000..994b946 --- /dev/null +++ b/Android/ForegroundGallery/ForegroundCameraPreview.java @@ -0,0 +1,73 @@ +/* + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. +*/ + +package com.foregroundcameraplugin; + +import java.io.IOException; + +import android.content.Context; +import android.hardware.Camera; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class ForegroundCameraPreview extends SurfaceView implements SurfaceHolder.Callback { + private SurfaceHolder mHolder; + private Camera mCamera; + private static final String TAG = "CameraPreview"; + + public ForegroundCameraPreview(Context context, Camera camera) { + super(context); + mCamera = camera; + + mHolder = getHolder(); + mHolder.addCallback(this); + mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + } + + public void surfaceCreated(SurfaceHolder holder) { + try { + mCamera.setPreviewDisplay(holder); + mCamera.startPreview(); + } catch (IOException e) { + Log.d(TAG, "Error setting camera preview: " + e.getMessage()); + } + } + + public void surfaceDestroyed(SurfaceHolder holder) { + } + + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + + if (mHolder.getSurface() == null){ + return; + } + + try { + mCamera.stopPreview(); + } catch (Exception e){ + } + + try { + mCamera.setPreviewDisplay(mHolder); + mCamera.startPreview(); + + } catch (Exception e){ + Log.d(TAG, "Error starting camera preview: " + e.getMessage()); + } + } +} diff --git a/Android/ForegroundGallery/LICENSE b/Android/ForegroundGallery/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/Android/ForegroundGallery/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Android/ForegroundGallery/NOTICE b/Android/ForegroundGallery/NOTICE new file mode 100644 index 0000000..999a0b1 --- /dev/null +++ b/Android/ForegroundGallery/NOTICE @@ -0,0 +1,9 @@ +Foreground Camera Plugin for Phonegap (Cordova) +Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org) + +- src/com/foregroundcameraplugin/ForegroundCameraLauncher.java was based on CameraLauncher.java, found in Phonegap (Apache Cordova) version 1.8.0. +- assets/www/js/camera.js was based on cordova-1.8.0.js, found in Phonegap (Apache Cordova) version 1.8.0. +- res/xml/plugins.xml was adapted from plugins.xml, found in Phonegap (Apache Cordova) version 1.8.0. \ No newline at end of file diff --git a/Android/ForegroundGallery/README.txt b/Android/ForegroundGallery/README.txt new file mode 100644 index 0000000..72e0668 --- /dev/null +++ b/Android/ForegroundGallery/README.txt @@ -0,0 +1,43 @@ + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. + +Foreground Camera Plugin for Phonegap (Cordova). + +Originally by: - Bruno Carreira + - Lucas Farias + - Rafael Luna + - Vinicius Fonseca + +The default Phonegap (Cordova) Camera Plugin calls the native camera and this makes Android Garbage Collector to kill background applications. This plugin avoid your application to go background and be killed by Garbage Collector with other applications. We used the Phonegap source code and modified it to avoid this problem. This plugin works only with File URI. + +Adding the plugin to your project + + 1) To install the plugin, move the file camera.js to your project's www folder and include a reference to it in your html files. + 2) Put the Java files in your src/ folder. + 3) Change the default Camera Plugin into res/xml/plugins.xml file to . + 4) Put the strings.xml in your res/values folder. + 5) Put the foregroundcameraplugin.xml in your res/layout folder. + 6) In you AndroidManifest.xml, put this permissions: + + + And declare the Camera Activity: + + + +Using the plugin + + See the index.xhtml. \ No newline at end of file diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/.classpath b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/.classpath new file mode 100644 index 0000000..e088a5d --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/.project b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/.project new file mode 100644 index 0000000..ac1c057 --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/.project @@ -0,0 +1,33 @@ + + + ForegroundCameraPlugin + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/AndroidManifest.xml b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/AndroidManifest.xml new file mode 100644 index 0000000..e5a4ead --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/AndroidManifest.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/LICENSE b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/NOTICE b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/NOTICE new file mode 100644 index 0000000..999a0b1 --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/NOTICE @@ -0,0 +1,9 @@ +Foreground Camera Plugin for Phonegap (Cordova) +Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org) + +- src/com/foregroundcameraplugin/ForegroundCameraLauncher.java was based on CameraLauncher.java, found in Phonegap (Apache Cordova) version 1.8.0. +- assets/www/js/camera.js was based on cordova-1.8.0.js, found in Phonegap (Apache Cordova) version 1.8.0. +- res/xml/plugins.xml was adapted from plugins.xml, found in Phonegap (Apache Cordova) version 1.8.0. \ No newline at end of file diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/README.txt b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/README.txt new file mode 100644 index 0000000..00b8b68 --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/README.txt @@ -0,0 +1,49 @@ + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vinícius Fonseca. + + 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. + +Foreground Camera Plugin for Phonegap (Cordova). + +Originally by: - Bruno Carreira + - Lucas Farias + - Rafael Luna + - Vinicius Fonseca + +The default Phonegap (Cordova) Camera Plugin calls the native camera and this makes Android Garbage Collector to kill background applications. This plugin avoid your application to go background and be killed by Garbage Collector with other applications. We used the Phonegap source code and modified it to avoid this problem. This plugin works only with File URI. + +Adding the plugin to your project + + 1) To install the plugin, move the file camera.js to your project's www folder and include a reference to it in your html files. + 2) Put the Java files in your src/ folder. + 3) Change the default Camera Plugin into res/xml/config.xml file to . + 4) Put the strings.xml in your res/values folder. + 5) Put the foregroundcameraplugin.xml in your res/layout folder. + 6) In you AndroidManifest.xml, put this permissions: + + + + + + + + + And declare the Camera Activity: + + + +Using the plugin + + See the index.xhtml. \ No newline at end of file diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/index.html b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/index.html new file mode 100644 index 0000000..672d577 --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/index.html @@ -0,0 +1,83 @@ + + + + + Capture Photo + + + + + + +
+ + + \ No newline at end of file diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/js/camera.js b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/js/camera.js new file mode 100644 index 0000000..1c246f4 --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/js/camera.js @@ -0,0 +1,91 @@ +/** + Copyright 2012 Bruno Carreira - Lucas Farias - Rafael Luna - Vin�cius Fonseca. + + 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. +*/ + +/** + * This class provides access to the device camera. + * + * @constructor + */ +var Camera = function() { + this.successCallback = null; + this.errorCallback = null; + this.options = null; +}; + +/** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=DATA_URL. + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ +Camera.prototype.getPicture = function(successCallback, errorCallback, + options) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Camera Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Camera Error: errorCallback is not a function"); + return; + } + + if (options === null || typeof options === "undefined") { + options = {}; + } + if (options.quality === null || typeof options.quality === "undefined") { + options.quality = 80; + } + if (options.maxResolution === null + || typeof options.maxResolution === "undefined") { + options.maxResolution = 0; + } + + if (options.targetWidth === null + || typeof options.targetWidth === "undefined") { + options.targetWidth = -1; + } else if (typeof options.targetWidth === "string") { + var width = new Number(options.targetWidth); + if (isNaN(width) === false) { + options.targetWidth = width.valueOf(); + } + } + if (options.targetHeight === null + || typeof options.targetHeight === "undefined") { + options.targetHeight = -1; + } else if (typeof options.targetHeight === "string") { + var height = new Number(options.targetHeight); + if (isNaN(height) === false) { + options.targetHeight = height.valueOf(); + } + } + + cordova.exec(successCallback, errorCallback, "Camera", "takePicture", + [ options ]); +}; + +cordova.addConstructor(function() { + if (typeof navigator.camera === "undefined") { + navigator.camera = new Camera(); + } +}); \ No newline at end of file diff --git a/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/js/cordova-2.1.0.js b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/js/cordova-2.1.0.js new file mode 100644 index 0000000..9e7fa8e --- /dev/null +++ b/Android/ForegroundGallery/Sample/ForegroundCameraPlugin/assets/www/js/cordova-2.1.0.js @@ -0,0 +1,5814 @@ +// commit 143f5221a6251c9cbccdedc57005c61551b97f12 + +// File generated at :: Wed Sep 12 2012 12:51:58 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code + */ + fireDocumentEvent: function(type, data, bNoDetach) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + if( bNoDetach ) { + documentEventHandlers[type].fire(evt); + } + else { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'), + nextGuid = 1; + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + var headers = null; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + headers = options.headers; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + * @param headers {Object} Keys are header names, values are header values. Multiple + * headers of the same name are not supported. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; + this.headers = headers || null; +}; + +module.exports = FileUploadOptions; + +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger than file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(null, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param msgType The 'type' of update this is + * @param value Use of value is determined by the msgType + */ +Media.onStatus = function(id, msgType, value) { + + var media = mediaObjects[id]; + + if(media) { + switch(msgType) { + case Media.MEDIA_STATE : + media.statusCallback && media.statusCallback(value); + if(value == Media.MEDIA_STOPPED) { + media.successCallback && media.successCallback(); + } + break; + case Media.MEDIA_DURATION : + media._duration = value; + break; + case Media.MEDIA_ERROR : + media.errorCallback && media.errorCallback(value); + break; + case Media.MEDIA_POSITION : + media._position = Number(value); + break; + default : + console && console.error && console.error("Unhandled Media.onStatus :: " + msgType); + break; + } + } + else { + console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id); + } + +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. +*/ +/* + According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror + We should never be creating these objects, we should just implement the interface + which has 1 property for an instance, 'code' + + instead of doing : + errorCallbackFunction( new MediaError(3,'msg') ); +we should simply use a literal : + errorCallbackFunction( {'code':3} ); + */ + +if(!MediaError) { + var MediaError = function(code, msg) { + this.code = (typeof code != 'undefined') ? code : null; + this.message = msg || ""; // message is NON-standard! do not use! + }; +} + +MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0; +MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1; +MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2; +MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4; +// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below. +// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes +MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4; + +module.exports = MediaError; + +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +module.exports = MediaFile; + +}); + +// file: lib/common/plugin/MediaFileData.js +define("cordova/plugin/MediaFileData", function(require, exports, module) { +/** + * MediaFileData encapsulates format information of a media file. + * + * @param {DOMString} codecs + * @param {long} bitrate + * @param {long} height + * @param {long} width + * @param {float} duration + */ +var MediaFileData = function(codecs, bitrate, height, width, duration){ + this.codecs = codecs || null; + this.bitrate = bitrate || 0; + this.height = height || 0; + this.width = width || 0; + this.duration = duration || 0; +}; + +module.exports = MediaFileData; +}); + +// file: lib/common/plugin/Metadata.js +define("cordova/plugin/Metadata", function(require, exports, module) { +/** + * Information about the state of the file or directory + * + * {Date} modificationTime (readonly) + */ +var Metadata = function(time) { + this.modificationTime = (typeof time != 'undefined'?new Date(time):null); +}; + +module.exports = Metadata; +}); + +// file: lib/common/plugin/Position.js +define("cordova/plugin/Position", function(require, exports, module) { +var Coordinates = require('cordova/plugin/Coordinates'); + +var Position = function(coords, timestamp) { + if (coords) { + this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy); + } else { + this.coords = new Coordinates(); + } + this.timestamp = (timestamp !== undefined) ? timestamp : new Date(); +}; + +module.exports = Position; + +}); + +// file: lib/common/plugin/PositionError.js +define("cordova/plugin/PositionError", function(require, exports, module) { +/** + * Position error object + * + * @constructor + * @param code + * @param message + */ +var PositionError = function(code, message) { + this.code = code || null; + this.message = message || ''; +}; + +PositionError.PERMISSION_DENIED = 1; +PositionError.POSITION_UNAVAILABLE = 2; +PositionError.TIMEOUT = 3; + +module.exports = PositionError; +}); + +// file: lib/common/plugin/ProgressEvent.js +define("cordova/plugin/ProgressEvent", function(require, exports, module) { +// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill +// Feature test: See if we can instantiate a native ProgressEvent; +// if so, use that approach, +// otherwise fill-in with our own implementation. +// +// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview. +var ProgressEvent = (function() { + /* + var createEvent = function(data) { + var event = document.createEvent('Events'); + event.initEvent('ProgressEvent', false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + if (data.target) { + // TODO: cannot call .dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retrieved a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/android/plugin/android/app.js +define("cordova/plugin/android/app", function(require, exports, module) { +var exec = require('cordova/exec'); + +module.exports = { + /** + * Clear the resource cache. + */ + clearCache:function() { + exec(null, null, "App", "clearCache", []); + }, + + /** + * Load the url into the webview or into new browser instance. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * clearHistory: boolean => clear webview history (default=false) + * openExternal: boolean => open in a new browser (default=false) + * + * Example: + * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ + loadUrl:function(url, props) { + exec(null, null, "App", "loadUrl", [url, props]); + }, + + /** + * Cancel loadUrl that is waiting to be loaded. + */ + cancelLoadUrl:function() { + exec(null, null, "App", "cancelLoadUrl", []); + }, + + /** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ + clearHistory:function() { + exec(null, null, "App", "clearHistory", []); + }, + + /** + * Go to previous page displayed. + * This is the same as pressing the backbutton on Android device. + */ + backHistory:function() { + exec(null, null, "App", "backHistory", []); + }, + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ + overrideBackbutton:function(override) { + exec(null, null, "App", "overrideBackbutton", [override]); + }, + + /** + * Exit and terminate the application. + */ + exitApp:function() { + return exec(null, null, "App", "exitApp", []); + } +}; +}); + +// file: lib/android/plugin/android/callback.js +define("cordova/plugin/android/callback", function(require, exports, module) { +var port = null, + token = null, + xmlhttp; + +function startXhr() { + // cordova/exec depends on this module, so we can't require cordova/exec on the module level. + var exec = require('cordova/exec'), + xmlhttp = new XMLHttpRequest(); + + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if (!xmlhttp) { + return; + } + if (xmlhttp.readyState === 4){ + // If callback has JavaScript statement to execute + if (xmlhttp.status === 200) { + + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); + setTimeout(function() { + try { + var t = eval(msg); + } + catch (e) { + // If we're getting an error here, seeing the message will help in debugging + console.log("JSCallback: Message from Server: " + msg); + console.log("JSCallback Error: "+e); + } + }, 1); + setTimeout(startXhr, 1); + } + + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(startXhr, 10); + } + + // 0 == Page is unloading. + // 400 == Bad request. + // 403 == invalid token. + // 503 == server stopped. + else { + console.log("JSCallback Error: Request failed with status " + xmlhttp.status); + exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING); + } + } + }; + + if (port === null) { + port = prompt("getPort", "gap_callbackServer:"); + } + if (token === null) { + token = prompt("getToken", "gap_callbackServer:"); + } + xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true); + xmlhttp.send(); +} + +module.exports = { + start: function() { + startXhr(); + }, + + stop: function() { + if (xmlhttp) { + var tmp = xmlhttp; + xmlhttp = null; + tmp.abort(); + } + }, + + isAvailable: function() { + return ("true" != prompt("usePolling", "gap_callbackServer:")); + } +}; + + +}); + +// file: lib/android/plugin/android/device.js +define("cordova/plugin/android/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + app = require('cordova/plugin/android/app'); + +module.exports = { + /* + * DEPRECATED + * This is only for Android. + * + * You must explicitly override the back button. + */ + overrideBackButton:function() { + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This resets the back button to the default behaviour + */ + resetBackButton:function() { + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This terminates the activity! + */ + exitApp:function() { + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); + } +}; + +}); + +// file: lib/android/plugin/android/notification.js +define("cordova/plugin/android/notification", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides Android enhanced notification API. + */ +module.exports = { + activityStart : function(title, message) { + // If title and message not specified then mimic Android behavior of + // using default strings. + if (typeof title === "undefined" && typeof message == "undefined") { + title = "Busy"; + message = 'Please wait...'; + } + + exec(null, null, 'Notification', 'activityStart', [ title, message ]); + }, + + /** + * Close an activity dialog + */ + activityStop : function() { + exec(null, null, 'Notification', 'activityStop', []); + }, + + /** + * Display a progress dialog with progress bar that goes from 0 to 100. + * + * @param {String} + * title Title of the progress dialog. + * @param {String} + * message Message to display in the dialog. + */ + progressStart : function(title, message) { + exec(null, null, 'Notification', 'progressStart', [ title, message ]); + }, + + /** + * Close the progress dialog. + */ + progressStop : function() { + exec(null, null, 'Notification', 'progressStop', []); + }, + + /** + * Set the progress dialog value. + * + * @param {Number} + * value 0-100 + */ + progressValue : function(value) { + exec(null, null, 'Notification', 'progressValue', [ value ]); + } +}; +}); + +// file: lib/android/plugin/android/polling.js +define("cordova/plugin/android/polling", function(require, exports, module) { +var cordova = require('cordova'), + POLL_INTERVAL = 50, + enabled = false; + +function pollOnce() { + var msg = prompt("", "gap_poll:"); + if (msg) { + try { + eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + return true; + } + return false; +} + +function doPoll() { + if (!enabled) { + return; + } + var nextDelay = POLL_INTERVAL; + if (pollOnce()) { + nextDelay = 0; + } + setTimeout(doPoll, nextDelay); +} + +module.exports = { + start: function() { + enabled = true; + setTimeout(doPoll, 1); + }, + stop: function() { + enabled = false; + }, + pollOnce: pollOnce +}; + + +}); + +// file: lib/android/plugin/android/storage.js +define("cordova/plugin/android/storage", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + channel = require('cordova/channel'); + +var queryQueue = {}; + +/** + * SQL result set object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Rows = function() { + this.resultSet = []; // results array + this.length = 0; // number of rows +}; + +/** + * Get item from SQL result set + * + * @param row The row number to return + * @return The row object + */ +DroidDB_Rows.prototype.item = function(row) { + return this.resultSet[row]; +}; + +/** + * SQL result set that is returned to user. + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Result = function() { + this.rows = new DroidDB_Rows(); +}; + +/** + * Callback from native code when query is complete. + * PRIVATE METHOD + * + * @param id Query id + */ +function completeQuery(id, data) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + + // Save query results + var r = new DroidDB_Result(); + r.rows.resultSet = data; + r.rows.length = data.length; + try { + if (typeof query.successCallback === 'function') { + query.successCallback(query.tx, r); + } + } catch (ex) { + console.log("executeSql error calling user success callback: "+ex); + } + + tx.queryComplete(id); + } + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * Callback from native code when query fails + * PRIVATE METHOD + * + * @param reason Error message + * @param id Query id + */ +function failQuery(reason, id) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + tx.queryList = {}; + + try { + if (typeof query.errorCallback === 'function') { + query.errorCallback(query.tx, reason); + } + } catch (ex) { + console.log("executeSql error calling user error callback: "+ex); + } + + tx.queryFailed(id, reason); + } + + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * SQL query object + * PRIVATE METHOD + * + * @constructor + * @param tx The transaction object that this query belongs to + */ +var DroidDB_Query = function(tx) { + + // Set the id of the query + this.id = utils.createUUID(); + + // Add this query to the queue + queryQueue[this.id] = this; + + // Init result + this.resultSet = []; + + // Set transaction that this query belongs to + this.tx = tx; + + // Add this query to transaction list + this.tx.queryList[this.id] = this; + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + +}; + +/** + * Transaction object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Tx = function() { + + // Set the id of the transaction + this.id = utils.createUUID(); + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + + // Query list + this.queryList = {}; +}; + +/** + * Mark query in transaction as complete. + * If all queries are complete, call the user's transaction success callback. + * + * @param id Query id + */ +DroidDB_Tx.prototype.queryComplete = function(id) { + delete this.queryList[id]; + + // If no more outstanding queries, then fire transaction success + if (this.successCallback) { + var count = 0; + var i; + for (i in this.queryList) { + if (this.queryList.hasOwnProperty(i)) { + count++; + } + } + if (count === 0) { + try { + this.successCallback(); + } catch(e) { + console.log("Transaction error calling user success callback: " + e); + } + } + } +}; + +/** + * Mark query in transaction as failed. + * + * @param id Query id + * @param reason Error message + */ +DroidDB_Tx.prototype.queryFailed = function(id, reason) { + + // The sql queries in this transaction have already been run, since + // we really don't have a real transaction implemented in native code. + // However, the user callbacks for the remaining sql queries in transaction + // will not be called. + this.queryList = {}; + + if (this.errorCallback) { + try { + this.errorCallback(reason); + } catch(e) { + console.log("Transaction error calling user error callback: " + e); + } + } +}; + +/** + * Execute SQL statement + * + * @param sql SQL statement to execute + * @param params Statement parameters + * @param successCallback Success callback + * @param errorCallback Error callback + */ +DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { + + // Init params array + if (typeof params === 'undefined') { + params = []; + } + + // Create query and add to queue + var query = new DroidDB_Query(this); + queryQueue[query.id] = query; + + // Save callbacks + query.successCallback = successCallback; + query.errorCallback = errorCallback; + + // Call native code + exec(null, null, "Storage", "executeSql", [sql, params, query.id]); +}; + +var DatabaseShell = function() { +}; + +/** + * Start a transaction. + * Does not support rollback in event of failure. + * + * @param process {Function} The transaction function + * @param successCallback {Function} + * @param errorCallback {Function} + */ +DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) { + var tx = new DroidDB_Tx(); + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + try { + process(tx); + } catch (e) { + console.log("Transaction error: "+e); + if (tx.errorCallback) { + try { + tx.errorCallback(e); + } catch (ex) { + console.log("Transaction error calling user error callback: "+e); + } + } + } +}; + +/** + * Open database + * + * @param name Database name + * @param version Database version + * @param display_name Database display name + * @param size Database size in bytes + * @return Database object + */ +var DroidDB_openDatabase = function(name, version, display_name, size) { + exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]); + var db = new DatabaseShell(); + return db; +}; + +/** + * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api. + * TODO: Do similar for sessionStorage. + * @constructor + */ +var CupcakeLocalStorage = function() { + channel.waitForInitialization("cupcakeStorage"); + + try { + + this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440); + var storage = {}; + this.length = 0; + function setLength (length) { + this.length = length; + localStorage.length = length; + } + this.db.transaction( + function (transaction) { + var i; + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('SELECT * FROM storage', [], function(tx, result) { + for(var i = 0; i < result.rows.length; i++) { + storage[result.rows.item(i).id] = result.rows.item(i).body; + } + setLength(result.rows.length); + channel.initializationComplete("cupcakeStorage"); + }); + + }, + function (err) { + utils.alert(err.message); + } + ); + this.setItem = function(key, val) { + if (typeof(storage[key])=='undefined') { + this.length++; + } + storage[key] = val; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]); + } + ); + }; + this.getItem = function(key) { + return storage[key]; + }; + this.removeItem = function(key) { + delete storage[key]; + this.length--; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage where id=?', [key]); + } + ); + }; + this.clear = function() { + storage = {}; + this.length = 0; + this.db.transaction( + function (transaction) { + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('DELETE FROM storage', []); + } + ); + }; + this.key = function(index) { + var i = 0; + for (var j in storage) { + if (i==index) { + return j; + } else { + i++; + } + } + return null; + }; + + } catch(e) { + utils.alert("Database error "+e+"."); + return; + } +}; + +module.exports = { + openDatabase:DroidDB_openDatabase, + CupcakeLocalStorage:CupcakeLocalStorage, + failQuery:failQuery, + completeQuery:completeQuery +}; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object whose properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/echo.js +define("cordova/plugin/echo", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback. + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + * @param message The string to be echoed. + * @param forceAsync Whether to force an async return value (for testing native->js bridge). + */ +module.exports = function(successCallback, errorCallback, message, forceAsync) { + var action = forceAsync ? 'echoAsync' : 'echo'; + exec(successCallback, errorCallback, "Echo", action, [message]); +}; + + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + +
+ + + \ No newline at end of file diff --git a/Android/ForegroundGallery/strings.xml b/Android/ForegroundGallery/strings.xml new file mode 100644 index 0000000..f5d85c5 --- /dev/null +++ b/Android/ForegroundGallery/strings.xml @@ -0,0 +1,22 @@ + + + + Hello Foreground Camera Plugin! + Foreground Camera Plugin + Capture + Cancel + diff --git a/Android/Globalization/GlobalizationCommand.java b/Android/Globalization/GlobalizationCommand.java index 577c9de..58badef 100755 --- a/Android/Globalization/GlobalizationCommand.java +++ b/Android/Globalization/GlobalizationCommand.java @@ -1,27 +1,30 @@ /** * */ -package com.phonegap.plugins.globalization; +package org.apache.cordova.plugins.globalization; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.Currency; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.text.format.Time; -import org.apache.cordova.api.Plugin; -import org.apache.cordova.api.PluginResult; - -import java.text.DateFormat; -import java.text.DateFormatSymbols; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Currency; -import java.util.Date; -import java.util.TimeZone; -import java.util.Locale; - /** * */ @@ -36,7 +39,10 @@ public class GlobalizationCommand extends Plugin { if (action.equals(Resources.GETLOCALENAME)){ obj = getLocaleName(); return new PluginResult(status, obj); - }else if(action.equalsIgnoreCase(Resources.DATETOSTRING)){ + }else if (action.equals(Resources.GETPREFERREDLANGUAGE)){ + obj = getPreferredLanguage(); + return new PluginResult(status, obj); + } else if (action.equalsIgnoreCase(Resources.DATETOSTRING)) { obj = getDateToString(data); return new PluginResult(PluginResult.Status.OK, obj); }else if(action.equalsIgnoreCase(Resources.STRINGTODATE)){ @@ -91,6 +97,23 @@ public class GlobalizationCommand extends Plugin { throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR); } } + /* + * @Description: Returns the string identifier for the client's current language + * + * @Return: JSONObject + * Object.value {String}: The language identifier + * + * @throws: GlobalizationError.UNKNOWN_ERROR + */ + private JSONObject getPreferredLanguage() throws GlobalizationError { + JSONObject obj = new JSONObject(); + try { + obj.put("value", Locale.getDefault().getDisplayLanguage().toString()); + return obj; + } catch (Exception e) { + throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR); + } + } /* * @Description: Returns a date formatted as a string according to the client's user preferences and * calendar using the time zone of the client. @@ -240,10 +263,9 @@ public class GlobalizationCommand extends Plugin { JSONObject obj = new JSONObject(); //String[] value; JSONArray value = new JSONArray(); - String[] list; + List namesList = new ArrayList(); + final Map namesMap; // final needed for sorting with anonymous comparator try{ - SimpleDateFormat s = (SimpleDateFormat)android.text.format.DateFormat.getDateFormat(this.ctx.getContext()); - DateFormatSymbols ds = s.getDateFormatSymbols(); int type = 0; //default wide int item = 0; //default months @@ -262,14 +284,31 @@ public class GlobalizationCommand extends Plugin { } //determine return value int method = item + type; - if (method == 1){list = ds.getShortMonths();}//months and narrow - else if (method == 10){list = ds.getWeekdays();}//days and wide - else if (method == 11){list = ds.getShortWeekdays();}//days and narrow - else{list = ds.getMonths();}//default: months and wide + if (method == 1) { //months and narrow + namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()); + } else if (method == 10) { //days and wide + namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()); + } else if (method == 11) { //days and narrow + namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault()); + } else { //default: months and wide + namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault()); + } + + // save names as a list + for(String name : namesMap.keySet()) { + namesList.add(name); + } - //convert String[] into JSONArray of String objects - for (int i = 0; i < list.length; i ++){ - value.put(list[i]); + // sort the list according to values in namesMap + Collections.sort(namesList, new Comparator() { + public int compare(String arg0, String arg1) { + return namesMap.get(arg0).compareTo(namesMap.get(arg1)); + } + }); + + // convert nameList into JSONArray of String objects + for (int i = 0; i < namesList.size(); i ++){ + value.put(namesList.get(i)); } //return array of names diff --git a/Android/Globalization/GlobalizationError.java b/Android/Globalization/GlobalizationError.java index 332ece9..eff0d21 100755 --- a/Android/Globalization/GlobalizationError.java +++ b/Android/Globalization/GlobalizationError.java @@ -1,7 +1,7 @@ /** * User initiated exception */ -package com.phonegap.plugins.globalization; +package org.apache.cordova.plugins.globalization; /** * @description Exception class representing defined Globalization error codes diff --git a/Android/Globalization/Resources.java b/Android/Globalization/Resources.java index 9e9dad1..a993554 100755 --- a/Android/Globalization/Resources.java +++ b/Android/Globalization/Resources.java @@ -1,7 +1,7 @@ /** * */ -package com.phonegap.plugins.globalization; +package org.apache.cordova.plugins.globalization; /** * @author costanzo @@ -20,6 +20,7 @@ public class Resources { public static final String STRINGTONUMBER = "stringToNumber"; public static final String GETNUMBERPATTERN = "getNumberPattern"; public static final String GETCURRENCYPATTERN = "getCurrencyPattern"; + public static final String GETPREFERREDLANGUAGE = "getPreferredLanguage"; //GlobalizationCommand Option Parameters public static final String OPTIONS = "options"; diff --git a/Android/Globalization/globalization.js b/Android/Globalization/globalization.js index a87472d..6d49b60 100755 --- a/Android/Globalization/globalization.js +++ b/Android/Globalization/globalization.js @@ -1,7 +1,21 @@ - -function Globalization() -{ +var Globalization = function() { +}; +Globalization.prototype.getPreferredLanguage = function(successCB, failureCB) +{ + // successCallback required + if (typeof successCB != "function") { + console.log("Globalization.getPreferredLanguage Error: successCB is not a function"); + return; + } + + // errorCallback required + if (typeof failureCB != "function") { + console.log("Globalization.getPreferredLanguage Error: failureCB is not a function"); + return; + } + + cordova.exec(successCB, failureCB, "GlobalizationCommand","getPreferredLanguage", []); }; /** @@ -173,7 +187,7 @@ Globalization.prototype.stringToDate = function(dateString, successCB, failureCB * @error GlobalizationError.PATTERN_ERROR * * Example -* globalization.getDatePattern(new Date(), +* globalization.getDatePattern( * function (date) {alert('pattern:' + date.pattern + '\n');}, * function () {}, * {formatLength:'short'}); @@ -506,10 +520,6 @@ Globalization.prototype.getCurrencyPattern = function(currencyCode, successCB, f console.log("Globalization.getCurrencyPattern Error: currencyCode is not a currency code"); } }; -cordova.addConstructor(function() -{ - cordova.addPlugin('globalization', new Globalization()); -}); GlobalizationError = function() { this.code = null; @@ -520,3 +530,11 @@ GlobalizationError.UNKNOWN_ERROR = 0; GlobalizationError.FORMATTING_ERROR = 1; GlobalizationError.PARSING_ERROR = 2; GlobalizationError.PATTERN_ERROR = 3; + + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.globalization) { + window.plugins.globalization = new Globalization(); +} diff --git a/Android/HeadsetWatcher/HeadsetWatcher.java b/Android/HeadsetWatcher/HeadsetWatcher.java new file mode 100644 index 0000000..82974d0 --- /dev/null +++ b/Android/HeadsetWatcher/HeadsetWatcher.java @@ -0,0 +1,76 @@ +/** + * HeadsetWatcher plugin for Cordova/Phonegap + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) Triggertrap Ltd. 2012 + * + */ + + +package com.triggertrap; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.apache.cordova.api.PluginResult.Status; +import org.json.JSONArray; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; + +public class HeadsetWatcher extends Plugin { + + private String callback; + public HeadsetBroadcastReceiver headsetReceiver; + @Override + public PluginResult execute(String action, JSONArray data, String callbackId) { + this.callback = callbackId; + headsetReceiver = new HeadsetBroadcastReceiver(this); + PluginResult result = new PluginResult(Status.NO_RESULT); + this.cordova.getActivity().registerReceiver(headsetReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); + result.setKeepCallback(true); + return result; + } + + public void changed(int state) { + + JSONObject status = new JSONObject(); + try { + status.put("plugged", state == 1 ? true : false); + } catch (Exception ex) { + Log.e("Headset", "JSON error " + ex.toString()); + return; + } + PluginResult result = new PluginResult(PluginResult.Status.OK, status); + result.setKeepCallback(true); + this.success(result, this.callback); + } + + public class HeadsetBroadcastReceiver extends BroadcastReceiver + { + protected HeadsetWatcher watcher; + + public HeadsetBroadcastReceiver(HeadsetWatcher watcher) { + super(); + this.watcher = watcher; + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d("ACTION_HEADSET_PLUG Received", action); + if( (action.compareTo(Intent.ACTION_HEADSET_PLUG)) == 0) { + int headsetState = intent.getIntExtra("state", 0); + watcher.changed(headsetState); + } + + } + + } + + +} + diff --git a/Android/HeadsetWatcher/HeadsetWatcher.js b/Android/HeadsetWatcher/HeadsetWatcher.js new file mode 100644 index 0000000..c8d2f5d --- /dev/null +++ b/Android/HeadsetWatcher/HeadsetWatcher.js @@ -0,0 +1,19 @@ +/** + * HeadsetWatcher plugin for Cordova/Phonegap + * + * Copyright (c) Triggertrap Ltd. 2012. All Rights Reserved. + * Available under the terms of the MIT License. + * + */ +var HeadsetWatcher = { + watch: function(callback) { + return cordova.exec(function(result) { + HeadsetWatcher.plugged = result.plugged; + if(callback) { + callback(result); + } + + }, HeadsetWatcher.fail, "HeadsetWatcher", "watch", []); + }, + plugged: false +} \ No newline at end of file diff --git a/Android/HeadsetWatcher/README.md b/Android/HeadsetWatcher/README.md new file mode 100644 index 0000000..cff4fa1 --- /dev/null +++ b/Android/HeadsetWatcher/README.md @@ -0,0 +1,63 @@ +# HeadsetWatcher plugin for Cordova/Phonegap # +By Matt Kane / Triggertrap Ltd. + +This plugin allows you to watch for headphones being plugged or unplugged from a device. +It does this by registering for ACTION_HEADSET_PLUG notifications. + + +## Adding the Plugin to your project ## + +1. To install the plugin, move HeadsetWatcher.js to your project's www folder and include a reference to it in your html files. +2. Create a folder called 'com/triggertrap/' within your project's src folder. +3. And copy the HeadsetWatcher.java file into that new folder. +4. In your res/xml/plugins.xml file add the following line: + + `` + +## Using the plugin ## + +There is a single method to call: `HeadsetWatcher.watch(callback)`. +The callback is a function, which is passed an object with the single boolean +property `plugged`, which indicates whether the headset is currently plugged in. +It is a persistent callback, and will be called whenever the action occurs. +The callback will also be called immediately when first registered. + +The static property HeadsetWatcher.plugged is also set once watch has been called. + +```javascript + + HeadsetWatcher.watch(function(result) { + if(result.plugged) { + alert("The headphones have been plugged in!"); + } else { + alert("The headphones have been unplugged!"); + } + + }) + + +``` + +## Licence ## + +The MIT License + +Copyright © 2012 Triggertrap Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Android/IMEI/v2.0.0/IMEIPlugin.java b/Android/IMEI/v2.0.0/IMEIPlugin.java new file mode 100644 index 0000000..4741047 --- /dev/null +++ b/Android/IMEI/v2.0.0/IMEIPlugin.java @@ -0,0 +1,29 @@ +package com.simonmacdonald.imei; + + +import org.json.JSONArray; + +import android.content.Context; +import android.telephony.TelephonyManager; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; + +public class IMEIPlugin extends Plugin { + + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + if (action.equals("get")) { + TelephonyManager telephonyManager = (TelephonyManager)this.cordova.getActivity().getSystemService(Context.TELEPHONY_SERVICE); + result = telephonyManager.getDeviceId(); + } + else { + status = PluginResult.Status.INVALID_ACTION; + } + return new PluginResult(status, result); + } + +} diff --git a/Android/IMEI/v2.0.0/imei.js b/Android/IMEI/v2.0.0/imei.js new file mode 100644 index 0000000..19d1085 --- /dev/null +++ b/Android/IMEI/v2.0.0/imei.js @@ -0,0 +1,12 @@ +var IMEI = function(){}; + +IMEI.prototype.get = function(onSuccess, onFail){ + return cordova.exec(onSuccess, onFail, 'IMEI', 'get', []); +}; + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.imei) { + window.plugins.imei = new IMEI(); +} \ No newline at end of file diff --git a/Android/IMEI/v2.0.0/index.html b/Android/IMEI/v2.0.0/index.html new file mode 100644 index 0000000..0a14301 --- /dev/null +++ b/Android/IMEI/v2.0.0/index.html @@ -0,0 +1,31 @@ + + + + + PhoneGap Events Example + + + + + +

IMEI v1.5

+ IMEI:
+ + diff --git a/Android/LowLatencyAudio/src/PGLowLatencyAudio.js b/Android/LowLatencyAudio/src/PGLowLatencyAudio.js index e93513d..5ed4ac7 100644 --- a/Android/LowLatencyAudio/src/PGLowLatencyAudio.js +++ b/Android/LowLatencyAudio/src/PGLowLatencyAudio.js @@ -1,27 +1,27 @@ var PGLowLatencyAudio = { preloadFX: function ( id, assetPath, success, fail) { - return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "preloadFX", [id, assetPath]); + return cordova.exec(success, fail, "PGLowLatencyAudio", "preloadFX", [id, assetPath]); }, preloadAudio: function ( id, assetPath, voices, success, fail) { - return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "preloadAudio", [id, assetPath, voices]); + return cordova.exec(success, fail, "PGLowLatencyAudio", "preloadAudio", [id, assetPath, voices]); }, play: function (id, success, fail) { - return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "play", [id]); + return cordova.exec(success, fail, "PGLowLatencyAudio", "play", [id]); }, stop: function (id, success, fail) { - return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "stop", [id]); + return cordova.exec(success, fail, "PGLowLatencyAudio", "stop", [id]); }, loop: function (id, success, fail) { - return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "loop", [id]); + return cordova.exec(success, fail, "PGLowLatencyAudio", "loop", [id]); }, unload: function (id, success, fail) { - return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "unload", [id]); + return cordova.exec(success, fail, "PGLowLatencyAudio", "unload", [id]); } diff --git a/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java b/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java index 8afec40..492dfc3 100644 --- a/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java +++ b/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java @@ -22,9 +22,9 @@ import android.media.AudioManager; import android.media.SoundPool; import android.util.Log; -import com.phonegap.api.Plugin; -import com.phonegap.api.PluginResult; -import com.phonegap.api.PluginResult.Status; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.apache.cordova.api.PluginResult.Status; /** * @author triceam @@ -58,10 +58,12 @@ public class PGLowLatencyAudio extends Plugin { { PluginResult result = null; initSoundPool(); + try { String audioID = data.getString(0); + Log.d(audioID, action); if ( PRELOAD_FX.equals( action ) ) { @@ -70,7 +72,7 @@ public class PGLowLatencyAudio extends Plugin { String assetPath =data.getString(1); String fullPath = "www/".concat( assetPath ); - AssetManager am = ctx.getResources().getAssets(); + AssetManager am = ctx.getResources().getAssets(); AssetFileDescriptor afd = am.openFd(fullPath); int assetIntID = soundPool.load(afd, 1); soundMap.put( audioID , assetIntID ); diff --git a/Android/OcrApiService/com/ocrapiservice/OcrApiServicePlugin.java b/Android/OcrApiService/com/ocrapiservice/OcrApiServicePlugin.java new file mode 100755 index 0000000..2577ee9 --- /dev/null +++ b/Android/OcrApiService/com/ocrapiservice/OcrApiServicePlugin.java @@ -0,0 +1,81 @@ +package com.ocrapiservice; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; + + +import android.content.ContentResolver; +import android.database.Cursor; +import android.net.Uri; +import android.provider.MediaStore; +import android.util.Log; + +public class OcrApiServicePlugin extends Plugin { + + public static final String NATIVE_ACTION_STRING="convert"; + + private String imageUri = ""; + private String language = ""; + private String apikey = ""; + private OcrService apiClient; + + public PluginResult execute(String action, JSONArray data, String callbackId) { + if (NATIVE_ACTION_STRING.equals(action)) { + try { + // Retrieve parameters + imageUri = data.getString(0); + language = data.getString(1); + apikey = data.getString(2); + + // Load the http client and perform the conversion + apiClient = new OcrService(apikey); + + String filePath = getFilePathFromResourcePath(imageUri); + Log.d("filePath : " , filePath); + apiClient.convertToText(language, filePath); + if (!checkRequestSucceed()) + throw new Exception(apiClient.getResponseText()); + + // return the text as success + String recognizedText = apiClient.getResponseText(); + return new PluginResult(PluginResult.Status.OK, recognizedText); + + } + catch (Exception ex) { + return new PluginResult(PluginResult.Status.ERROR, ex.getMessage()); + } + + + } + + return null; + } + + // check if the request has succeeded + private boolean checkRequestSucceed(){ + if (apiClient.getResponseCode() == 200) + return true; + else + return false; + } + + // Convert a resource path to a file path + private String getFilePathFromResourcePath(String resourcePath){ + String filePath = ""; + // Discussion about the warning + // http://simonmacdonald.blogspot.fr/2012/07/phonegap-android-plugins-sometimes-we.html + ContentResolver cr = this.cordova.getContext().getContentResolver(); + String [] projection = {MediaStore.Images.Media.DATA}; + Cursor cur = cr.query(Uri.parse(resourcePath), projection, null, null, null); + if(cur != null) + { + cur.moveToFirst(); + filePath = cur.getString(0); + } + + return filePath; + } + + +} diff --git a/Android/OcrApiService/com/ocrapiservice/OcrService.java b/Android/OcrApiService/com/ocrapiservice/OcrService.java new file mode 100755 index 0000000..cf812d7 --- /dev/null +++ b/Android/OcrApiService/com/ocrapiservice/OcrService.java @@ -0,0 +1,118 @@ +package com.ocrapiservice; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.mime.MultipartEntity; +import org.apache.http.entity.mime.content.FileBody; +import org.apache.http.entity.mime.content.StringBody; +import org.apache.http.impl.client.DefaultHttpClient; + +public class OcrService { + public final String SERVICE_URL = "http://api.ocrapiservice.com/1.0/rest/ocr"; + + private final String PARAM_IMAGE = "image"; + private final String PARAM_LANGUAGE = "language"; + private final String PARAM_APIKEY = "apikey"; + + private String apiKey; + + private int responseCode; + private String responseText; + + public OcrService(final String apiKey) { + this.apiKey = apiKey; + } + + /* + * Convert image to text. + * + * @param language The image text language. + * @param filePath The image absolute file path. + * + * @return true if everything went okay and false if there is an error with sending and receiving data. + */ + public boolean convertToText(final String language, final String filePath) { + try { + sendImage(language, filePath); + + return true; + } catch (ClientProtocolException e) { + e.printStackTrace(); + + return false; + } catch (IOException e) { + e.printStackTrace(); + + return false; + } + } + + /* + * Send image to OCR service and read response. + * + * @param language The image text language. + * @param filePath The image absolute file path. + * + */ + private void sendImage(final String language, final String filePath) throws ClientProtocolException, IOException { + final HttpClient httpclient = new DefaultHttpClient(); + final HttpPost httppost = new HttpPost(SERVICE_URL); + + final FileBody image = new FileBody(new File(filePath)); + + final MultipartEntity reqEntity = new MultipartEntity(); + reqEntity.addPart(PARAM_IMAGE, image); + reqEntity.addPart(PARAM_LANGUAGE, new StringBody(language)); + reqEntity.addPart(PARAM_APIKEY, new StringBody(getApiKey())); + httppost.setEntity(reqEntity); + + final HttpResponse response = httpclient.execute(httppost); + final HttpEntity resEntity = response.getEntity(); + final StringBuilder sb = new StringBuilder(); + if (resEntity != null) { + final InputStream stream = resEntity.getContent(); + byte bytes[] = new byte[4096]; + int numBytes; + while ((numBytes=stream.read(bytes))!=-1) { + if (numBytes!=0) { + sb.append(new String(bytes, 0, numBytes)); + } + } + } + + setResponseCode(response.getStatusLine().getStatusCode()); + + setResponseText(sb.toString()); + } + + public int getResponseCode() { + return responseCode; + } + + public void setResponseCode(int responseCode) { + this.responseCode = responseCode; + } + + public String getResponseText() { + return responseText; + } + + public void setResponseText(String responseText) { + this.responseText = responseText; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } +} diff --git a/Android/OcrApiService/ocrapiservice.js b/Android/OcrApiService/ocrapiservice.js new file mode 100755 index 0000000..ecfb894 --- /dev/null +++ b/Android/OcrApiService/ocrapiservice.js @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2012 by Guillaume Charhon + */ +var ocrapiservice = { + + // parameters + // - imageURI : URI of the image to convert + // - language : 2 letter language code (ex : "en" for english) + // - apikey : Your api key displayed in your http://ocrapiservice.com dashboard + + convert: function (success, fail, imageURI,language, apikey ) { + return cordova.exec( success, fail, + "OcrApiServicePlugin", + "convert", [imageURI, language, apikey]); + } +}; \ No newline at end of file diff --git a/Android/OcrApiService/org/apache/http/entity/mime/FormBodyPart.java b/Android/OcrApiService/org/apache/http/entity/mime/FormBodyPart.java new file mode 100755 index 0000000..dc67c05 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/FormBodyPart.java @@ -0,0 +1,109 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +import org.apache.http.entity.mime.content.ContentBody; + +/** + * FormBodyPart class represents a content body that can be used as a part of multipart encoded + * entities. This class automatically populates the header with standard fields based on + * the content description of the enclosed body. + * + * @since 4.0 + */ +public class FormBodyPart { + + private final String name; + private final Header header; + + private final ContentBody body; + + public FormBodyPart(final String name, final ContentBody body) { + super(); + if (name == null) { + throw new IllegalArgumentException("Name may not be null"); + } + if (body == null) { + throw new IllegalArgumentException("Body may not be null"); + } + this.name = name; + this.body = body; + this.header = new Header(); + + generateContentDisp(body); + generateContentType(body); + generateTransferEncoding(body); + } + + public String getName() { + return this.name; + } + + public ContentBody getBody() { + return this.body; + } + + public Header getHeader() { + return this.header; + } + + public void addField(final String name, final String value) { + if (name == null) { + throw new IllegalArgumentException("Field name may not be null"); + } + this.header.addField(new MinimalField(name, value)); + } + + protected void generateContentDisp(final ContentBody body) { + StringBuilder buffer = new StringBuilder(); + buffer.append("form-data; name=\""); + buffer.append(getName()); + buffer.append("\""); + if (body.getFilename() != null) { + buffer.append("; filename=\""); + buffer.append(body.getFilename()); + buffer.append("\""); + } + addField(MIME.CONTENT_DISPOSITION, buffer.toString()); + } + + protected void generateContentType(final ContentBody body) { + StringBuilder buffer = new StringBuilder(); + buffer.append(body.getMimeType()); // MimeType cannot be null + if (body.getCharset() != null) { // charset may legitimately be null + buffer.append("; charset="); + buffer.append(body.getCharset()); + } + addField(MIME.CONTENT_TYPE, buffer.toString()); + } + + protected void generateTransferEncoding(final ContentBody body) { + addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/Header.java b/Android/OcrApiService/org/apache/http/entity/mime/Header.java new file mode 100755 index 0000000..2c4c6dd --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/Header.java @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * The header of an entity (see RFC 2045). + */ +public class Header implements Iterable { + + private final List fields; + private final Map> fieldMap; + + public Header() { + super(); + this.fields = new LinkedList(); + this.fieldMap = new HashMap>(); + } + + public void addField(final MinimalField field) { + if (field == null) { + return; + } + String key = field.getName().toLowerCase(Locale.US); + List values = this.fieldMap.get(key); + if (values == null) { + values = new LinkedList(); + this.fieldMap.put(key, values); + } + values.add(field); + this.fields.add(field); + } + + public List getFields() { + return new ArrayList(this.fields); + } + + public MinimalField getField(final String name) { + if (name == null) { + return null; + } + String key = name.toLowerCase(Locale.US); + List list = this.fieldMap.get(key); + if (list != null && !list.isEmpty()) { + return list.get(0); + } + return null; + } + + public List getFields(final String name) { + if (name == null) { + return null; + } + String key = name.toLowerCase(Locale.US); + List list = this.fieldMap.get(key); + if (list == null || list.isEmpty()) { + return Collections.emptyList(); + } else { + return new ArrayList(list); + } + } + + public int removeFields(final String name) { + if (name == null) { + return 0; + } + String key = name.toLowerCase(Locale.US); + List removed = fieldMap.remove(key); + if (removed == null || removed.isEmpty()) { + return 0; + } + this.fields.removeAll(removed); + return removed.size(); + } + + public void setField(final MinimalField field) { + if (field == null) { + return; + } + String key = field.getName().toLowerCase(Locale.US); + List list = fieldMap.get(key); + if (list == null || list.isEmpty()) { + addField(field); + return; + } + list.clear(); + list.add(field); + int firstOccurrence = -1; + int index = 0; + for (Iterator it = this.fields.iterator(); it.hasNext(); index++) { + MinimalField f = it.next(); + if (f.getName().equalsIgnoreCase(field.getName())) { + it.remove(); + if (firstOccurrence == -1) { + firstOccurrence = index; + } + } + } + this.fields.add(firstOccurrence, field); + } + + public Iterator iterator() { + return Collections.unmodifiableList(fields).iterator(); + } + + @Override + public String toString() { + return this.fields.toString(); + } + +} + diff --git a/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipart.java b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipart.java new file mode 100755 index 0000000..ce7f2d8 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipart.java @@ -0,0 +1,262 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.entity.mime.content.ContentBody; +import org.apache.http.util.ByteArrayBuffer; + +/** + * HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is + * capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or + * the browser compatible modes. + * + * @since 4.0 + */ +public class HttpMultipart { + + private static ByteArrayBuffer encode( + final Charset charset, final String string) { + ByteBuffer encoded = charset.encode(CharBuffer.wrap(string)); + ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining()); + bab.append(encoded.array(), encoded.position(), encoded.remaining()); + return bab; + } + + private static void writeBytes( + final ByteArrayBuffer b, final OutputStream out) throws IOException { + out.write(b.buffer(), 0, b.length()); + } + + private static void writeBytes( + final String s, final Charset charset, final OutputStream out) throws IOException { + ByteArrayBuffer b = encode(charset, s); + writeBytes(b, out); + } + + private static void writeBytes( + final String s, final OutputStream out) throws IOException { + ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s); + writeBytes(b, out); + } + + private static void writeField( + final MinimalField field, final OutputStream out) throws IOException { + writeBytes(field.getName(), out); + writeBytes(FIELD_SEP, out); + writeBytes(field.getBody(), out); + writeBytes(CR_LF, out); + } + + private static void writeField( + final MinimalField field, final Charset charset, final OutputStream out) throws IOException { + writeBytes(field.getName(), charset, out); + writeBytes(FIELD_SEP, out); + writeBytes(field.getBody(), charset, out); + writeBytes(CR_LF, out); + } + + private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": "); + private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n"); + private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--"); + + + private final String subType; + private final Charset charset; + private final String boundary; + private final List parts; + + private final HttpMultipartMode mode; + + /** + * Creates an instance with the specified settings. + * + * @param subType mime subtype - must not be {@code null} + * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + * @param boundary to use - must not be {@code null} + * @param mode the mode to use + * @throws IllegalArgumentException if charset is null or boundary is null + */ + public HttpMultipart(final String subType, final Charset charset, final String boundary, HttpMultipartMode mode) { + super(); + if (subType == null) { + throw new IllegalArgumentException("Multipart subtype may not be null"); + } + if (boundary == null) { + throw new IllegalArgumentException("Multipart boundary may not be null"); + } + this.subType = subType; + this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET; + this.boundary = boundary; + this.parts = new ArrayList(); + this.mode = mode; + } + + /** + * Creates an instance with the specified settings. + * Mode is set to {@link HttpMultipartMode#STRICT} + * + * @param subType mime subtype - must not be {@code null} + * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + * @param boundary to use - must not be {@code null} + * @throws IllegalArgumentException if charset is null or boundary is null + */ + public HttpMultipart(final String subType, final Charset charset, final String boundary) { + this(subType, charset, boundary, HttpMultipartMode.STRICT); + } + + public HttpMultipart(final String subType, final String boundary) { + this(subType, null, boundary); + } + + public String getSubType() { + return this.subType; + } + + public Charset getCharset() { + return this.charset; + } + + public HttpMultipartMode getMode() { + return this.mode; + } + + public List getBodyParts() { + return this.parts; + } + + public void addBodyPart(final FormBodyPart part) { + if (part == null) { + return; + } + this.parts.add(part); + } + + public String getBoundary() { + return this.boundary; + } + + private void doWriteTo( + final HttpMultipartMode mode, + final OutputStream out, + boolean writeContent) throws IOException { + + ByteArrayBuffer boundary = encode(this.charset, getBoundary()); + for (FormBodyPart part: this.parts) { + writeBytes(TWO_DASHES, out); + writeBytes(boundary, out); + writeBytes(CR_LF, out); + + Header header = part.getHeader(); + + switch (mode) { + case STRICT: + for (MinimalField field: header) { + writeField(field, out); + } + break; + case BROWSER_COMPATIBLE: + // Only write Content-Disposition + // Use content charset + MinimalField cd = part.getHeader().getField(MIME.CONTENT_DISPOSITION); + writeField(cd, this.charset, out); + String filename = part.getBody().getFilename(); + if (filename != null) { + MinimalField ct = part.getHeader().getField(MIME.CONTENT_TYPE); + writeField(ct, this.charset, out); + } + break; + } + writeBytes(CR_LF, out); + + if (writeContent) { + part.getBody().writeTo(out); + } + writeBytes(CR_LF, out); + } + writeBytes(TWO_DASHES, out); + writeBytes(boundary, out); + writeBytes(TWO_DASHES, out); + writeBytes(CR_LF, out); + } + + /** + * Writes out the content in the multipart/form encoding. This method + * produces slightly different formatting depending on its compatibility + * mode. + * + * @see #getMode() + */ + public void writeTo(final OutputStream out) throws IOException { + doWriteTo(this.mode, out, true); + } + + /** + * Determines the total length of the multipart content (content length of + * individual parts plus that of extra elements required to delimit the parts + * from one another). If any of the @{link BodyPart}s contained in this object + * is of a streaming entity of unknown length the total length is also unknown. + *

+ * This method buffers only a small amount of data in order to determine the + * total length of the entire entity. The content of individual parts is not + * buffered. + * + * @return total length of the multipart entity if known, -1 + * otherwise. + */ + public long getTotalLength() { + long contentLen = 0; + for (FormBodyPart part: this.parts) { + ContentBody body = part.getBody(); + long len = body.getContentLength(); + if (len >= 0) { + contentLen += len; + } else { + return -1; + } + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + doWriteTo(this.mode, out, false); + byte[] extra = out.toByteArray(); + return contentLen + extra.length; + } catch (IOException ex) { + // Should never happen + return -1; + } + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipartMode.java b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipartMode.java new file mode 100755 index 0000000..13e15e0 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipartMode.java @@ -0,0 +1,41 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +/** + * + * @since 4.0 + */ +public enum HttpMultipartMode { + + /** RFC 822, RFC 2045, RFC 2046 compliant */ + STRICT, + /** browser-compatible mode, i.e. only write Content-Disposition; use content charset */ + BROWSER_COMPATIBLE + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/MIME.java b/Android/OcrApiService/org/apache/http/entity/mime/MIME.java new file mode 100755 index 0000000..775e397 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/MIME.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +import java.nio.charset.Charset; + +/** + * + * @since 4.0 + */ +public final class MIME { + + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_TRANSFER_ENC = "Content-Transfer-Encoding"; + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + + public static final String ENC_8BIT = "8bit"; + public static final String ENC_BINARY = "binary"; + + /** The default character set to be used, i.e. "US-ASCII" */ + public static final Charset DEFAULT_CHARSET = Charset.forName("US-ASCII"); + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/MinimalField.java b/Android/OcrApiService/org/apache/http/entity/mime/MinimalField.java new file mode 100755 index 0000000..8fb160c --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/MinimalField.java @@ -0,0 +1,63 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +/** + * Minimal MIME field. + * + * @since 4.0 + */ +public class MinimalField { + + private final String name; + private final String value; + + MinimalField(final String name, final String value) { + super(); + this.name = name; + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getBody() { + return this.value; + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(this.name); + buffer.append(": "); + buffer.append(this.value); + return buffer.toString(); + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/MultipartEntity.java b/Android/OcrApiService/org/apache/http/entity/mime/MultipartEntity.java new file mode 100755 index 0000000..deed644 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/MultipartEntity.java @@ -0,0 +1,186 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.Random; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.entity.mime.content.ContentBody; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; + +/** + * Multipart/form coded HTTP entity consisting of multiple body parts. + * + * @since 4.0 + */ +public class MultipartEntity implements HttpEntity { + + /** + * The pool of ASCII chars to be used for generating a multipart boundary. + */ + private final static char[] MULTIPART_CHARS = + "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + .toCharArray(); + + private final HttpMultipart multipart; + private final Header contentType; + + // @GuardedBy("dirty") // we always read dirty before accessing length + private long length; + private volatile boolean dirty; // used to decide whether to recalculate length + + /** + * Creates an instance using the specified parameters + * @param mode the mode to use, may be {@code null}, in which case {@link HttpMultipartMode#STRICT} is used + * @param boundary the boundary string, may be {@code null}, in which case {@link #generateBoundary()} is invoked to create the string + * @param charset the character set to use, may be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + */ + public MultipartEntity( + HttpMultipartMode mode, + String boundary, + Charset charset) { + super(); + if (boundary == null) { + boundary = generateBoundary(); + } + if (mode == null) { + mode = HttpMultipartMode.STRICT; + } + this.multipart = new HttpMultipart("form-data", charset, boundary, mode); + this.contentType = new BasicHeader( + HTTP.CONTENT_TYPE, + generateContentType(boundary, charset)); + this.dirty = true; + } + + /** + * Creates an instance using the specified {@link HttpMultipartMode} mode. + * Boundary and charset are set to {@code null}. + * @param mode the desired mode + */ + public MultipartEntity(final HttpMultipartMode mode) { + this(mode, null, null); + } + + /** + * Creates an instance using mode {@link HttpMultipartMode#STRICT} + */ + public MultipartEntity() { + this(HttpMultipartMode.STRICT, null, null); + } + + protected String generateContentType( + final String boundary, + final Charset charset) { + StringBuilder buffer = new StringBuilder(); + buffer.append("multipart/form-data; boundary="); + buffer.append(boundary); + if (charset != null) { + buffer.append("; charset="); + buffer.append(charset.name()); + } + return buffer.toString(); + } + + protected String generateBoundary() { + StringBuilder buffer = new StringBuilder(); + Random rand = new Random(); + int count = rand.nextInt(11) + 30; // a random size from 30 to 40 + for (int i = 0; i < count; i++) { + buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); + } + return buffer.toString(); + } + + public void addPart(final FormBodyPart bodyPart) { + this.multipart.addBodyPart(bodyPart); + this.dirty = true; + } + + public void addPart(final String name, final ContentBody contentBody) { + addPart(new FormBodyPart(name, contentBody)); + } + + public boolean isRepeatable() { + for (FormBodyPart part: this.multipart.getBodyParts()) { + ContentBody body = part.getBody(); + if (body.getContentLength() < 0) { + return false; + } + } + return true; + } + + public boolean isChunked() { + return !isRepeatable(); + } + + public boolean isStreaming() { + return !isRepeatable(); + } + + public long getContentLength() { + if (this.dirty) { + this.length = this.multipart.getTotalLength(); + this.dirty = false; + } + return this.length; + } + + public Header getContentType() { + return this.contentType; + } + + public Header getContentEncoding() { + return null; + } + + public void consumeContent() + throws IOException, UnsupportedOperationException{ + if (isStreaming()) { + throw new UnsupportedOperationException( + "Streaming entity does not implement #consumeContent()"); + } + } + + public InputStream getContent() throws IOException, UnsupportedOperationException { + throw new UnsupportedOperationException( + "Multipart form entity does not implement #getContent()"); + } + + public void writeTo(final OutputStream outstream) throws IOException { + this.multipart.writeTo(outstream); + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/AbstractContentBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/AbstractContentBody.java new file mode 100755 index 0000000..3b55773 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/AbstractContentBody.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime.content; + +/** + * + * @since 4.0 + */ +public abstract class AbstractContentBody implements ContentBody { + + private final String mimeType; + private final String mediaType; + private final String subType; + + public AbstractContentBody(final String mimeType) { + super(); + if (mimeType == null) { + throw new IllegalArgumentException("MIME type may not be null"); + } + this.mimeType = mimeType; + int i = mimeType.indexOf('/'); + if (i != -1) { + this.mediaType = mimeType.substring(0, i); + this.subType = mimeType.substring(i + 1); + } else { + this.mediaType = mimeType; + this.subType = null; + } + } + + public String getMimeType() { + return this.mimeType; + } + + public String getMediaType() { + return this.mediaType; + } + + public String getSubType() { + return this.subType; + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/ByteArrayBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/ByteArrayBody.java new file mode 100755 index 0000000..863c254 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/ByteArrayBody.java @@ -0,0 +1,98 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.entity.mime.content; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.http.entity.mime.MIME; +import org.apache.http.entity.mime.content.AbstractContentBody; + +/** + * Body part that is built using a byte array containing a file. + * + * @since 4.1 + */ +public class ByteArrayBody extends AbstractContentBody { + + /** + * The contents of the file contained in this part. + */ + private final byte[] data; + + /** + * The name of the file contained in this part. + */ + private final String filename; + + /** + * Creates a new ByteArrayBody. + * + * @param data The contents of the file contained in this part. + * @param mimeType The mime type of the file contained in this part. + * @param filename The name of the file contained in this part. + */ + public ByteArrayBody(final byte[] data, final String mimeType, final String filename) { + super(mimeType); + if (data == null) { + throw new IllegalArgumentException("byte[] may not be null"); + } + this.data = data; + this.filename = filename; + } + + /** + * Creates a new ByteArrayBody. + * + * @param data The contents of the file contained in this part. + * @param filename The name of the file contained in this part. + */ + public ByteArrayBody(final byte[] data, final String filename) { + this(data, "application/octet-stream", filename); + } + + public String getFilename() { + return filename; + } + + public void writeTo(final OutputStream out) throws IOException { + out.write(data); + } + + public String getCharset() { + return null; + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public long getContentLength() { + return data.length; + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/ContentBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentBody.java new file mode 100755 index 0000000..58076f9 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentBody.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime.content; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @since 4.0 + */ +public interface ContentBody extends ContentDescriptor { + + String getFilename(); + + void writeTo(OutputStream out) throws IOException; + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/ContentDescriptor.java b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentDescriptor.java new file mode 100755 index 0000000..6320d38 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentDescriptor.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime.content; + +/** + * Represents common content properties. + */ +public interface ContentDescriptor { + + /** + * Returns the body descriptors MIME type. + * @see #getMediaType() + * @see #getSubType() + * @return The MIME type, which has been parsed from the + * content-type definition. Must not be null, but + * "text/plain", if no content-type was specified. + */ + String getMimeType(); + + /** + * Gets the defaulted MIME media type for this content. + * For example TEXT, IMAGE, MULTIPART + * @see #getMimeType() + * @return the MIME media type when content-type specified, + * otherwise the correct default (TEXT) + */ + String getMediaType(); + + /** + * Gets the defaulted MIME sub type for this content. + * @see #getMimeType() + * @return the MIME media type when content-type is specified, + * otherwise the correct default (PLAIN) + */ + String getSubType(); + + /** + *

The body descriptors character set, defaulted appropriately for the MIME type.

+ *

+ * For TEXT types, this will be defaulted to us-ascii. + * For other types, when the charset parameter is missing this property will be null. + *

+ * @return Character set, which has been parsed from the + * content-type definition. Not null for TEXT types, when unset will + * be set to default us-ascii. For other types, when unset, + * null will be returned. + */ + String getCharset(); + + /** + * Returns the body descriptors transfer encoding. + * @return The transfer encoding. Must not be null, but "7bit", + * if no transfer-encoding was specified. + */ + String getTransferEncoding(); + + /** + * Returns the body descriptors content-length. + * @return Content length, if known, or -1, to indicate the absence of a + * content-length header. + */ + long getContentLength(); + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/FileBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/FileBody.java new file mode 100755 index 0000000..206bc78 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/FileBody.java @@ -0,0 +1,125 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime.content; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.http.entity.mime.MIME; + +/** + * + * @since 4.0 + */ +public class FileBody extends AbstractContentBody { + + private final File file; + private final String filename; + private final String charset; + + /** + * @since 4.1 + */ + public FileBody(final File file, + final String filename, + final String mimeType, + final String charset) { + super(mimeType); + if (file == null) { + throw new IllegalArgumentException("File may not be null"); + } + this.file = file; + if (filename != null) + this.filename = filename; + else + this.filename = file.getName(); + this.charset = charset; + } + + /** + * @since 4.1 + */ + public FileBody(final File file, + final String mimeType, + final String charset) { + this(file, null, mimeType, charset); + } + + public FileBody(final File file, final String mimeType) { + this(file, mimeType, null); + } + + public FileBody(final File file) { + this(file, "application/octet-stream"); + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(this.file); + } + + public void writeTo(final OutputStream out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + InputStream in = new FileInputStream(this.file); + try { + byte[] tmp = new byte[4096]; + int l; + while ((l = in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } finally { + in.close(); + } + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public String getCharset() { + return charset; + } + + public long getContentLength() { + return this.file.length(); + } + + public String getFilename() { + return filename; + } + + public File getFile() { + return this.file; + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/InputStreamBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/InputStreamBody.java new file mode 100755 index 0000000..db1b4dd --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/InputStreamBody.java @@ -0,0 +1,94 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime.content; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.http.entity.mime.MIME; + +/** + * + * @since 4.0 + */ +public class InputStreamBody extends AbstractContentBody { + + private final InputStream in; + private final String filename; + + public InputStreamBody(final InputStream in, final String mimeType, final String filename) { + super(mimeType); + if (in == null) { + throw new IllegalArgumentException("Input stream may not be null"); + } + this.in = in; + this.filename = filename; + } + + public InputStreamBody(final InputStream in, final String filename) { + this(in, "application/octet-stream", filename); + } + + public InputStream getInputStream() { + return this.in; + } + + public void writeTo(final OutputStream out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + try { + byte[] tmp = new byte[4096]; + int l; + while ((l = this.in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } finally { + this.in.close(); + } + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public String getCharset() { + return null; + } + + public long getContentLength() { + return -1; + } + + public String getFilename() { + return this.filename; + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/StringBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/StringBody.java new file mode 100755 index 0000000..8ea90bf --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/StringBody.java @@ -0,0 +1,164 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.entity.mime.content; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +import org.apache.http.entity.mime.MIME; + +/** + * + * @since 4.0 + */ +public class StringBody extends AbstractContentBody { + + private final byte[] content; + private final Charset charset; + + /** + * @since 4.1 + */ + public static StringBody create( + final String text, + final String mimeType, + final Charset charset) throws IllegalArgumentException { + try { + return new StringBody(text, mimeType, charset); + } catch (UnsupportedEncodingException ex) { + throw new IllegalArgumentException("Charset " + charset + " is not supported", ex); + } + } + + /** + * @since 4.1 + */ + public static StringBody create( + final String text, final Charset charset) throws IllegalArgumentException { + return create(text, null, charset); + } + + /** + * @since 4.1 + */ + public static StringBody create(final String text) throws IllegalArgumentException { + return create(text, null, null); + } + + /** + * Create a StringBody from the specified text, mime type and character set. + * + * @param text to be used for the body, not {@code null} + * @param mimeType the mime type, not {@code null} + * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + */ + public StringBody( + final String text, + final String mimeType, + Charset charset) throws UnsupportedEncodingException { + super(mimeType); + if (text == null) { + throw new IllegalArgumentException("Text may not be null"); + } + if (charset == null) { + charset = Charset.forName("US-ASCII"); + } + this.content = text.getBytes(charset.name()); + this.charset = charset; + } + + /** + * Create a StringBody from the specified text and character set. + * The mime type is set to "text/plain". + * + * @param text to be used for the body, not {@code null} + * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + */ + public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException { + this(text, "text/plain", charset); + } + + /** + * Create a StringBody from the specified text. + * The mime type is set to "text/plain". + * The hosts default charset is used. + * + * @param text to be used for the body, not {@code null} + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + */ + public StringBody(final String text) throws UnsupportedEncodingException { + this(text, "text/plain", null); + } + + public Reader getReader() { + return new InputStreamReader( + new ByteArrayInputStream(this.content), + this.charset); + } + + public void writeTo(final OutputStream out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + InputStream in = new ByteArrayInputStream(this.content); + byte[] tmp = new byte[4096]; + int l; + while ((l = in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } + + public String getTransferEncoding() { + return MIME.ENC_8BIT; + } + + public String getCharset() { + return this.charset.name(); + } + + public long getContentLength() { + return this.content.length; + } + + public String getFilename() { + return null; + } + +} diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/package.html b/Android/OcrApiService/org/apache/http/entity/mime/content/package.html new file mode 100755 index 0000000..4532091 --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/content/package.html @@ -0,0 +1,35 @@ + + + + + +MIME Multipart content body parts. + + diff --git a/Android/OcrApiService/org/apache/http/entity/mime/package.html b/Android/OcrApiService/org/apache/http/entity/mime/package.html new file mode 100755 index 0000000..1472f3f --- /dev/null +++ b/Android/OcrApiService/org/apache/http/entity/mime/package.html @@ -0,0 +1,35 @@ + + + + + +Support for MIME multipart encoded entities + + diff --git a/Android/OcrApiService/readme.md b/Android/OcrApiService/readme.md new file mode 100755 index 0000000..f35aab6 --- /dev/null +++ b/Android/OcrApiService/readme.md @@ -0,0 +1,98 @@ +OcrApiService.com plugin for Cordova +--------------- +This plug-in exposes OcrApiService.com optical character recognition api. +Tested with Cordova 2.0 + +## Integration instruction ## + +* Sign up for an account at http://ocrapiservice.com to get an api key +* Copy in your "src" folder : + * the "com" folder + * the "org" folder +* Add "ocrapiservice.js" to your www folder + * include it in your index.html file ` +` +* Open your config.xml and add `` + +## Usage functions ## + +### ocrapiservice.convert ### +Returns a text from an image + +**ocrapiservice.convert ( success, error, imageURI, languageCode, apiKey );** + +**Parameters** + +* success : The callback that is called with the extracted text. +* error : The callback that is called if there was an error. +* imageURI : A URI containing the image. +* languageCode : Two letters language code +* apiKey : Private api key provided in your ocrapiservice.com dashboard + +### Quick example #### +
   
+function successCallback(result) {    
+   // display the extracted text   
+   alert(result);    
+}    
+function errorCallback(error) {
+   alert(error); 
+} 
+
+function onSuccess(imageURI) {
+	ocrapiservice.convert( successCallback, errorCallback, "content://media/external/images/media/5", "en", "DdfJmSnWjK" ); 
+    
+}
+
+ + +### Full example ### +
  
+
+function testExample() { 
+	navigator.camera.getPicture(onSuccess, onFail, { quality: 50, 
+	sourceType : Camera.PictureSourceType.PHOTOLIBRARY, 
+        destinationType: Camera.DestinationType.FILE_URI }); 
+} 
+
+function successCallback (result) { 
+   alert(result); 
+} 
+function errorCallback (error) { 
+   alert(error); 
+} 
+
+function onSuccess(imageURI) {
+	alert(imageURI);
+	ocrapiservice.convert( successCallback, errorCallback, imageURI, "en", "DdfJmSnWjK" ); 
+}
+
+function onFail(message) {
+    alert('Failed because: ' + message);
+}
+
+testExample();
+
+
+ +### MIT License ### + +Copyright (c) 2012 Smart Mobile Software + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/Android/PayPalPlugin/AndroidManifest.xml b/Android/PayPalPlugin/AndroidManifest.xml index 9ed944d..00d406a 100644 --- a/Android/PayPalPlugin/AndroidManifest.xml +++ b/Android/PayPalPlugin/AndroidManifest.xml @@ -1,28 +1,41 @@ - - - + - - - + - - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/PayPalPlugin/LICENSE b/Android/PayPalPlugin/LICENSE index 4a77837..3925c9a 100644 --- a/Android/PayPalPlugin/LICENSE +++ b/Android/PayPalPlugin/LICENSE @@ -3,6 +3,7 @@ The MIT License Copyright (C) 2011 Mobile Developer Solutions Copyright (C) 2011, Appception, Inc. +Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Android/PayPalPlugin/README.md b/Android/PayPalPlugin/README.md index 72ddb62..be134a2 100644 --- a/Android/PayPalPlugin/README.md +++ b/Android/PayPalPlugin/README.md @@ -1,19 +1,20 @@ # PhoneGap PayPal-Plugin # -by Paul Beusterien, Mobile Developer Solutions and Carl Stehle, Appception Inc. +by Paul Beusterien, Mobile Developer Solutions, Carl Stehle, Appception Inc. and Tomaz Kregar, Bucka IT ## Adding the Plugin to your project ## -Using this plugin requires Android PhoneGap and the PayPal Mobile Payments Library. The PayPal Mobile Payments Library can be downloaded [here](https://www.x.com/community/ppx/xspaces/mobile/mep). +Using this plugin requires Android Cordova (PhoneGap) and the PayPal Mobile Payments Library. The PayPal Mobile Payments Library can be downloaded [here](https://www.x.com/community/ppx/xspaces/mobile/mep). -1. Create an Android PhoneGap project. Details at http://www.mobiledevelopersolutions.com/home/start +1. Create an Android Cordova project. Details at http://docs.phonegap.com/en/2.0.0/guide_getting-started_android_index.md.html 2. Put PayPal_MPL.jar into your project's libs directory and add it to the build path. In Eclipse, right click on PayPal_MPL.jar and select Add to Build Path. 3. Copy assets/www/ files into your project's assets/www/ directory 4. Copy src/com/phonegap/plugin/ files into your project's src/com/phonegap/plugin/ directory 5. Make sure your AndroidManifest.xml includes a superset of the permissions shown in the reference AndroidManifest.xml 6. Add the com.paypal.android.MEP.PayPalActivity as shown in the reference AndroidManifest.xml -7. Make sure the phonegap.{version}.js filename in index.html matches the filename in your www directory. -8. Deploy and test the app. The default environment is ENV_NONE. +7. Include the plugin registration in /res/xml/config.xml as shown in the reference config.xml +8. Make sure the cordova.{version}.js filename in index.html matches the filename in your www directory. +9. Deploy and test the app. The default environment is ENV_NONE. ## Using the PayPal Sandbox ## @@ -24,7 +25,10 @@ Using this plugin requires Android PhoneGap and the PayPal Mobile Payments Libra ## RELEASE NOTES ## -### 201100618 ### +### 20120829 ### +* Added suppport for Cordova 2.0.0 + +### 20110618 ### * Initial release * By default the PayPalPlugin-Host runs in ENV_NONE (offline) with a dummy PayPal ID. Change to ENV_SANDBOX or ENV_LIVE * Only tested with ENV_NONE and ENV_SANDBOX diff --git a/Android/PayPalPlugin/assets/www/index.html b/Android/PayPalPlugin/assets/www/index.html index e7a51b7..08290dc 100644 --- a/Android/PayPalPlugin/assets/www/index.html +++ b/Android/PayPalPlugin/assets/www/index.html @@ -1,7 +1,7 @@ - + diff --git a/Android/PayPalPlugin/assets/www/paypal.js b/Android/PayPalPlugin/assets/www/paypal.js index ef6367d..da333ee 100644 --- a/Android/PayPalPlugin/assets/www/paypal.js +++ b/Android/PayPalPlugin/assets/www/paypal.js @@ -2,13 +2,13 @@ * * Copyright (C) 2011, Appception, Inc.. All Rights Reserved. * Copyright (C) 2011, Mobile Developer Solutions All Rights Reserved. +* Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. All Rights Reserved. */ /* * @return Instance of PayPal */ -var PayPal = function() { -}; +var PayPal = function() {}; /** * @param directory @@ -29,7 +29,7 @@ var fail = function(e) { PayPal.prototype.invoke = function(callee, arg, successFunction) { var succ = successFunction ? successFunction : genericSuccess; - return PhoneGap.exec(succ, + return cordova.exec(succ, fail, 'PayPalPlugin', // Telling PhoneGap that we want to run "PayPal Plugin" callee, // Telling the plugin, which action we want to perform @@ -38,34 +38,40 @@ PayPal.prototype.invoke = function(callee, arg, successFunction) { /** *
    - *
  • Register the Directory Listing Javascript plugin.
  • - *
  • Also register native call which will be called when this plugin runs
  • + *
  • Register native call which will be called when this plugin runs
  • + *
  • Plugin must be registered in /res/xml/config.xml : <plugin name="PayPalPlugin" value="com.phonegap.plugin.PayPalPlugin"/>
  • *
*/ -PhoneGap.addConstructor(function() { - // Register the javascript plugin with PhoneGap - PhoneGap.addPlugin('PayPal', new PayPal()); - // Register the native class of plugin with PhoneGap - PluginManager.addService("PayPalPlugin", "com.phonegap.plugin.PayPalPlugin"); -}); +if(cordova.addPlugin) { //versions before 2.0.0 + cordova.addConstructor(function() { + // Register the javascript plugin with PhoneGap + cordova.addPlugin('PayPal', new PayPal()); + }); +} else { + // Creating the native object of plugin + if(!window.plugins) + window.plugins = {}; + if (!window.plugins.PayPal) + window.plugins.PayPal = new PayPal(); +} var mpl = { - construct : function(str) { - window.plugins.PayPal.invoke('construct', str); - }, - prepare : function(ptype) { - window.plugins.PayPal.invoke('prepare', ptype); - }, - getStatus : function() { - return window.plugins.PayPal.invoke('getStatus', null, getStatusCallback); - }, - setPaymentInfo : function(arg) { - window.plugins.PayPal.invoke('setPaymentInfo', arg); - }, - payButton : function(btype) { - window.plugins.PayPal.invoke('pay', btype); - } + construct : function(str) { + window.plugins.PayPal.invoke('construct', str); + }, + prepare : function(ptype) { + window.plugins.PayPal.invoke('prepare', ptype); + }, + getStatus : function() { + return window.plugins.PayPal.invoke('getStatus', null, getStatusCallback); + }, + setPaymentInfo : function(arg) { + window.plugins.PayPal.invoke('setPaymentInfo', arg); + }, + payButton : function(btype) { + window.plugins.PayPal.invoke('pay', btype); + } }; /* diff --git a/Android/PayPalPlugin/res/xml/config.xml b/Android/PayPalPlugin/res/xml/config.xml new file mode 100644 index 0000000..61b91bb --- /dev/null +++ b/Android/PayPalPlugin/res/xml/config.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java b/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java index 2b5e100..6a7cf97 100644 --- a/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java +++ b/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java @@ -2,6 +2,7 @@ * * Copyright (C) 2011, Appception, Inc.. All Rights Reserved. * Copyright (C) 2011, Mobile Developer Solutions All Rights Reserved. + * Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. All Rights Reserved. */ package com.phonegap.plugin; @@ -12,9 +13,9 @@ import org.json.JSONObject; import android.util.Log; -import com.phonegap.api.Plugin; -import com.phonegap.api.PluginResult; -import com.phonegap.api.PluginResult.Status; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.apache.cordova.api.PluginResult.Status; public class PayPalPlugin extends Plugin { private static mpl PluginMpl; @@ -29,7 +30,7 @@ public class PayPalPlugin extends Plugin { try { if (action.equals("construct")) { thisPlugin = this; - PluginMpl = new mpl(this.ctx, data.getString(0)); + PluginMpl = new mpl(this.ctx.getContext(), data.getString(0)); // PluginMpl = new mpl(this.ctx, PayPal.ENV_NONE, ""); result = new PluginResult(Status.OK); diff --git a/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java b/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java index 4293253..f59db1c 100644 --- a/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java +++ b/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java @@ -2,6 +2,7 @@ * * Copyright (C) 2011, Appception, Inc.. All Rights Reserved. * Copyright (C) 2011, Mobile Developer Solutions All Rights Reserved. + * Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. All Rights Reserved. */ package com.phonegap.plugin; @@ -23,7 +24,7 @@ import com.paypal.android.MEP.PayPalInvoiceData; import com.paypal.android.MEP.PayPalInvoiceItem; import com.paypal.android.MEP.PayPalPayment; import com.paypal.android.MEP.PayPalPreapproval; -import com.phonegap.DroidGap; +import org.apache.cordova.DroidGap; public class mpl { @@ -88,9 +89,9 @@ public class mpl { } // Construct new instance with information from main activity - public mpl(Context context, String JSONdata) { - mpl_activity = (Activity) context; - mpl_context = context.getApplicationContext(); + public mpl(Context ctx, String JSONdata) { + mpl_activity = (Activity) ctx; + mpl_context = ctx.getApplicationContext(); parseConstructArg(JSONdata); if (mpjs_server.equals("ENV_NONE")) { server = PayPal.ENV_NONE; diff --git a/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java b/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java index c5da050..4d46058 100644 --- a/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java +++ b/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java @@ -29,8 +29,8 @@ import android.app.PendingIntent; import android.content.Intent; import android.telephony.SmsManager; -import com.phonegap.api.Plugin; -import com.phonegap.api.PluginResult; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; public class SmsPlugin extends Plugin { public final String ACTION_SEND_SMS = "SendSMS"; diff --git a/Android/Screenshot/README.md b/Android/Screenshot/README.md new file mode 100644 index 0000000..c0ff635 --- /dev/null +++ b/Android/Screenshot/README.md @@ -0,0 +1,36 @@ +Screenshot +=============== +Plugin for Cordova (1.6+) + +The Screenshot plugin allows your application to take screenshots of the current screen and save them into the phone. + +Platforms +--------- +Currently available on: + +### Android +Copy the content of *src* folder to your *src* folder. + +Copy the content of *www* folder to you *www* folder. + +Edit your *AndroidManifest.xml* and add the following permission: +`` + +In addition you have to edit your *res/xml/plugins.xml* file to let Cordova know about the plugin: +`` + +### iOS +Copy the *Screenshot.h* and *Screenshot.m* files to your projects "Plugins" folder. + +Copy the *Screenshot.js* file to you *www* folder. + +Add the Screenshot plugin to the *Cordova.plist* file (to the Plugins list). Both Key and Value are "Screenshot". + + +License +======= +Copyright (C) 2012 30ideas (http://30ide.as) +MIT licensed + +Created by Josemando Sobral +Jul 2nd, 2012 diff --git a/Android/Screenshot/v1.8.1/src/org/apache/cordova/Screenshot.java b/Android/Screenshot/v1.8.1/src/org/apache/cordova/Screenshot.java new file mode 100644 index 0000000..1b7d1e7 --- /dev/null +++ b/Android/Screenshot/v1.8.1/src/org/apache/cordova/Screenshot.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2012 30ideas (http://30ide.as) + * MIT licensed + * + * @author Josemando Sobral + * @created Jul 2nd, 2012. + */ +package org.apache.cordova; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; + +import android.graphics.Bitmap; +import android.os.Environment; +import android.view.View; + +public class Screenshot extends Plugin { + private PluginResult result = null; + + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + // starting on ICS, some WebView methods + // can only be called on UI threads + super.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + View view = webView.getRootView(); + + view.setDrawingCacheEnabled(true); + Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); + view.setDrawingCacheEnabled(false); + + try { + File folder = new File(Environment.getExternalStorageDirectory(), "Pictures"); + if (!folder.exists()) { + folder.mkdirs(); + } + + File f = new File(folder, "screenshot_" + System.currentTimeMillis() + ".png"); + + FileOutputStream fos = new FileOutputStream(f); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + result = new PluginResult(PluginResult.Status.OK); + + } catch (IOException e) { + result = new PluginResult(PluginResult.Status.IO_EXCEPTION, e.getMessage()); + } + } + }); + + // waiting ui thread to finish + while (this.result == null) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignoring exception, since we have to wait + // ui thread to finish + } + } + + return this.result; + } + +} diff --git a/Android/Screenshot/v1.8.1/www/Screenshot.js b/Android/Screenshot/v1.8.1/www/Screenshot.js new file mode 100644 index 0000000..e418c42 --- /dev/null +++ b/Android/Screenshot/v1.8.1/www/Screenshot.js @@ -0,0 +1,40 @@ +/* + * This code is adapted from the work of Michael Nachbaur + * by Simon Madine of The Angry Robot Zombie Factory + * - Converted to Cordova 1.6.1 by Josemando Sobral. + * 2012-07-03 + * MIT licensed + */ + +/* + * Temporary Scope to contain the plugin. + * - More information here: + * https://github.com/apache/incubator-cordova-ios/blob/master/guides/Cordova%20Plugin%20Upgrade%20Guide.md + */ +(function() { + /* Get local ref to global PhoneGap/Cordova/cordova object for exec function. + - This increases the compatibility of the plugin. */ + var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + + /** + * This class exposes the ability to take a Screenshot to JavaScript + */ + function Screenshot() { } + + /** + * Save the screenshot to the user's Photo Library + */ + Screenshot.prototype.saveScreenshot = function() { + cordovaRef.exec(null, null, "Screenshot", "saveScreenshot", []); + }; + + cordovaRef.addConstructor(function() { + if (!window.plugins) { + window.plugins = {}; + } + if (!window.plugins.screenshot) { + window.plugins.screenshot = new Screenshot(); + } + }); + + })(); /* End of Temporary Scope. */ \ No newline at end of file diff --git a/Android/Screenshot/v2.0.0/src/org/apache/cordova/Screenshot.java b/Android/Screenshot/v2.0.0/src/org/apache/cordova/Screenshot.java new file mode 100644 index 0000000..651f217 --- /dev/null +++ b/Android/Screenshot/v2.0.0/src/org/apache/cordova/Screenshot.java @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2012 30ideas (http://30ide.as) + * MIT licensed + * + * @author Josemando Sobral + * @created Jul 2nd, 2012. + */ +package org.apache.cordova; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; + +import android.graphics.Bitmap; +import android.os.Environment; +import android.view.View; + +public class Screenshot extends Plugin { + + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + // starting on ICS, some WebView methods + // can only be called on UI threads + final Plugin that = this; + final String id = callbackId; + super.cordova.getActivity().runOnUiThread(new Runnable() { + //@Override + public void run() { + View view = webView.getRootView(); + + view.setDrawingCacheEnabled(true); + Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); + view.setDrawingCacheEnabled(false); + + try { + File folder = new File(Environment.getExternalStorageDirectory(), "Pictures"); + if (!folder.exists()) { + folder.mkdirs(); + } + + File f = new File(folder, "screenshot_" + System.currentTimeMillis() + ".png"); + + FileOutputStream fos = new FileOutputStream(f); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + that.success(new PluginResult(PluginResult.Status.OK), id); + + } catch (IOException e) { + that.success(new PluginResult(PluginResult.Status.IO_EXCEPTION, e.getMessage()), id); + } + } + }); + + PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT); + result.setKeepCallback(true); + return result; + } + +} diff --git a/Android/Screenshot/v2.0.0/www/Screenshot.js b/Android/Screenshot/v2.0.0/www/Screenshot.js new file mode 100644 index 0000000..7d07d9c --- /dev/null +++ b/Android/Screenshot/v2.0.0/www/Screenshot.js @@ -0,0 +1,35 @@ +/* + * This code is adapted from the work of Michael Nachbaur + * by Simon Madine of The Angry Robot Zombie Factory + * - Converted to Cordova 1.6.1 by Josemando Sobral. + * - Converted to Cordova 2.0.0 by Simon MacDonald + * 2012-07-03 + * MIT licensed + */ + +cordova.define("cordova/plugin/screenshot", function(require, exports, module) { + var exec = require('cordova/exec'); + + /** + * This class exposes the ability to take a Screenshot to JavaScript + */ + var Screenshot = function() {}; + + /** + * Save the screenshot to the user's Photo Library + */ + Screenshot.prototype.saveScreenshot = function() { + exec(null, null, "Screenshot", "saveScreenshot", []); + }; + + var screenshot = new Screenshot(); + module.exports = screenshot; + +}); + +if (!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.screenshot) { + window.plugins.screenshot = cordova.require("cordova/plugin/screenshot"); +} diff --git a/Android/Share/Share.java b/Android/Share/Share.java index c879feb..b60c33e 100644 --- a/Android/Share/Share.java +++ b/Android/Share/Share.java @@ -34,7 +34,7 @@ public class Share extends Plugin { sendIntent.setType("text/plain"); sendIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, text); - this.ctx.startActivity(sendIntent); + this.cordova.startActivityForResult(this, sendIntent, 0); } } diff --git a/Android/Share/share.js b/Android/Share/share.js index 71e0727..843268c 100644 --- a/Android/Share/share.js +++ b/Android/Share/share.js @@ -15,6 +15,9 @@ Share.prototype.show = function(content, success, fail) { }, 'Share', '', [content]); }; -cordova.addConstructor(function(){ - cordova.addPlugin('share', new Share()); -}); \ No newline at end of file +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.share) { + window.plugins.share = new Share(); +} diff --git a/Android/SpeechRecognizer/SpeechRecognizer.java b/Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.java similarity index 100% rename from Android/SpeechRecognizer/SpeechRecognizer.java rename to Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.java diff --git a/Android/SpeechRecognizer/SpeechRecognizer.js b/Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.js similarity index 100% rename from Android/SpeechRecognizer/SpeechRecognizer.js rename to Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.js diff --git a/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.java b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.java new file mode 100644 index 0000000..6d14c6d --- /dev/null +++ b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.java @@ -0,0 +1,199 @@ +/** + * SpeechRecognizer.java + * Speech Recognition PhoneGap plugin (Android) + * + * @author Colin Turner + * + * Copyright (c) 2011, Colin Turner + * + * MIT Licensed + */ +package com.urbtek.phonegap; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +import org.json.JSONArray; +import org.json.JSONObject; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.apache.cordova.api.PluginResult.Status; + +import android.util.Log; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.speech.RecognizerIntent; + +/** + * Style and such borrowed from the TTS and PhoneListener plugins + */ +public class SpeechRecognizer extends Plugin { + private static final String LOG_TAG = SpeechRecognizer.class.getSimpleName(); + public static final String ACTION_INIT = "init"; + public static final String ACTION_SPEECH_RECOGNIZE = "startRecognize"; + public static final String NOT_PRESENT_MESSAGE = "Speech recognition is not present or enabled"; + + private String speechRecognizerCallbackId = ""; + private boolean recognizerPresent = false; + + /* (non-Javadoc) + * @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String) + */ + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + // Dispatcher + if (ACTION_INIT.equals(action)) { + // init + if (DoInit()) + return new PluginResult(Status.OK); + else + return new PluginResult(Status.ERROR, NOT_PRESENT_MESSAGE); + } + else if (ACTION_SPEECH_RECOGNIZE.equals(action)) { + // recognize speech + if (!recognizerPresent) { + return new PluginResult(PluginResult.Status.ERROR, NOT_PRESENT_MESSAGE); + } + if (!this.speechRecognizerCallbackId.equals("")) { + return new PluginResult(PluginResult.Status.ERROR, "Speech recognition is in progress."); + } + + this.speechRecognizerCallbackId = callbackId; + startSpeechRecognitionActivity(args); + PluginResult res = new PluginResult(Status.NO_RESULT); + res.setKeepCallback(true); + return res; + } + else { + // Invalid action + String res = "Unknown action: " + action; + return new PluginResult(PluginResult.Status.INVALID_ACTION, res); + } + } + + /** + * Initialize the speech recognizer by checking if one exists. + */ + private boolean DoInit() { + this.recognizerPresent = IsSpeechRecognizerPresent(); + return this.recognizerPresent; + } + + /** + * Checks if a recognizer is present on this device + */ + private boolean IsSpeechRecognizerPresent() { + PackageManager pm = cordova.getActivity().getPackageManager(); + List activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0); + return !activities.isEmpty(); + } + + /** + * Fire an intent to start the speech recognition activity. + * + * @param args Argument array with the following string args: [req code][number of matches][prompt string] + */ + private void startSpeechRecognitionActivity(JSONArray args) { + int reqCode = 42; //Hitchhiker? + int maxMatches = 0; + String prompt = ""; + + try { + if (args.length() > 0) { + // Request code - passed back to the caller on a successful operation + String temp = args.getString(0); + reqCode = Integer.parseInt(temp); + } + if (args.length() > 1) { + // Maximum number of matches, 0 means the recognizer decides + String temp = args.getString(1); + maxMatches = Integer.parseInt(temp); + } + if (args.length() > 2) { + // Optional text prompt + prompt = args.getString(2); + } + } + catch (Exception e) { + Log.e(LOG_TAG, String.format("startSpeechRecognitionActivity exception: %s", e.toString())); + } + + Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH); + if (maxMatches > 0) + intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxMatches); + if (!prompt.equals("")) + intent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); + cordova.startActivityForResult(this, intent, reqCode); + } + + /** + * Handle the results from the recognition activity. + */ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + // Fill the list view with the strings the recognizer thought it could have heard + ArrayList matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); + float[] confidence = data.getFloatArrayExtra(RecognizerIntent.EXTRA_CONFIDENCE_SCORES); + + if (confidence != null) { + Log.d(LOG_TAG, "confidence length "+ confidence.length); + Iterator iterator = matches.iterator(); + int i = 0; + while(iterator.hasNext()) { + Log.d(LOG_TAG, "Match = " + iterator.next() + " confidence = " + confidence[i]); + i++; + } + } else { + Log.d(LOG_TAG, "No confidence" + + ""); + } + + ReturnSpeechResults(requestCode, matches); + } + else { + // Failure - Let the caller know + ReturnSpeechFailure(resultCode); + } + + super.onActivityResult(requestCode, resultCode, data); + } + + private void ReturnSpeechResults(int requestCode, ArrayList matches) { + boolean firstValue = true; + StringBuilder sb = new StringBuilder(); + sb.append("{\"speechMatches\": {"); + sb.append("\"requestCode\": "); + sb.append(Integer.toString(requestCode)); + sb.append(", \"speechMatch\": ["); + + Iterator iterator = matches.iterator(); + while(iterator.hasNext()) { + String match = iterator.next(); + + if (firstValue == false) + sb.append(", "); + firstValue = false; + sb.append(JSONObject.quote(match)); + } + sb.append("]}}"); + + PluginResult result = new PluginResult(PluginResult.Status.OK, sb.toString()); + result.setKeepCallback(false); + this.success(result, this.speechRecognizerCallbackId); + this.speechRecognizerCallbackId = ""; + } + + private void ReturnSpeechFailure(int resultCode) { + PluginResult result = new PluginResult(PluginResult.Status.ERROR, Integer.toString(resultCode)); + result.setKeepCallback(false); + this.error(result, this.speechRecognizerCallbackId); + this.speechRecognizerCallbackId = ""; + } +} \ No newline at end of file diff --git a/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.js b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.js new file mode 100644 index 0000000..809ff65 --- /dev/null +++ b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.js @@ -0,0 +1,60 @@ +/** + * SpeechRecognizer.js + * Speech Recognizer cordova plugin (Android) + * + * @author Colin Turner + * + * MIT Licensed + */ + +/** + * c'tor + */ +function SpeechRecognizer() { +} + +/** + * Initialize + * + * @param successCallback + * @param errorCallback + */ +SpeechRecognizer.prototype.init = function(successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "SpeechRecognizer", "init", []); +}; + +/** + * Recognize speech and return a list of matches + * + * @param successCallback + * @param errorCallback + * @param reqCode User-defined integer request code which will be returned when recognition is complete + * @param maxMatches The maximum number of matches to return. 0 means the service decides how many to return. + * @param promptString An optional string to prompt the user during recognition + */ +SpeechRecognizer.prototype.startRecognize = function(successCallback, errorCallback, reqCode, maxMatches, promptString) { + return cordova.exec(successCallback, errorCallback, "SpeechRecognizer", "startRecognize", [reqCode, maxMatches, promptString]); +}; + +/** + * Get the list of the supported languages in IETF BCP 47 format + * + * @param successCallback + * @param errorCallback + * + * Returns an array of codes in the success callback + */ +SpeechRecognizer.prototype.getSupportedLanguages = function(successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "SpeechRecognizer", "getSupportedLanguages", []); +}; + +/** + * Load + */ +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.speechrecognizer) { + window.plugins.speechrecognizer = new SpeechRecognizer(); +} + diff --git a/Android/StatusBarNotification/README.md b/Android/StatusBarNotification/README.md index c15a31c..2731567 100644 --- a/Android/StatusBarNotification/README.md +++ b/Android/StatusBarNotification/README.md @@ -1,51 +1,35 @@ -# StatusBarNotification plugin for Phonegap # +# StatusBarNotification plugin for Cordova # -Plugin allows you to display notifications in the status bar from your PhoneGap application. +This plugin allows you to display notifications in the status bar from your Cordova application. On Android you have to explicitly add things to the status bar (as opposed to iOS where push notifications automatically get displayed in the UI). The Android status bar is the UI component at the top of the screen that has a bunch of little icons. You can also drag the status bar down to view a list of notifications. ## Adding the Plugin to your project ## -Using this plugin requires [Android PhoneGap](http://github.com/phonegap/phonegap-android). +Using this plugin requires [Android Cordova](http://github.com/apache/incubator-cordova-android). -1. To install the plugin, move statusbarnotification.js to your project's www folder and include a reference to it in your html file after phonegap.js. +1. To install the plugin, move statusbarnotification.js to your project's www folder and include a reference to it in your html file after cordova.js. - <script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
+ <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="statusbarnotification.js"></script> -2. Create a directory within your project called "src/com/phonegap/plugins/statusBarNotification" and move StatusBarNotification.java into it. +2. Create a directory within your project called "src/com/phonegap/plugins/statusBarNotification" and move the .java files from this folder into it. -3. Add the following activity to your AndroidManifest.xml file. It should be added inside the <application> tag. - - <activity android:name="com.phonegap.DroidGap" android:label="@string/app_name">
- <intent-filter>
- </intent-filter>
- </activity> - -4. In your res/xml/plugins.xml file add the following line: +3. In your res/xml/plugins.xml file add the following line: <plugin name="StatusBarNotification" value="com.phonegap.plugins.statusBarNotification.StatusBarNotification"/> -5. You will need to add a nofication.png file to you applications res/drawable-ldpi, res/drawable-mdpi & res/drawable-hdpi directories. + CAUTION: Using PhoneGap ≥ 2.0 (aka Cordova) you have to add this line into res/xml/config.xml in the <plugins>-section. +The plugins.xml is no longer supported. The plugins are all located in the config.xml -6. You will need to add an import line like: + +4. You will need to add a notification.png file to your applications res/drawable-ldpi, res/drawable-mdpi & res/drawable-hdpi or res/drawable-xhdpi directories (depending on what resolutions you want to support). + +5. You will need to add an import line like this to the .java files (see commented out lines inside the files): import com.my.app.R; -Where com.my.app is your applications package name in order for this to compile. - ## Using the plugin ## -The plugin creates the object `window.plugins.statusBarNotification`. To use, call one of the following, available methods: - -
-  /**
-   * Displays new status bar notification
-   * 
-   * @param notificationTitle	The url to load
-   * @param notificationBody	Load url in PhoneGap webview [optional] - Default: false
-   */
-   
-  notify(notificationTitle, notificationBody);
-
+The plugin creates the object `window.plugins.statusBarNotification`. Sample use: @@ -54,6 +38,7 @@ Sample use: ## License ## Copyright (C) 2011 Dmitry Savchenko +Copyright (C) 2012 Max Ogden Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/Android/StatusBarNotification/StatusBarNotification.java b/Android/StatusBarNotification/StatusBarNotification.java index 8b523dd..74c58b9 100644 --- a/Android/StatusBarNotification/StatusBarNotification.java +++ b/Android/StatusBarNotification/StatusBarNotification.java @@ -1,116 +1,138 @@ /* -* -* Copyright (C) 2011 Dmitry Savchenko -* -* Permission is hereby granted, free of charge, to any person -* obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without -* restriction, including without limitation the rights to use, -* copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following -* conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -* -*/ + * + * Copyright (C) 2011 Dmitry Savchenko + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ package com.phonegap.plugins.statusBarNotification; +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; +import org.apache.cordova.api.PluginResult.Status; import org.json.JSONArray; import org.json.JSONException; import android.app.Notification; import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; + +import android.os.Handler; import android.util.Log; -import com.phonegap.api.Plugin; -import com.phonegap.api.PluginResult; -import com.phonegap.api.PluginResult.Status; - public class StatusBarNotification extends Plugin { - // Action to execute - public static final String NOTIFY = "notify"; - public static final String CLEAR = "clear"; - - /** - * Executes the request and returns PluginResult - * - * @param action Action to execute - * @param data JSONArray of arguments to the plugin - * @param callbackId The callback id used when calling back into JavaScript - * - * @return A PluginRequest object with a status - * */ - @Override - public PluginResult execute(String action, JSONArray data, String callbackId) { - String ns = Context.NOTIFICATION_SERVICE; - mNotificationManager = (NotificationManager) ctx.getSystemService(ns); - context = ctx.getApplicationContext(); - - PluginResult result = null; - if (NOTIFY.equals(action)) { - try { + // Action to execute + public static final String NOTIFY = "notify"; + public static final String CLEAR = "clear"; - String title = data.getString(0); - String body = data.getString(1); - Log.d("NotificationPlugin", "Notification: " + title + ", " + body); - showNotification(title, body); - result = new PluginResult(Status.OK); - } catch (JSONException jsonEx) { - Log.d("NotificationPlugin", "Got JSON Exception " - + jsonEx.getMessage()); - result = new PluginResult(Status.JSON_EXCEPTION); - } - } else if (CLEAR.equals(action)){ - clearNotification(); - } else { - result = new PluginResult(Status.INVALID_ACTION); - Log.d("NotificationPlugin", "Invalid action : "+action+" passed"); - } - return result; - } + /** + * Executes the request and returns PluginResult + * + * @param action Action to execute + * @param data JSONArray of arguments to the plugin + * @param callbackId The callback id used when calling back into JavaScript + * + * @return A PluginRequest object with a status + * */ + @Override + public PluginResult execute(String action, JSONArray data, String callbackId) { + PluginResult result = null; + if (NOTIFY.equals(action)) { + try { + String tag = data.getString(0); + String title = data.getString(1); + String body = data.getString(2); + Log.d("NotificationPlugin", "Notification: " + tag + ", " + title + ", " + body); + showNotification(tag, title, body); + result = new PluginResult(Status.OK); + } catch (JSONException jsonEx) { + Log.d("NotificationPlugin", "Got JSON Exception " + + jsonEx.getMessage()); + result = new PluginResult(Status.JSON_EXCEPTION); + } + } else if (CLEAR.equals(action)){ + try { + String tag = data.getString(0); + Log.d("NotificationPlugin", "Notification cancel: " + tag); + clearNotification(tag); + } catch (JSONException jsonEx) { + Log.d("NotificationPlugin", "Got JSON Exception " + jsonEx.getMessage()); + result = new PluginResult(Status.JSON_EXCEPTION); + } + } else { + result = new PluginResult(Status.INVALID_ACTION); + Log.d("NotificationPlugin", "Invalid action : "+action+" passed"); + } + return result; + } - /** - * Displays status bar notification - * - * @param contentTitle Notification title - * @param contentText Notification text - * */ - public void showNotification( CharSequence contentTitle, CharSequence contentText ) { - int icon = R.drawable.notification; - long when = System.currentTimeMillis(); - - Notification notification = new Notification(icon, contentTitle, when); - //notification.flags |= Notification.FLAG_NO_CLEAR; //Notification cannot be clearned by user - - Intent notificationIntent = new Intent(ctx, ctx.getClass()); - PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, notificationIntent, 0); - notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); - - mNotificationManager.notify(1, notification); - } - - /** - * Removes the Notification from status bar - */ - public void clearNotification() { - mNotificationManager.cancelAll(); - } - - private NotificationManager mNotificationManager; + /** + * Displays status bar notification + * + * @param tag Notification tag. + * @param contentTitle Notification title + * @param contentText Notification text + * */ + public void showNotification( CharSequence tag, CharSequence contentTitle, CharSequence contentText ) { + String ns = Context.NOTIFICATION_SERVICE; + context = cordova.getActivity().getApplicationContext(); + mNotificationManager = (NotificationManager) context.getSystemService(ns); + + Notification noti = StatusNotificationIntent.buildNotification(context, tag, contentTitle, contentText); + mNotificationManager.notify(tag.hashCode(), noti); + } + + /** + * Cancels a single notification by tag. + * + * @param tag Notification tag to cancel. + */ + public void clearNotification(String tag) { + mNotificationManager.cancel(tag.hashCode()); + } + + /** + * Removes all Notifications from the status bar. + */ + public void clearAllNotifications() { + mNotificationManager.cancelAll(); + } + + /** + * Called when a notification is clicked. + * @param intent The new Intent passed from the notification. + */ + @Override + public void onNewIntent(Intent intent) { + // The incoming Intent may or may not have been for a notification. + String tag = intent.getStringExtra("notificationTag"); + if (tag != null) { + sendJavascript("window.Notification.callOnclickByTag('"+ tag + "')"); + } + } + + + private NotificationManager mNotificationManager; private Context context; } diff --git a/Android/StatusBarNotification/StatusNotificationIntent.java b/Android/StatusBarNotification/StatusNotificationIntent.java new file mode 100644 index 0000000..d5314fb --- /dev/null +++ b/Android/StatusBarNotification/StatusNotificationIntent.java @@ -0,0 +1,27 @@ +// This class is used on all Androids below Honeycomb +package com.phonegap.plugins.statusBarNotification; +// import com.yourapp.R; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; + +public class StatusNotificationIntent { + public static Notification buildNotification( Context context, CharSequence tag, CharSequence contentTitle, CharSequence contentText ) { + int icon = R.drawable.notification; + long when = System.currentTimeMillis(); + Notification noti = new Notification(icon, contentTitle, when); + noti.flags |= Notification.FLAG_AUTO_CANCEL; + + PackageManager pm = context.getPackageManager(); + Intent notificationIntent = pm.getLaunchIntentForPackage(context.getPackageName()); + notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + notificationIntent.putExtra("notificationTag", tag); + + PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); + noti.setLatestEventInfo(context, contentTitle, contentText, contentIntent); + return noti; + } +} diff --git a/Android/StatusBarNotification/drawable-hdpi/notification.png b/Android/StatusBarNotification/drawable-hdpi/notification.png new file mode 100644 index 0000000..00e8f8a Binary files /dev/null and b/Android/StatusBarNotification/drawable-hdpi/notification.png differ diff --git a/Android/StatusBarNotification/drawable-mdpi/notification.png b/Android/StatusBarNotification/drawable-mdpi/notification.png new file mode 100644 index 0000000..b41ccd0 Binary files /dev/null and b/Android/StatusBarNotification/drawable-mdpi/notification.png differ diff --git a/Android/StatusBarNotification/drawable-xhdpi/notification.png b/Android/StatusBarNotification/drawable-xhdpi/notification.png new file mode 100644 index 0000000..15182b9 Binary files /dev/null and b/Android/StatusBarNotification/drawable-xhdpi/notification.png differ diff --git a/Android/StatusBarNotification/statusbarnotification.js b/Android/StatusBarNotification/statusbarnotification.js index ea29f40..4a546ba 100644 --- a/Android/StatusBarNotification/statusbarnotification.js +++ b/Android/StatusBarNotification/statusbarnotification.js @@ -25,36 +25,111 @@ * */ -/** - * - * Constructor - */ -var NotificationMessenger = function() { -} +var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + +/** @deprecated Use the W3C standard window.Notification API instead. */ +var NotificationMessenger = function() { } /** * @param title Title of the notification * @param body Body of the notification + * @deprecated Use the W3C standard window.Notification API instead. */ NotificationMessenger.prototype.notify = function(title, body) { - return PhoneGap.exec(null, null, 'StatusBarNotification', 'notify', [title, body]); -}; + if (window.Notification) { + this.activeNotification = new window.Notification(title, { + body: body + }); + } +} /** * Clears the Notificaiton Bar + * @deprecated Use the W3C standard window.Notification API instead. */ NotificationMessenger.prototype.clear = function() { - return PhoneGap.exec(null, null, 'StatusBarNotification', 'clear', []); -}; + if (this.activeNotification) { + this.activeNotification.close(); + this.activeNotification = undefined; + } +} -/** - * Load StatusBarNotification - * */ +if (!window.plugins) window.plugins = {} +if (!window.plugins.statusBarNotification) window.plugins.statusBarNotification = new NotificationMessenger(); -PhoneGap.addConstructor(function() { - PhoneGap.addPlugin('statusBarNotification', new NotificationMessenger()); - -// @deprecated: No longer needed in PhoneGap 1.0. Uncomment the addService code for earlier -// PhoneGap releases. -// PluginManager.addService("StatusBarNotificationPlugin","com.trial.phonegap.plugin.directorylisting.StatusBarNotificationPlugin"); -}); + +/* + * The W3C standard API, window.Notification. See http://www.w3.org/TR/notifications/ + * This API should be used for new applications instead of the old plugin API above. + */ +if (typeof window.Notification == 'undefined') { + + /** + * Creates and shows a new notification. + * @param title + * @param options + */ + window.Notification = function(title, options) { + options = options || {}; + this.tag = options.tag || 'defaultTag'; + + // Add this notification to the global index by tag. + window.Notification.active[this.tag] = this; + + // May be undefined. + this.onclick = options.onclick; + this.onerror = options.onerror; + this.onshow = options.onshow; + this.onclose = options.onclose; + + var content = options.body || ''; + + cordova.exec(function() { + if (this.onshow) { + this.onshow(); + } + }, function(error) { + if (this.onerror) { + this.onerror(error); + } + }, 'StatusBarNotification', 'notify', [this.tag, title, content]); + }; + + // Permission is always granted on Android. + window.Notification.permission = 'granted'; + + window.Notification.requestPermission = function(callback) { + callback('granted'); + }; + + // Not part of the W3C API. Used by the native side to call onclick handlers. + window.Notification.callOnclickByTag = function(tag) { + console.log('callOnclickByTag'); + var notification = window.Notification.active[tag]; + if (notification && notification.onclick && typeof notification.onclick == 'function') { + console.log('inside if'); + notification.onclick(); + } + }; + + // A global map of notifications by tag, so their onclick callbacks can be called. + window.Notification.active = {}; + + + /** + * Cancels a notification that has already been created and shown to the user. + */ + window.Notification.prototype.close = function() { + cordova.exec(function() { + if (this.onclose) { + this.onclose(); + } + }, function(error) { + if (this.onerror) { + this.onerror(error); + } + }, 'StatusBarNotification', 'clear', [this.tag]); + }; +} + +// vim: tabstop=4:softtabstop=4:shiftwidth=4:expandtab diff --git a/Android/TTS/1.8.1/LICENSE b/Android/TTS/1.8.1/LICENSE new file mode 100644 index 0000000..3da82a1 --- /dev/null +++ b/Android/TTS/1.8.1/LICENSE @@ -0,0 +1,64 @@ +PhoneGap is available under *either* the terms of the modified BSD license *or* the +MIT License (2008). As a recipient of PhonegGap, you may choose which +license to receive this code under (except as noted in per-module LICENSE +files). Some modules may not be the copyright of Nitobi. These +modules contain explicit declarations of copyright in both the LICENSE files in +the directories in which they reside and in the code itself. No external +contributions are allowed under licenses which are fundamentally incompatible +with the MIT or BSD licenses that PhoneGap is distributed under. + +The text of the MIT and BSD licenses is reproduced below. + +------------------------------------------------------------------------------- +The "New" BSD License: +********************** + +Copyright (c) 2005-2010, Nitobi Software Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Phonegap/Nitobi nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +The MIT License +***************** + +Copyright (c) <2010> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. \ No newline at end of file diff --git a/Android/TTS/config.xml b/Android/TTS/1.8.1/config.xml similarity index 100% rename from Android/TTS/config.xml rename to Android/TTS/1.8.1/config.xml diff --git a/Android/TTS/docs/TTS.md b/Android/TTS/1.8.1/docs/TTS.md similarity index 100% rename from Android/TTS/docs/TTS.md rename to Android/TTS/1.8.1/docs/TTS.md diff --git a/Android/TTS/manifest b/Android/TTS/1.8.1/manifest similarity index 100% rename from Android/TTS/manifest rename to Android/TTS/1.8.1/manifest diff --git a/Android/TTS/src/com/phonegap/plugins/speech/TTS.java b/Android/TTS/1.8.1/src/com/phonegap/plugins/speech/TTS.java similarity index 100% rename from Android/TTS/src/com/phonegap/plugins/speech/TTS.java rename to Android/TTS/1.8.1/src/com/phonegap/plugins/speech/TTS.java diff --git a/Android/TTS/www/tts.js b/Android/TTS/1.8.1/www/tts.js similarity index 100% rename from Android/TTS/www/tts.js rename to Android/TTS/1.8.1/www/tts.js diff --git a/Android/TTS/2.0.0/LICENSE b/Android/TTS/2.0.0/LICENSE new file mode 100644 index 0000000..3da82a1 --- /dev/null +++ b/Android/TTS/2.0.0/LICENSE @@ -0,0 +1,64 @@ +PhoneGap is available under *either* the terms of the modified BSD license *or* the +MIT License (2008). As a recipient of PhonegGap, you may choose which +license to receive this code under (except as noted in per-module LICENSE +files). Some modules may not be the copyright of Nitobi. These +modules contain explicit declarations of copyright in both the LICENSE files in +the directories in which they reside and in the code itself. No external +contributions are allowed under licenses which are fundamentally incompatible +with the MIT or BSD licenses that PhoneGap is distributed under. + +The text of the MIT and BSD licenses is reproduced below. + +------------------------------------------------------------------------------- +The "New" BSD License: +********************** + +Copyright (c) 2005-2010, Nitobi Software Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Phonegap/Nitobi nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +The MIT License +***************** + +Copyright (c) <2010> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. \ No newline at end of file diff --git a/Android/TTS/2.0.0/config.xml b/Android/TTS/2.0.0/config.xml new file mode 100644 index 0000000..55b78c8 --- /dev/null +++ b/Android/TTS/2.0.0/config.xml @@ -0,0 +1,33 @@ + + + + + The TTS class that allows you to access the devices TTS services. + + + +The TTS class that allows you to access the devices TTS services. + +A simple use case would be: + +- Playing text passed into the service out as synthesized speech + + + Simon MacDonald + + +PhoneGap is available under *either* the terms of the modified BSD license *or* the +MIT License (2008). As a recipient of PhonegGap, you may choose which +license to receive this code under (except as noted in per-module LICENSE +files). Some modules may not be the copyright of Nitobi. These +modules contain explicit declarations of copyright in both the LICENSE files in +the directories in which they reside and in the code itself. No external +contributions are allowed under licenses which are fundamentally incompatible +with the MIT or BSD licenses that PhoneGap is distributed under. + + \ No newline at end of file diff --git a/Android/TTS/2.0.0/docs/TTS.md b/Android/TTS/2.0.0/docs/TTS.md new file mode 100755 index 0000000..0729fa8 --- /dev/null +++ b/Android/TTS/2.0.0/docs/TTS.md @@ -0,0 +1,127 @@ +TTS +========== + +The TTS class that allows you to access the devices TTS services. + +Properties +---------- + +N/A + +Methods +------- + +- __startup__: starts the TTS service. +- __shutdown__: stops the TTS service. +- __speak__: speaks the specified text. +- __silence__: plays silence for the specified number of ms. +- __getLanguage__: gets the current TTS language. +- __setLanguage__: sets the current TTS language. +- __isLanguageAvailable__: finds out if TTS supports the language. + + +Details +------- + +The TTS class is a way to have your application read out text in a machine generated format. + + + +Supported Platforms +------------------- + +- Android + +Quick Example +------------------------------ + + window.plugins.tts.startup(startupWin, startupFail); + + function startupWin(result) { + // When result is equal to STARTED we are ready to play + if (result == TTS.STARTED) { + window.plugins.tts.speak("The text to speech service is ready"); + } + } + + function startupFail(result) { + console.log("Startup failure = " + result); + } + +Full Example +------------ + + + + + PhoneGap Events Example + + + + + + +

TTS Example

+
+ + Speak
+ test + + \ No newline at end of file diff --git a/Android/TTS/2.0.0/manifest b/Android/TTS/2.0.0/manifest new file mode 100644 index 0000000..945a250 --- /dev/null +++ b/Android/TTS/2.0.0/manifest @@ -0,0 +1,3 @@ +lib/commons-net-2.2.jar +src/com/phonegap/plugins/FtpClient.java +www/ftpclient.js \ No newline at end of file diff --git a/Android/TTS/2.0.0/src/com/phonegap/plugins/speech/TTS.java b/Android/TTS/2.0.0/src/com/phonegap/plugins/speech/TTS.java new file mode 100644 index 0000000..c28101e --- /dev/null +++ b/Android/TTS/2.0.0/src/com/phonegap/plugins/speech/TTS.java @@ -0,0 +1,210 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2011, IBM Corporation + * + * Modified by Murray Macdonald (murray@workgroup.ca) on 2012/05/30 to add support for stop(), pitch(), speed() and interrupt(); + * + */ + +package com.phonegap.plugins.speech; + +import java.util.HashMap; +import java.util.Locale; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.OnInitListener; +import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; + +public class TTS extends Plugin implements OnInitListener, OnUtteranceCompletedListener { + + private static final String LOG_TAG = "TTS"; + private static final int STOPPED = 0; + private static final int INITIALIZING = 1; + private static final int STARTED = 2; + private TextToSpeech mTts = null; + private int state = STOPPED; + + private String startupCallbackId = ""; + + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("speak")) { + String text = args.getString(0); + if (isReady()) { + HashMap map = null; + map = new HashMap(); + map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackId); + mTts.speak(text, TextToSpeech.QUEUE_ADD, map); + PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT); + pr.setKeepCallback(true); + return pr; + } else { + JSONObject error = new JSONObject(); + error.put("message","TTS service is still initialzing."); + error.put("code", TTS.INITIALIZING); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } else if (action.equals("interrupt")) { + String text = args.getString(0); + if (isReady()) { + HashMap map = null; + map = new HashMap(); + map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackId); + mTts.speak(text, TextToSpeech.QUEUE_FLUSH, map); + PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT); + pr.setKeepCallback(true); + return pr; + } else { + JSONObject error = new JSONObject(); + error.put("message","TTS service is still initialzing."); + error.put("code", TTS.INITIALIZING); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } else if (action.equals("stop")) { + if (isReady()) { + mTts.stop(); + return new PluginResult(status, result); + } else { + JSONObject error = new JSONObject(); + error.put("message","TTS service is still initialzing."); + error.put("code", TTS.INITIALIZING); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } else if (action.equals("silence")) { + if (isReady()) { + mTts.playSilence(args.getLong(0), TextToSpeech.QUEUE_ADD, null); + return new PluginResult(status, result); + } else { + JSONObject error = new JSONObject(); + error.put("message","TTS service is still initialzing."); + error.put("code", TTS.INITIALIZING); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } else if (action.equals("speed")) { + if (isReady()) { + float speed= (float) (args.optLong(0, 100)) /(float) 100.0; + mTts.setSpeechRate(speed); + return new PluginResult(status, result); + } else { + JSONObject error = new JSONObject(); + error.put("message","TTS service is still initialzing."); + error.put("code", TTS.INITIALIZING); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } else if (action.equals("pitch")) { + if (isReady()) { + float pitch= (float) (args.optLong(0, 100)) /(float) 100.0; + mTts.setPitch(pitch); + return new PluginResult(status, result); + } else { + JSONObject error = new JSONObject(); + error.put("message","TTS service is still initialzing."); + error.put("code", TTS.INITIALIZING); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } else if (action.equals("startup")) { + if (mTts == null) { + this.startupCallbackId = callbackId; + state = TTS.INITIALIZING; + mTts = new TextToSpeech(cordova.getActivity().getApplicationContext(), this); + } + PluginResult pluginResult = new PluginResult(status, TTS.INITIALIZING); + pluginResult.setKeepCallback(true); + return pluginResult; + } + else if (action.equals("shutdown")) { + if (mTts != null) { + mTts.shutdown(); + } + return new PluginResult(status, result); + } + else if (action.equals("getLanguage")) { + if (mTts != null) { + result = mTts.getLanguage().toString(); + return new PluginResult(status, result); + } + } + else if (action.equals("isLanguageAvailable")) { + if (mTts != null) { + Locale loc = new Locale(args.getString(0)); + int available = mTts.isLanguageAvailable(loc); + result = (available < 0) ? "false" : "true"; + return new PluginResult(status, result); + } + } + else if (action.equals("setLanguage")) { + if (mTts != null) { + Locale loc = new Locale(args.getString(0)); + int available = mTts.setLanguage(loc); + result = (available < 0) ? "false" : "true"; + return new PluginResult(status, result); + } + } + return new PluginResult(status, result); + } catch (JSONException e) { + e.printStackTrace(); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + /** + * Is the TTS service ready to play yet? + * + * @return + */ + private boolean isReady() { + return (state == TTS.STARTED) ? true : false; + } + + /** + * Called when the TTS service is initialized. + * + * @param status + */ + public void onInit(int status) { + if (status == TextToSpeech.SUCCESS) { + state = TTS.STARTED; + PluginResult result = new PluginResult(PluginResult.Status.OK, TTS.STARTED); + result.setKeepCallback(false); + this.success(result, this.startupCallbackId); + mTts.setOnUtteranceCompletedListener(this); + } + else if (status == TextToSpeech.ERROR) { + state = TTS.STOPPED; + PluginResult result = new PluginResult(PluginResult.Status.ERROR, TTS.STOPPED); + result.setKeepCallback(false); + this.error(result, this.startupCallbackId); + } + } + + /** + * Clean up the TTS resources + */ + public void onDestroy() { + if (mTts != null) { + mTts.shutdown(); + } + } + + /** + * Once the utterance has completely been played call the speak's success callback + */ + public void onUtteranceCompleted(String utteranceId) { + PluginResult result = new PluginResult(PluginResult.Status.OK); + result.setKeepCallback(false); + this.success(result, utteranceId); + } +} \ No newline at end of file diff --git a/Android/TTS/2.0.0/www/tts.js b/Android/TTS/2.0.0/www/tts.js new file mode 100644 index 0000000..b09b5c9 --- /dev/null +++ b/Android/TTS/2.0.0/www/tts.js @@ -0,0 +1,153 @@ +/* + * cordova is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2011, IBM Corporation + */ +/* + * cordova is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2011, IBM Corporation + * + * Modified by Murray Macdonald (murray@workgroup.ca) on 2012/05/30 to add pitch(), speed(), stop(), and interrupt() methods. + */ + +/** + * Constructor + */ +function TTS() { +} + +TTS.STOPPED = 0; +TTS.INITIALIZING = 1; +TTS.STARTED = 2; + +/** + * Play the passed in text as synthesized speech + * + * @param {DOMString} text + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.speak = function(text, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "speak", [text]); +}; + +/** + * Interrupt any existing speech, then speak the passed in text as synthesized speech + * + * @param {DOMString} text + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.interrupt = function(text, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "interrupt", [text]); +}; + +/** + * Stop any queued synthesized speech + * + * @param {DOMString} text + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.stop= function(successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "stop", []); +}; + +/** + * Play silence for the number of ms passed in as duration + * + * @param {long} duration + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.silence = function(duration, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "silence", [duration]); +}; + +/** + * Set speed of speech. Usable from 30 to 500. Higher values make little difference. + * + * @param {long} speed + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.speed = function(speed, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "speed", [speed]); +}; + +/** + * Set pitch of speech. Useful values are approximately 30 - 300 + * + * @param {long} pitch + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.pitch = function(pitch, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "pitch", [pitch]); +}; + +/** + * Starts up the TTS Service + * + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.startup = function(successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "startup", []); +}; + +/** + * Shuts down the TTS Service if you no longer need it. + * + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.shutdown = function(successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "shutdown", []); +}; + +/** + * Finds out if the language is currently supported by the TTS service. + * + * @param {DOMSting} lang + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.isLanguageAvailable = function(lang, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "isLanguageAvailable", [lang]); +}; + +/** + * Finds out the current language of the TTS service. + * + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.getLanguage = function(successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "getLanguage", []); +}; + +/** + * Sets the language of the TTS service. + * + * @param {DOMString} lang + * @param {Object} successCallback + * @param {Object} errorCallback + */ +TTS.prototype.setLanguage = function(lang, successCallback, errorCallback) { + return cordova.exec(successCallback, errorCallback, "TTS", "setLanguage", [lang]); +}; + +/** + * Load TTS + */ + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.tts) { + window.plugins.tts = new TTS(); +} diff --git a/Android/Torch/README.md b/Android/Torch/1.9/README.md similarity index 100% rename from Android/Torch/README.md rename to Android/Torch/1.9/README.md diff --git a/Android/Torch/Torch.js b/Android/Torch/1.9/Torch.js similarity index 100% rename from Android/Torch/Torch.js rename to Android/Torch/1.9/Torch.js diff --git a/Android/Torch/TorchPlugin.java b/Android/Torch/1.9/TorchPlugin.java similarity index 100% rename from Android/Torch/TorchPlugin.java rename to Android/Torch/1.9/TorchPlugin.java diff --git a/Android/Torch/2.1/README.md b/Android/Torch/2.1/README.md new file mode 100644 index 0000000..07e8565 --- /dev/null +++ b/Android/Torch/2.1/README.md @@ -0,0 +1,91 @@ +# Torch plugin for Phonegap (Android) # +By Arne de Bree + +## Adding the Plugin to your project ## +1. To install the plugin, move `Torch.js` to your project's www folder and include a reference to it +in your html files. + + <script src="Torch.js"></script> + +2. Create a folder called 'nl/debree/phonegap/plugin/torch' within your project's src folder. +3. And copy the java file into that new folder. + +
+    mkdir -p /src/nl/debree/phonegap/plugin/torch/
+    cp ./TorchPlugin.java /src/nl/debree/phonegap/plugin/torch/
+
+ +4. Add a plugin line to `res/xml/plugins.xml` + + <plugin name="Torch" value="nl.debree.phonegap.plugin.torch.TorchPlugin" /> + +## Using the plugin ## +The plugin creates the object `window.plugins.Torch` within your DOM. This object +exposes the following functions: + +- isOn +- isCapable +- toggle +- turnOn +- turnOff + +
+    window.plugins.Torch.isOn( 
+        function( result ) { console.log( "isOn: " + result.on ) }      // success
+    ,   function() { console.log( "error" ) }                           // error
+    );
+    
+    window.plugins.Torch.isCapable( 
+        function( result ) { console.log( "isCapable: " + result.capable ) }      // success
+    ,   function() { console.log( "error" ) }                           // error
+    );
+    
+    window.plugins.Torch.toggle( 
+        function() { console.log( "toggle" ) }                          // success
+    ,   function() { console.log( "error" ) }                           // error
+    );
+
+    window.plugins.Torch.turnOn( 
+        function() { console.log( "turnOn" ) }                          // success
+    ,   function() { console.log( "error" ) }                           // error
+    );
+
+    window.plugins.Torch.turnOff( 
+        function() { console.log( "turnOff" ) }                         // success
+    ,   function() { console.log( "error" ) }                           // error
+    );
+
+ + +## BUGS AND CONTRIBUTIONS ## +The latest bleeding-edge version is available [on GitHub](http://github.com/adebrees/phonegap-plugins/tree/master/Android/) +If you have a patch, fork my repo baby and send me a pull request. Submit bug reports on GitHub, please. + +## Licence ## + +The MIT License + +Copyright (c) 2011 Arne de Bree + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + + \ No newline at end of file diff --git a/Android/Torch/2.1/Torch.js b/Android/Torch/2.1/Torch.js new file mode 100644 index 0000000..90a3ad7 --- /dev/null +++ b/Android/Torch/2.1/Torch.js @@ -0,0 +1,68 @@ +/** + * Phonegap Torch plugin + * Copyright (c) Arne de Bree 2011 + * + */ + +/** + * + * @return Object literal singleton instance of Torch + */ +var Torch = function() {}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.isCapable = function( success, error ) +{ + return cordova.exec( success, error, "Torch", "isCapable", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.isOn = function( success, error ) +{ + return cordova.exec( success, error, "Torch", "isOn", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.turnOn = function( success, error ) +{ + return cordova.exec( success, error, "Torch", "turnOn", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.turnOff = function( success, error ) +{ + return cordova.exec( success, error, "Torch", "turnOff", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.toggle = function( success, error ) +{ + return cordova.exec( success, error, "Torch", "toggle", [] ); +}; + +/** + * Load Analytics + */ + +if(!window.plugins) { + window.plugins = {}; +} + +if (!window.plugins.Torch) { + window.plugins.Torch = new Torch(); +} diff --git a/Android/Torch/2.1/Torch.js~ b/Android/Torch/2.1/Torch.js~ new file mode 100644 index 0000000..a7d4650 --- /dev/null +++ b/Android/Torch/2.1/Torch.js~ @@ -0,0 +1,61 @@ +/** + * Phonegap Torch plugin + * Copyright (c) Arne de Bree 2011 + * + */ + +/** + * + * @return Object literal singleton instance of Torch + */ +var Torch = function() {}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.isCapable = function( success, error ) +{ + return Cordova.exec( success, error, "Torch", "isCapable", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.isOn = function( success, error ) +{ + return Cordova.exec( success, error, "Torch", "isOn", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.turnOn = function( success, error ) +{ + return Cordova.exec( success, error, "Torch", "turnOn", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.turnOff = function( success, error ) +{ + return Cordova.exec( success, error, "Torch", "turnOff", [] ); +}; + +/** + * @param success The callback for success + * @param error The callback for error + */ +Torch.prototype.toggle = function( success, error ) +{ + return Cordova.exec( success, error, "Torch", "toggle", [] ); +}; + +Cordova.addConstructor( function() +{ + Cordova.addPlugin( "Torch", new Torch() ); +} ); diff --git a/Android/Torch/2.1/TorchPlugin.java b/Android/Torch/2.1/TorchPlugin.java new file mode 100644 index 0000000..09557dd --- /dev/null +++ b/Android/Torch/2.1/TorchPlugin.java @@ -0,0 +1,154 @@ +/** + * Phonegap Torch Plugin + * Copyright (c) Arne de Bree 2011 + * + */ +package nl.debree.phonegap.plugin.torch; + +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import com.phonegap.api.Plugin; +import com.phonegap.api.PluginResult; +import com.phonegap.api.PluginResult.Status; + +import android.hardware.Camera; +import android.util.Log; + +/** + * Plugin to turn on or off the Camera Flashlight of an Android device + * after the capability is tested + */ +public class TorchPlugin extends Plugin { + + public static final String CMD_ON = "turnOn"; + public static final String CMD_OFF = "turnOff"; + public static final String CMD_TOGGLE = "toggle"; + public static final String CMD_IS_ON = "isOn"; + public static final String CMD_HAS_TORCH = "isCapable"; + + // Create camera and parameter objects + private Camera mCamera; + private Camera.Parameters mParameters; + private boolean mbTorchEnabled = false; + + /** + * Constructor + */ + public TorchPlugin() { + Log.d( "TorchPlugin", "Plugin created" ); + + mCamera = Camera.open(); + } + + /* + * Executes the request and returns PluginResult. + * + * @param action action to perform. Allowed values: turnOn, turnOff, toggle, isOn, isCapable + * @param data input data, currently not in use + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + * + * @see com.phonegap.api.Plugin#execute(java.lang.String, + * org.json.JSONArray, java.lang.String) + */ + @Override + public PluginResult execute(String action, JSONArray data, String callbackId) { + Log.d( "TorchPlugin", "Plugin Called " + action ); + + PluginResult result = null; + JSONObject response = new JSONObject(); + + if (action.equals(CMD_ON)) { + + this.toggleTorch( true ); + result = new PluginResult( Status.OK ); + + } else if (action.equals(CMD_OFF)) { + + this.toggleTorch( false ); + result = new PluginResult( Status.OK ); + + } else if (action.equals(CMD_TOGGLE)) { + + this.toggleTorch(); + result = new PluginResult( Status.OK ); + + } else if (action.equals(CMD_IS_ON)) { + try { + response.put( "on", mbTorchEnabled ); + + result = new PluginResult( Status.OK, response ); + } catch( JSONException jsonEx ) { + result = new PluginResult(Status.JSON_EXCEPTION); + } + } else if (action.equals(CMD_HAS_TORCH)) { + try { + response.put( "capable", this.isCapable() ); + + result = new PluginResult( Status.OK, response ); + } catch( JSONException jsonEx ) { + result = new PluginResult(Status.JSON_EXCEPTION); + } + + } else { + result = new PluginResult(Status.INVALID_ACTION); + Log.d( "TorchPlugin", "Invalid action : " + action + " passed"); + } + + return result; + } + + /** + * Test if this device has a Flashlight we can use and put in Torch mode + * + * @return boolean + */ + protected boolean isCapable() { + boolean result = false; + + List flashModes = mParameters.getSupportedFlashModes(); + + if (flashModes != null && flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) { + result = true; + } + + return result; + } + + /** + * True toggle function, turns the torch on when off and vise versa + * + */ + protected void toggleTorch() { + toggleTorch( !mbTorchEnabled ); + } + + /** + * Toggle the torch in the requested state + * + * @param state The requested state + * + */ + protected void toggleTorch(boolean state) { + mParameters = mCamera.getParameters(); + + // Make sure that torch mode is supported + // + if ( this.isCapable() ) { + if (state) { + mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); + } else { + mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON); + } + + // Commit the camera parameters + // + mCamera.setParameters(mParameters); + + mbTorchEnabled = state; + } + } +} \ No newline at end of file diff --git a/Android/VideoPlayer/src/com/phonegap/plugins/video/VideoPlayer.java b/Android/VideoPlayer/1.8.1/src/com/phonegap/plugins/video/VideoPlayer.java similarity index 100% rename from Android/VideoPlayer/src/com/phonegap/plugins/video/VideoPlayer.java rename to Android/VideoPlayer/1.8.1/src/com/phonegap/plugins/video/VideoPlayer.java diff --git a/Android/VideoPlayer/www/video.js b/Android/VideoPlayer/1.8.1/www/video.js similarity index 100% rename from Android/VideoPlayer/www/video.js rename to Android/VideoPlayer/1.8.1/www/video.js diff --git a/Android/VideoPlayer/2.0.0/src/com/phonegap/plugins/video/VideoPlayer.java b/Android/VideoPlayer/2.0.0/src/com/phonegap/plugins/video/VideoPlayer.java new file mode 100644 index 0000000..8e1d810 --- /dev/null +++ b/Android/VideoPlayer/2.0.0/src/com/phonegap/plugins/video/VideoPlayer.java @@ -0,0 +1,102 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2011, IBM Corporation + */ + +package com.phonegap.plugins.video; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.json.JSONArray; +import org.json.JSONException; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import org.apache.cordova.api.Plugin; +import org.apache.cordova.api.PluginResult; + +public class VideoPlayer extends Plugin { + private static final String YOU_TUBE = "youtube.com"; + private static final String ASSETS = "file:///android_asset/"; + + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("playVideo")) { + playVideo(args.getString(0)); + } + else { + status = PluginResult.Status.INVALID_ACTION; + } + return new PluginResult(status, result); + } catch (JSONException e) { + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } catch (IOException e) { + return new PluginResult(PluginResult.Status.IO_EXCEPTION); + } + } + + private void playVideo(String url) throws IOException { + // Create URI + Uri uri = Uri.parse(url); + + Intent intent = null; + // Check to see if someone is trying to play a YouTube page. + if (url.contains(YOU_TUBE)) { + // If we don't do it this way you don't have the option for youtube + uri = Uri.parse("vnd.youtube:" + uri.getQueryParameter("v")); + intent = new Intent(Intent.ACTION_VIEW, uri); + } else if(url.contains(ASSETS)) { + // get file path in assets folder + String filepath = url.replace(ASSETS, ""); + // get actual filename from path as command to write to internal storage doesn't like folders + String filename = filepath.substring(filepath.lastIndexOf("/")+1, filepath.length()); + + // Don't copy the file if it already exists + File fp = new File(this.cordova.getActivity().getFilesDir() + "/" + filename); + if (!fp.exists()) { + this.copy(filepath, filename); + } + + // change uri to be to the new file in internal storage + uri = Uri.parse("file://" + this.cordova.getActivity().getFilesDir() + "/" + filename); + + // Display video player + intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, "video/*"); + } else { + // Display video player + intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, "video/*"); + } + + this.cordova.getActivity().startActivity(intent); + } + + private void copy(String fileFrom, String fileTo) throws IOException { + // get file to be copied from assets + InputStream in = this.cordova.getActivity().getAssets().open(fileFrom); + // get file where copied too, in internal storage. + // must be MODE_WORLD_READABLE or Android can't play it + FileOutputStream out = this.cordova.getActivity().openFileOutput(fileTo, Context.MODE_WORLD_READABLE); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) + out.write(buf, 0, len); + in.close(); + out.close(); + } +} diff --git a/Android/VideoPlayer/2.0.0/www/video.js b/Android/VideoPlayer/2.0.0/www/video.js new file mode 100644 index 0000000..b92ba5e --- /dev/null +++ b/Android/VideoPlayer/2.0.0/www/video.js @@ -0,0 +1,33 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2011, IBM Corporation + */ + +/** + * Constructor + */ +function VideoPlayer() { +}; + +/** + * Starts the video player intent + * + * @param url The url to play + */ +VideoPlayer.prototype.play = function(url) { + cordova.exec(null, null, "VideoPlayer", "playVideo", [url]); +}; + +/** + * Load VideoPlayer + */ + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.videoPlayer) { + window.plugins.videoPlayer = new VideoPlayer(); +} diff --git a/Android/VideoPlayer/README.md b/Android/VideoPlayer/README.md index e7d157e..2c4732b 100755 --- a/Android/VideoPlayer/README.md +++ b/Android/VideoPlayer/README.md @@ -6,7 +6,7 @@ This command fires an Intent to have your devices video player show the video. ## Adding the Plugin to your project ## -Using this plugin requires [Android PhoneGap](http://github.com/phonegap/phonegap-android). +Using this plugin requires [Android PhoneGap](https://github.com/apache/incubator-cordova-android). 1. To install the plugin, move www/video to your project's www folder and include a reference to it in your html file after phonegap.js. @@ -21,7 +21,7 @@ Using this plugin requires [Android PhoneGap](http://github.com/phonegap/phonega ## Using the plugin ## -The plugin creates the object `window.plugins.video`. To use, call the play() method: +The plugin creates the object `window.plugins.videoPlayer`. To use, call the play() method:
   /**
diff --git a/Android/WebIntent/WebIntent.java b/Android/WebIntent/WebIntent.java
index 9e7ea29..c6bdee9 100644
--- a/Android/WebIntent/WebIntent.java
+++ b/Android/WebIntent/WebIntent.java
@@ -13,8 +13,8 @@ import android.net.Uri;
 import android.util.Log;
 import android.text.Html;
 
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
 
 /**
  * WebIntent is a PhoneGap plugin that bridges Android intents and web
@@ -73,7 +73,7 @@ public class WebIntent extends Plugin {
                 if (args.length() != 1) {
                     return new PluginResult(PluginResult.Status.INVALID_ACTION);
                 }
-                Intent i = ((DroidGap) this.ctx).getIntent();
+                Intent i = ((DroidGap)this.cordova.getContext()).getIntent();
                 String extraName = args.getString(0);
                 return new PluginResult(PluginResult.Status.OK, i.hasExtra(extraName));
 
@@ -81,7 +81,7 @@ public class WebIntent extends Plugin {
                 if (args.length() != 1) {
                     return new PluginResult(PluginResult.Status.INVALID_ACTION);
                 }
-                Intent i = ((DroidGap) this.ctx).getIntent();
+                Intent i = ((DroidGap)this.cordova.getContext()).getIntent();
                 String extraName = args.getString(0);
                 if (i.hasExtra(extraName)) {
                     return new PluginResult(PluginResult.Status.OK, i.getStringExtra(extraName));
@@ -93,7 +93,7 @@ public class WebIntent extends Plugin {
                     return new PluginResult(PluginResult.Status.INVALID_ACTION);
                 }
 
-                Intent i = ((DroidGap) this.ctx).getIntent();
+                Intent i = ((DroidGap)this.cordova.getContext()).getIntent();
                 String uri = i.getDataString();
                 return new PluginResult(PluginResult.Status.OK, uri);
             } else if (action.equals("onNewIntent")) {
@@ -173,7 +173,7 @@ public class WebIntent extends Plugin {
                 i.putExtra(key, value);
             }
         }
-        this.ctx.startActivity(i);
+        this.cordova.getActivity().startActivity(i);
     }
 
     void sendBroadcast(String action, Map extras) {
@@ -184,6 +184,6 @@ public class WebIntent extends Plugin {
             intent.putExtra(key, value);
         }
 
-        ((DroidGap) this.ctx).sendBroadcast(intent);
+        ((DroidGap)this.cordova.getContext()).sendBroadcast(intent);
     }
 }
diff --git a/Android/WebIntent/webintent.js b/Android/WebIntent/webintent.js
index 61a2f20..da22b80 100644
--- a/Android/WebIntent/webintent.js
+++ b/Android/WebIntent/webintent.js
@@ -1,5 +1,5 @@
 /**
- * Phonegap Web Intent plugin
+ * cordova Web Intent plugin
  * Copyright (c) Boris Smus 2010
  *
  */
@@ -15,7 +15,7 @@ WebIntent.EXTRA_STREAM = "android.intent.extra.STREAM";
 WebIntent.EXTRA_EMAIL = "android.intent.extra.EMAIL";
 
 WebIntent.prototype.startActivity = function(params, success, fail) {
-	return PhoneGap.exec(function(args) {
+	return cordova.exec(function(args) {
         success(args);
     }, function(args) {
         fail(args);
@@ -23,7 +23,7 @@ WebIntent.prototype.startActivity = function(params, success, fail) {
 };
 
 WebIntent.prototype.hasExtra = function(params, success, fail) {
-	return PhoneGap.exec(function(args) {
+	return cordova.exec(function(args) {
         success(args);
     }, function(args) {
         fail(args);
@@ -31,7 +31,7 @@ WebIntent.prototype.hasExtra = function(params, success, fail) {
 };
 
 WebIntent.prototype.getUri = function(success, fail) {
-	return PhoneGap.exec(function(args) {
+	return cordova.exec(function(args) {
         success(args);
     }, function(args) {
         fail(args);
@@ -39,7 +39,7 @@ WebIntent.prototype.getUri = function(success, fail) {
 };
 
 WebIntent.prototype.getExtra = function(params, success, fail) {
-	return PhoneGap.exec(function(args) {
+	return cordova.exec(function(args) {
         success(args);
     }, function(args) {
         fail(args);
@@ -48,20 +48,24 @@ WebIntent.prototype.getExtra = function(params, success, fail) {
 
 
 WebIntent.prototype.onNewIntent = function(callback) {
-	return PhoneGap.exec(function(args) {
+	return cordova.exec(function(args) {
 		callback(args);
     }, function(args) {
     }, 'WebIntent', 'onNewIntent', []);
 };
 
 WebIntent.prototype.sendBroadcast = function(params, success, fail) {
-    return PhoneGap.exec(function(args) {
+    return cordova.exec(function(args) {
         success(args);
     }, function(args) {
         fail(args);
     }, 'WebIntent', 'sendBroadcast', [params]);
 };
 
-PhoneGap.addConstructor(function() {
-	PhoneGap.addPlugin('webintent', new WebIntent());
+cordova.addConstructor(function() {
+	window.webintent = new WebIntent();
+	
+	// backwards compatibility	
+	window.plugins = window.plugins || {};
+	window.plugins.webintent = window.webintent;
 });
diff --git a/Android/WikitudeCamera/WikitudeCamera.java b/Android/WikitudeCamera/WikitudeCamera.java
index 57e8e5e..10e5947 100644
--- a/Android/WikitudeCamera/WikitudeCamera.java
+++ b/Android/WikitudeCamera/WikitudeCamera.java
@@ -17,14 +17,14 @@ import org.openintents.intents.WikitudePOI;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.ActivityNotFoundException;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.net.Uri;
 import android.util.Log;
 
-import com.phonegap.api.PhonegapActivity;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
 
 /**
  * This calls out to the Wikitude SDK and returns the result.
@@ -34,7 +34,7 @@ public class WikitudeCamera extends Plugin {
 	public static final String ACTION = "show";
 	public static final int REQUEST_CODE = 0x0ba7c0de;
 	public String callback;
-	
+
 	public static final String defaultInstallTitle = "Install Wikitude Browser?";
 	public static final String defaultInstallMessage = "This requires the free Wikitude Browser app. Would you like to install it now?";
 	public static final String defaultYesString = "Yes";
@@ -82,7 +82,7 @@ public class WikitudeCamera extends Plugin {
 
 			// add POIs to AR intent
 			WikitudeARIntent intent = new WikitudeARIntent(
-					this.ctx.getApplication(), null, null);
+                this.ctx.getActivity().getApplication(), null, null);
 			intent.addPOIs(pois);
 			intent.addTitleText(extract(options, "title"));
 
@@ -138,9 +138,10 @@ public class WikitudeCamera extends Plugin {
 			}
 		}
 	}
-	
+
 	private void showDownloadDialog(final String title, final String message, final String yesString, final String noString) {
-		final PhonegapActivity context = this.ctx;
+		final Context context = this.ctx.getContext();
+        final Activity activity = this.ctx.getActivity();
 		Runnable runnable = new Runnable() {
 			public void run() {
 
@@ -154,7 +155,7 @@ public class WikitudeCamera extends Plugin {
 												   Uri.parse("market://search?q=pname:com.wikitude")
 												   );
 						try {
-							context.startActivity(intent);
+                            activity.startActivity(intent);
 						} catch (ActivityNotFoundException e) {
 							// We don't have the market app installed.
 							e.printStackTrace();
@@ -171,7 +172,7 @@ public class WikitudeCamera extends Plugin {
 				dialog.show();
 			}
 		};
-		context.runOnUiThread(runnable);
+        activity.runOnUiThread(runnable);
 	}
 
 }
diff --git a/Android/WikitudeCamera/wikitudecamera.js b/Android/WikitudeCamera/wikitudecamera.js
index b04e99b..aad4b1e 100644
--- a/Android/WikitudeCamera/wikitudecamera.js
+++ b/Android/WikitudeCamera/wikitudecamera.js
@@ -2,18 +2,18 @@
  * Phonegap Wikitude AR Camera plugin
  * Copyright (c) Spletart 2011
  */
-var WikitudeCamera = function() { 
+var WikitudeCamera = function() {
 
 }
 
 WikitudeCamera.prototype.show = function(data, success, fail, options) {
-    return PhoneGap.exec(function(args) {
+    return cordova.exec(function(args) {
         success(args);
     }, function(args) {
         fail(args);
     }, 'WikitudeCamera', 'show', [data, options]);
 };
 
-PhoneGap.addConstructor(function() {
-	PhoneGap.addPlugin('wikitudeCamera', new WikitudeCamera());
+cordova.addConstructor(function() {
+	cordova.addPlugin('wikitudeCamera', new WikitudeCamera());
 });
\ No newline at end of file
diff --git a/BlackBerry/BarcodeScanner/barcodescanner.js b/BlackBerry/BarcodeScanner/barcodescanner.js
index 909a1e1..fab9ab7 100644
--- a/BlackBerry/BarcodeScanner/barcodescanner.js
+++ b/BlackBerry/BarcodeScanner/barcodescanner.js
@@ -38,7 +38,7 @@ var BarcodeScanner = BarcodeScanner || (function() {
     /**
      * Check that window.plugins.barcodeScanner has not been initialized.
      */
-    if (typeof window.plugins.barcodeScanner !== "undefined") {
+    if (typeof window.plugins !== "undefined" && typeof window.plugins.barcodeScanner !== "undefined") {
         return;
     }
 
@@ -146,9 +146,24 @@ var BarcodeScanner = BarcodeScanner || (function() {
         }, 'BarcodeScanner', 'encode', params);
     };
 
-    cordova.addConstructor(function() {
-        cordova.addPlugin('barcodeScanner', new BarcodeScanner());
-    });
+    /**
+     * Initialize BarcodeScanner global reference
+     */
+    if (cordova && (typeof cordova.addConstructor === "function") &&
+            (typeof cordova.addPlugin === "function")) {
+        // Cordova 1.6 - 1.8.1
+        cordova.addConstructor(function() {
+            cordova.addPlugin('barcodeScanner', new BarcodeScanner());
+        });
+    } else {
+        // Cordova 2.0
+        if (!window.plugins) {
+            window.plugins = {};
+        }
+        if (!window.plugins.barcodeScanner) {
+            window.plugins.barcodeScanner = new BarcodeScanner();
+        }
+    }
 
     /**
      * Return an object that contains the static constants.
diff --git a/BlackBerry/ChildBrowser/www/childbrowser.js b/BlackBerry/ChildBrowser/www/childbrowser.js
index e1a687f..17eca3d 100644
--- a/BlackBerry/ChildBrowser/www/childbrowser.js
+++ b/BlackBerry/ChildBrowser/www/childbrowser.js
@@ -94,9 +94,21 @@ var ChildBrowser = ChildBrowser || (function() {
     };
 
     /**
-     * Load ChildBrowser
+     * Initialize ChildBrowser global reference
      */
-    cordova.addConstructor(function() {
-        cordova.addPlugin("childBrowser", new ChildBrowser());
-    });
+    if (cordova && (typeof cordova.addConstructor === "function") &&
+            (typeof cordova.addPlugin === "function")) {
+        // Cordova 1.6 - 1.8.1
+        cordova.addConstructor(function() {
+            cordova.addPlugin("childBrowser", new ChildBrowser());
+        });
+    } else {
+        // Cordova 2.0
+        if (!window.plugins) {
+            window.plugins = {};
+        }
+        if (!window.plugins.childBrowser) {
+            window.plugins.childBrowser = new ChildBrowser();
+        }
+    }
 })();
diff --git a/BlackBerry/Globalization/Globalization.java b/BlackBerry/Globalization/Globalization.java
index c54bbd4..5cdfca0 100644
--- a/BlackBerry/Globalization/Globalization.java
+++ b/BlackBerry/Globalization/Globalization.java
@@ -2,7 +2,7 @@
  * PhoneGap is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
  *
- * Copyright (c) 2011, IBM Corporation
+ * Copyright (c) 2011-2012, IBM Corporation
  */
 package org.apache.cordova.plugins.globalization;
 
@@ -41,6 +41,8 @@ public class Globalization extends Plugin {
         try {
             if (action.equals(Resources.GETLOCALENAME)) {
                 obj = getLocaleName();
+            } else if (action.equals(Resources.GETPREFERREDLANGUAGE)){
+                obj = getPreferredLanguage();
             } else if (action.equalsIgnoreCase(Resources.DATETOSTRING)) {
                 obj = getDateToString(data);
             } else if (action.equalsIgnoreCase(Resources.STRINGTODATE)) {
@@ -90,6 +92,23 @@ public class Globalization extends Plugin {
         }
     }
 
+    /* 
+     * @Description: Returns the string identifier for the client's current language
+     * 
+     * @Return: JSONObject
+     *          Object.value {String}: The language identifier
+     * 
+     * @throws: GlobalizationError.UNKNOWN_ERROR
+     */	
+    private JSONObject getPreferredLanguage() throws GlobalizationError {
+        JSONObject obj = new JSONObject();
+        try {
+            return obj.put("value", Locale.getDefault().getDisplayLanguage().toString());
+        } catch (Exception e) {
+            throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
+        }
+    }
+
     /*
      * Returns a date formatted as a string according to the client's user
      * preferences and calendar using the time zone of the client.
diff --git a/BlackBerry/Globalization/Resources.java b/BlackBerry/Globalization/Resources.java
index 8213e76..668cb25 100644
--- a/BlackBerry/Globalization/Resources.java
+++ b/BlackBerry/Globalization/Resources.java
@@ -2,7 +2,7 @@
  * PhoneGap is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
  *
- * Copyright (c) 2011, IBM Corporation
+ * Copyright (c) 2011-2012, IBM Corporation
  */
 package org.apache.cordova.plugins.globalization;
 
@@ -19,6 +19,7 @@ public class Resources {
 	public static final String STRINGTONUMBER = "stringToNumber";
 	public static final String GETNUMBERPATTERN = "getNumberPattern";
 	public static final String GETCURRENCYPATTERN = "getCurrencyPattern";
+    public static final String GETPREFERREDLANGUAGE = "getPreferredLanguage";
 
     // Globalization Option Parameters
 	public static final String OPTIONS = "options";
diff --git a/BlackBerry/Globalization/globalization.js b/BlackBerry/Globalization/globalization.js
index 54b530c..bc9002d 100644
--- a/BlackBerry/Globalization/globalization.js
+++ b/BlackBerry/Globalization/globalization.js
@@ -2,567 +2,546 @@
  * PhoneGap is available under *either* the terms of the modified BSD license *or* the
  * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
  *
- * Copyright (c) 2011, IBM Corporation
+ * Copyright (c) 2011-2012, IBM Corporation
  */
 
-/**
- * window.plugins.globalization
- *
- * Provides access to native localization information.
- */
-(function() {
+var Globalization = function() {  
+};
 
-    /**
-     * Check that navigator.notification has not been initialized.
-     */
-    if (typeof window.plugins.globalization !== "undefined") {
+Globalization.prototype.getPreferredLanguage = function(successCB, failureCB)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.getPreferredLanguage Error: successCB is not a function");
         return;
     }
-
-    /**
-     * @constructor
-     */
-    function Globalization() {
-    };
-
-    /**
-     * Returns the string identifier for the client's current locale setting. It
-     * returns the locale identifier string to the successCB callback with a
-     * properties object as a parameter. If there is an error getting the
-     * locale, then the errorCB callback is invoked.
-     *
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     *
-     * @return Object.value {String}: The locale identifier
-     *
-     * @error GlobalizationError.UNKNOWN_ERROR
-     *
-     * Example
-     *      globalization.getLocaleName(
-     *          function (locale) {alert('locale:' + locale.value + '\n');},
-     *          function () {});
-     */
-    Globalization.prototype.getLocaleName = function(successCB, failureCB) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.getLocaleName Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.getLocaleName Error: failureCB is not a function");
-            return;
-        }
-        cordova.exec(successCB, failureCB, "Globalization",
-                "getLocaleName", []);
-    };
-
-    /**
-     * Returns a date formatted as a string according to the client's user
-     * preferences and calendar using the time zone of the client. It returns
-     * the formatted date string to the successCB callback with a properties
-     * object as a parameter. If there is an error formatting the date, then the
-     * errorCB callback is invoked.
-     *
-     * The defaults are: formatLenght="short" and selector="date and time"
-     *
-     * @param {Date} date
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     * @param {Object} options {optional}
-     *                  formatLength {String}: 'short', 'medium', 'long', or 'full'
-     *                  selector {String}: 'date', 'time', or 'date and time'
-     *
-     * @return Object.value {String}: The localized date string
-     *
-     * @error GlobalizationError.FORMATTING_ERROR
-     *
-     * Example
-     *      globalization.dateToString(new Date(),
-     *          function (date) {alert('date:' + date.value + '\n');},
-     *          function (errorCode) {alert(errorCode);},
-     *          {formatLength:'short'});
-     */
-    Globalization.prototype.dateToString = function(date, successCB, failureCB,
-            options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.dateToString Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.dateToString Error: failureCB is not a function");
-            return;
-        }
-
-        if (date instanceof Date) {
-            var dateValue;
-            dateValue = date.valueOf();
-            cordova.exec(successCB, failureCB, "Globalization",
-                    "dateToString", [ {"date" : dateValue, "options" : options} ]);
-        } else {
-            console.log("Globalization.dateToString Error: date is not a Date object");
-        }
-    };
-
-    /**
-     * Parses a date formatted as a string according to the client's user
-     * preferences and calendar using the time zone of the client and returns
-     * the corresponding date object. It returns the date to the successCB
-     * callback with a properties object as a parameter. If there is an error
-     * parsing the date string, then the errorCB callback is invoked.
-     *
-     * The defaults are: formatLength="short" and selector="date and time"
-     *
-     * @param {String} dateString
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     * @param {Object} options {optional}
-     *                  formatLength {String}: 'short', 'medium', 'long', or 'full'
-     *                  selector {String}: 'date', 'time', or 'date and time'
-     *
-     * @return Object.year {Number}:        The four digit year
-     *         Object.month {Number}:       The month from (0 - 11)
-     *         Object.day {Number}:         The day from (1 - 31)
-     *         Object.hour {Number}:        The hour from (0 - 23)
-     *         Object.minute {Number}:      The minute from (0 - 59)
-     *         Object.second {Number}:      The second from (0 - 59)
-     *         Object.millisecond {Number}: The milliseconds (from 0 - 999), not
-     *                                      available on all platforms
-     *
-     * @error GlobalizationError.PARSING_ERROR
-     *
-     * Example
-     *      globalization.stringToDate('4/11/2011',
-     *          function (date) {alert('Month:' + date.month + '\n' +
-     *                      'Day:' + date.day + '\n' +
-     *                      'Year:' + date.year + '\n');},
-     *          function (errorCode) {alert(errorCode);},
-     *          {selector:'date'});
-     */
-    Globalization.prototype.stringToDate = function(dateString, successCB,
-            failureCB, options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.stringToDate Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.stringToDate Error: failureCB is not a function");
-            return;
-        }
-        if (typeof dateString == "string") {
-            cordova.exec(successCB, failureCB, "Globalization",
-                    "stringToDate", [ {"dateString" : dateString, "options" : options} ]);
-        } else {
-            console.log("Globalization.stringToDate Error: dateString is not a string");
-        }
-    };
-
-    /**
-     * Returns a pattern string for formatting and parsing dates according to
-     * the client's user preferences. It returns the pattern to the successCB
-     * callback with a properties object as a parameter. If there is an error
-     * obtaining the pattern, then the errorCB callback is invoked.
-     *
-     * The defaults are: formatLength="short" and selector="date and time"
-     *
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     * @param {Object} options {optional}
-     *                  formatLength {String}: 'short', 'medium', 'long', or 'full'
-     *                  selector {String}: 'date', 'time', or 'date and time'
-     *
-     * @return Object.pattern {String}: The date and time pattern for formatting
-     *                                  and parsing dates. The patterns follow
-     *                                  Unicode Technical Standard #35
-     *                                  http://unicode.org/reports/tr35/tr35-4.html
-     *         Object.timezone {String}: The abbreviated name of the time zone
-     *                                  on the client.
-     *         Object.utc_offset {Number}: The current difference in seconds
-     *                                  between the client's time zone and
-     *                                  coordinated universal time.
-     *         Object.dst_offset {Number}: The current daylight saving time
-     *                                  offset in seconds between the client's
-     *                                  non-daylight saving's time zone and the
-     *                                  client's daylight saving's time zone.
-     *
-     * @error GlobalizationError.PATTERN_ERROR
-     *
-     * Example
-     *      globalization.getDatePattern(new Date(),
-     *          function (date) {alert('pattern:' + date.pattern + '\n');},
-     *          function () {},
-     *          {formatLength:'short'});
-     */
-    Globalization.prototype.getDatePattern = function(successCB, failureCB,
-            options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.getDatePattern Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.getDatePattern Error: failureCB is not a function");
-            return;
-        }
-
-        cordova.exec(successCB, failureCB, "Globalization",
-                "getDatePattern", [ {"options" : options} ]);
-    };
-
-    /**
-     * Returns an array of either the names of the months or days of the week
-     * according to the client's user preferences and calendar. It returns the
-     * array of names to the successCB callback with a properties object as a
-     * parameter. If there is an error obtaining the names, then the errorCB
-     * callback is invoked.
-     *
-     * The defaults are: type="wide" and item="months"
-     *
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     * @param {Object} options {optional}
-     *                  type {String}: 'narrow' or 'wide'
-     *                  item {String}: 'months', or 'days'
-     *
-     * @return Object.value {Array{String}}: The array of names starting from
-     *         either the first month in the year or the first day of the week.
-     * @error GlobalizationError.UNKNOWN_ERROR
-     *
-     * Example
-     *      globalization.getDateNames(
-     *          function (names) { for(var i = 0; i < names.value.length; i++) {
-     *              alert('Month:' + names.value[i] + '\n');}},
-     *          function () {});
-     */
-    Globalization.prototype.getDateNames = function(successCB, failureCB,
-            options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.getDateNames Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.getDateNames Error: failureCB is not a function");
-            return;
-        }
-        cordova.exec(successCB, failureCB, "Globalization",
-                "getDateNames", [ {"options" : options} ]);
-    };
-
-    /**
-     * Returns whether daylight savings time is in effect for a given date using
-     * the client's time zone and calendar. It returns whether or not daylight
-     * savings time is in effect to the successCB callback with a properties
-     * object as a parameter. If there is an error reading the date, then the
-     * errorCB callback is invoked.
-     *
-     * @param {Date} date
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     *
-     * @return Object.dst {Boolean}: The value "true" indicates that daylight
-     *         savings time is in effect for the given date and "false" indicate
-     *         that it is not.
-     *
-     * @error GlobalizationError.UNKNOWN_ERROR
-     *
-     * Example
-     *      globalization.isDayLightSavingsTime(new Date(),
-     *          function (date) {alert('dst:' + date.dst + '\n');},
-     *          function () {});
-     */
-    Globalization.prototype.isDayLightSavingsTime = function(date, successCB,
-            failureCB) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.isDayLightSavingsTime Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.isDayLightSavingsTime Error: failureCB is not a function");
-            return;
-        }
-
-        if (date instanceof Date) {
-            var dateValue;
-            dateValue = date.valueOf();
-            cordova.exec(successCB, failureCB, "Globalization",
-                    "isDayLightSavingsTime", [ {"date" : dateValue} ]);
-        } else {
-            console.log("Globalization.isDayLightSavingsTime Error: date is not a Date object");
-        }
-
-    };
-
-    /**
-     * Returns the first day of the week according to the client's user
-     * preferences and calendar. The days of the week are numbered starting from
-     * 1 where 1 is considered to be Sunday. It returns the day to the successCB
-     * callback with a properties object as a parameter. If there is an error
-     * obtaining the pattern, then the errorCB callback is invoked.
-     *
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     *
-     * @return Object.value {Number}: The number of the first day of the week.
-     *
-     * @error GlobalizationError.UNKNOWN_ERROR
-     *
-     * Example
-     *      globalization.getFirstDayOfWeek(
-     *          function (day) { alert('Day:' + day.value + '\n');},
-     *          function () {});
-     */
-    Globalization.prototype.getFirstDayOfWeek = function(successCB, failureCB) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.getFirstDayOfWeek Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.getFirstDayOfWeek Error: failureCB is not a function");
-            return;
-        }
-
-        cordova.exec(successCB, failureCB, "Globalization",
-                "getFirstDayOfWeek", []);
-    };
-
-    /**
-     * Returns a number formatted as a string according to the client's user
-     * preferences. It returns the formatted number string to the successCB
-     * callback with a properties object as a parameter. If there is an error
-     * formatting the number, then the errorCB callback is invoked.
-     *
-     * The defaults are: type="decimal"
-     *
-     * @param {Number} number
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     * @param {Object} options {optional}
-     *                      type {String}: 'decimal', "percent", or 'currency'
-     *
-     * @return Object.value {String}: The formatted number string.
-     *
-     * @error GlobalizationError.FORMATTING_ERROR
-     *
-     * Example
-     *      globalization.numberToString(3.25,
-     *          function (number) {alert('number:' + number.value + '\n');},
-     *          function () {},
-     *          {type:'decimal'});
-     */
-    Globalization.prototype.numberToString = function(number, successCB,
-            failureCB, options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.numberToString Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.numberToString Error: failureCB is not a function");
-            return;
-        }
-
-        if (typeof number == "number") {
-            cordova.exec(successCB, failureCB, "Globalization",
-                    "numberToString", [ {"number" : number, "options" : options} ]);
-        } else {
-            console.log("Globalization.numberToString Error: number is not a number");
-        }
-    };
-
-    /**
-     * Parses a number formatted as a string according to the client's user
-     * preferences and returns the corresponding number. It returns the number
-     * to the successCB callback with a properties object as a parameter. If
-     * there is an error parsing the number string, then the errorCB callback is
-     * invoked.
-     *
-     * The defaults are: type="decimal"
-     *
-     * @param {String} numberString
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     * @param {Object} options {optional}
-     *                      type {String}: 'decimal', "percent", or 'currency'
-     *
-     * @return Object.value {Number}: The parsed number.
-     *
-     * @error GlobalizationError.PARSING_ERROR
-     *
-     * Example
-     *      globalization.stringToNumber('1234.56',
-     *          function (number) {alert('Number:' + number.value + '\n');},
-     *          function () { alert('Error parsing number');});
-     */
-    Globalization.prototype.stringToNumber = function(numberString, successCB,
-            failureCB, options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.stringToNumber Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.stringToNumber Error: failureCB is not a function");
-            return;
-        }
-
-        if (typeof numberString == "string") {
-            cordova.exec(successCB, failureCB, "Globalization",
-                    "stringToNumber", [ {"numberString" : numberString,
-                        "options" : options} ]);
-        } else {
-            console.log("Globalization.stringToNumber Error: numberString is not a string");
-        }
-    };
-
-    /**
-     * Returns a pattern string for formatting and parsing numbers according to
-     * the client's user preferences. It returns the pattern to the successCB
-     * callback with a properties object as a parameter. If there is an error
-     * obtaining the pattern, then the errorCB callback is invoked.
-     *
-     * The defaults are: type="decimal"
-     *
-     * @param {Function}
-     *            successCB
-     * @param {Function}
-     *            errorCB
-     * @param {Object}
-     *            options {optional} type {String}: 'decimal', "percent", or 'currency'
-     *
-     * @return Object.pattern {String}: The number pattern for formatting and
-     *                                  parsing numbers. The patterns follow
-     *                                  Unicode Technical Standard #35.
-     *                                  http://unicode.org/reports/tr35/tr35-4.html
-     *         Object.symbol {String}:  The symbol to be used when formatting
-     *                                  and parsing e.g., percent or currency
-     *                                  symbol.
-     *         Object.fraction {Number}: The number of fractional digits to use
-     *                                  when parsing and formatting numbers.
-     *         Object.rounding {Number}: The rounding increment to use when
-     *                                  parsing and formatting.
-     *         Object.positive {String}: The symbol to use for positive numbers
-     *                                  when parsing and formatting.
-     *         Object.negative: {String}: The symbol to use for negative numbers
-     *                                  when parsing and formatting.
-     *         Object.decimal: {String}: The decimal symbol to use for parsing
-     *                                  and formatting.
-     *         Object.grouping: {String}: The grouping symbol to use for parsing
-     *                                  and formatting.
-     *
-     * @error GlobalizationError.PATTERN_ERROR
-     *
-     * Example
-     *      globalization.getNumberPattern(
-     *          function (pattern) {alert('Pattern:' + pattern.pattern + '\n');},
-     *          function () {});
-     */
-    Globalization.prototype.getNumberPattern = function(successCB, failureCB,
-            options) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.getNumberPattern Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.getNumberPattern Error: failureCB is not a function");
-            return;
-        }
-
-        cordova.exec(successCB, failureCB, "Globalization",
-                "getNumberPattern", [ {"options" : options} ]);
-    };
-
-    /**
-     * Returns a pattern string for formatting and parsing currency values
-     * according to the client's user preferences and ISO 4217 currency code. It
-     * returns the pattern to the successCB callback with a properties object as
-     * a parameter. If there is an error obtaining the pattern, then the errorCB
-     * callback is invoked.
-     *
-     * @param {String} currencyCode
-     * @param {Function} successCB
-     * @param {Function} errorCB
-     *
-     * @return Object.pattern {String}: The currency pattern for formatting and
-     *                                  parsing currency values. The patterns
-     *                                  follow Unicode Technical Standard #35
-     *                                  http://unicode.org/reports/tr35/tr35-4.html
-     *         Object.code {String}:    The ISO 4217 currency code for the pattern.
-     *         Object.fraction {Number}: The number of fractional digits to use
-     *                                  when parsing and formatting currency.
-     *         Object.rounding {Number}: The rounding increment to use when
-     *                                  parsing and formatting.
-     *         Object.decimal: {String}: The decimal symbol to use for parsing
-     *                                  and formatting.
-     *         Object.grouping: {String}: The grouping symbol to use for parsing
-     *                                  and formatting.
-     *
-     * @error GlobalizationError.FORMATTING_ERROR
-     *
-     * Example
-     *      globalization.getCurrencyPattern('EUR',
-     *          function (currency) {alert('Pattern:' + currency.pattern + '\n');}
-     *          function () {});
-     */
-    Globalization.prototype.getCurrencyPattern = function(currencyCode,
-            successCB, failureCB) {
-        // successCallback required
-        if (typeof successCB != "function") {
-            console.log("Globalization.getCurrencyPattern Error: successCB is not a function");
-            return;
-        }
-
-        // errorCallback required
-        if (typeof failureCB != "function") {
-            console.log("Globalization.getCurrencyPattern Error: failureCB is not a function");
-            return;
-        }
-
-        if (typeof currencyCode == "string") {
-            cordova.exec(successCB, failureCB, "Globalization",
-                    "getCurrencyPattern", [ {"currencyCode" : currencyCode} ]);
-        } else {
-            console.log("Globalization.getCurrencyPattern Error: currencyCode is not a currency code");
-        }
-    };
-
-    cordova.addConstructor(function() {
-        cordova.addPlugin('globalization', new Globalization());
-    });
-}());
-
-
-var GlobalizationError = function() {
-    this.code = null;
+    
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.getPreferredLanguage Error: failureCB is not a function");
+        return;
+    }
+    
+	cordova.exec(successCB, failureCB, "Globalization","getPreferredLanguage", []);
 };
 
+/**
+* Returns the string identifier for the client's current locale setting.
+* It returns the locale identifier string to the successCB callback with a 
+* properties object as a parameter. If there is an error getting the locale, 
+* then the errorCB callback is invoked.
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+*
+* @return Object.value {String}: The locale identifier
+*
+* @error GlobalizationError.UNKNOWN_ERROR 
+*
+* Example
+*	globalization.getLocaleName(function (locale) {alert('locale:' + locale.value + '\n');}, 
+*								function () {});
+*/	
+Globalization.prototype.getLocaleName = function(successCB, failureCB)
+{
+	// successCallback required
+	if (typeof successCB != "function") {	
+        console.log("Globalization.getLocaleName Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {        
+    	console.log("Globalization.getLocaleName Error: failureCB is not a function");
+        return;
+    }
+	cordova.exec(successCB, failureCB, "Globalization","getLocaleName", []);
+};
+
+	
+/**
+* Returns a date formatted as a string according to the client's user preferences and 
+* calendar using the time zone of the client. It returns the formatted date string to the 
+* successCB callback with a properties object as a parameter. If there is an error 
+* formatting the date, then the errorCB callback is invoked. 
+*
+* The defaults are: formatLenght="short" and selector="date and time"
+*
+* @param {Date} date 
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			formatLength {String}: 'short', 'medium', 'long', or 'full'
+*			selector {String}: 'date', 'time', or 'date and time' 
+* 
+* @return Object.value {String}: The localized date string
+*
+* @error GlobalizationError.FORMATTING_ERROR 
+*
+* Example
+*	globalization.dateToString(new Date(),
+*				function (date) {alert('date:' + date.value + '\n');},
+*				function (errorCode) {alert(errorCode);},
+*				{formatLength:'short'}); 
+*/	
+Globalization.prototype.dateToString = function(date, successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.dateToString Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.dateToString Error: failureCB is not a function");
+        return;
+    }
+	
+	
+	if (date instanceof Date){
+		var dateValue;
+		dateValue = date.valueOf();		
+		cordova.exec(successCB, failureCB, "Globalization", "dateToString", [{"date": dateValue, "options": options}]);
+	}
+	else {
+		console.log("Globalization.dateToString Error: date is not a Date object");
+	}
+};
+
+
+/**
+* Parses a date formatted as a string according to the client's user 
+* preferences and calendar using the time zone of the client and returns 
+* the corresponding date object. It returns the date to the successCB 
+* callback with a properties object as a parameter. If there is an error 
+* parsing the date string, then the errorCB callback is invoked.
+*
+* The defaults are: formatLength="short" and selector="date and time"
+*
+* @param {String} dateString 
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			formatLength {String}: 'short', 'medium', 'long', or 'full'
+*			selector {String}: 'date', 'time', or 'date and time' 
+* 
+* @return	Object.year {Number}: The four digit year
+*			Object.month {Number}: The month from (0 - 11)
+*			Object.day {Number}: The day from (1 - 31)
+*			Object.hour {Number}: The hour from (0 - 23)
+*			Object.minute {Number}: The minute from (0 - 59)
+*			Object.second {Number}: The second from (0 - 59)
+*			Object.millisecond {Number}: The milliseconds (from 0 - 999), 
+*										not available on all platforms
+* 
+* @error GlobalizationError.PARSING_ERROR
+*
+* Example
+*	globalization.stringToDate('4/11/2011',
+*				function (date) { alert('Month:' + date.month + '\n' +
+*					'Day:' + date.day + '\n' +
+*					'Year:' + date.year + '\n');},
+*				function (errorCode) {alert(errorCode);},
+*				{selector:'date'});
+*/	
+Globalization.prototype.stringToDate = function(dateString, successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.stringToDate Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.stringToDate Error: failureCB is not a function");
+        return;
+    }	
+	if (typeof dateString == "string"){
+		cordova.exec(successCB, failureCB, "Globalization", "stringToDate", [{"dateString": dateString, "options": options}]);
+	}
+	else {
+		console.log("Globalization.stringToDate Error: dateString is not a string");
+	}
+};
+
+	
+/**
+* Returns a pattern string for formatting and parsing dates according to the client's 
+* user preferences. It returns the pattern to the successCB callback with a 
+* properties object as a parameter. If there is an error obtaining the pattern, 
+* then the errorCB callback is invoked.
+*
+* The defaults are: formatLength="short" and selector="date and time"
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			formatLength {String}: 'short', 'medium', 'long', or 'full'
+*			selector {String}: 'date', 'time', or 'date and time' 
+* 
+* @return	Object.pattern {String}: The date and time pattern for formatting and parsing dates. 
+*									The patterns follow Unicode Technical Standard #35
+*									http://unicode.org/reports/tr35/tr35-4.html
+*			Object.timezone {String}: The abbreviated name of the time zone on the client
+*			Object.utc_offset {Number}: The current difference in seconds between the client's 
+*										time zone and coordinated universal time. 
+*			Object.dst_offset {Number}: The current daylight saving time offset in seconds 
+*										between the client's non-daylight saving's time zone 
+*										and the client's daylight saving's time zone.
+*
+* @error GlobalizationError.PATTERN_ERROR
+*
+* Example
+*	globalization.getDatePattern(
+*				function (date) {alert('pattern:' + date.pattern + '\n');},
+*				function () {},
+*				{formatLength:'short'});
+*/	
+Globalization.prototype.getDatePattern = function(successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.getDatePattern Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.getDatePattern Error: failureCB is not a function");
+        return;
+    }
+	
+	cordova.exec(successCB, failureCB, "Globalization", "getDatePattern", [{"options": options}]);
+};
+
+	
+/**
+* Returns an array of either the names of the months or days of the week 
+* according to the client's user preferences and calendar. It returns the array of names to the 
+* successCB callback with a properties object as a parameter. If there is an error obtaining the 
+* names, then the errorCB callback is invoked.
+*
+* The defaults are: type="wide" and item="months"
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			type {String}: 'narrow' or 'wide'
+*			item {String}: 'months', or 'days' 
+* 
+* @return Object.value {Array{String}}: The array of names starting from either 
+*										the first month in the year or the 
+*										first day of the week.
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+*	globalization.getDateNames(function (names) { 
+*		for(var i = 0; i < names.value.length; i++) {
+*			alert('Month:' + names.value[i] + '\n');}},
+*		function () {});
+*/	
+Globalization.prototype.getDateNames = function(successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.getDateNames Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.getDateNames Error: failureCB is not a function");
+        return;
+    }
+	cordova.exec(successCB, failureCB, "Globalization", "getDateNames", [{"options": options}]);
+};
+
+/**
+* Returns whether daylight savings time is in effect for a given date using the client's 
+* time zone and calendar. It returns whether or not daylight savings time is in effect 
+* to the successCB callback with a properties object as a parameter. If there is an error 
+* reading the date, then the errorCB callback is invoked.
+*
+* @param {Date} date
+* @param {Function} successCB
+* @param {Function} errorCB 
+* 
+* @return Object.dst {Boolean}: The value "true" indicates that daylight savings time is 
+*								in effect for the given date and "false" indicate that it is not.
+*
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+*	globalization.isDayLightSavingsTime(new Date(),
+*				function (date) {alert('dst:' + date.dst + '\n');}
+*				function () {});
+*/	
+Globalization.prototype.isDayLightSavingsTime = function(date, successCB, failureCB)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.isDayLightSavingsTime Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.isDayLightSavingsTime Error: failureCB is not a function");
+        return;
+    }
+	
+	
+	if (date instanceof Date){
+		var dateValue;
+		dateValue = date.valueOf();
+		cordova.exec(successCB, failureCB, "Globalization", "isDayLightSavingsTime", [{"date": dateValue}]);
+	}
+	else {
+		console.log("Globalization.isDayLightSavingsTime Error: date is not a Date object");
+	}
+	
+};
+
+/**
+* Returns the first day of the week according to the client's user preferences and calendar. 
+* The days of the week are numbered starting from 1 where 1 is considered to be Sunday. 
+* It returns the day to the successCB callback with a properties object as a parameter. 
+* If there is an error obtaining the pattern, then the errorCB callback is invoked.
+*
+* @param {Function} successCB
+* @param {Function} errorCB 
+* 
+* @return Object.value {Number}: The number of the first day of the week.
+*
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+*	globalization.getFirstDayOfWeek(function (day) 
+*				{ alert('Day:' + day.value + '\n');},
+*				function () {});
+*/	
+Globalization.prototype.getFirstDayOfWeek = function(successCB, failureCB)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.getFirstDayOfWeek Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.getFirstDayOfWeek Error: failureCB is not a function");
+        return;
+    }
+	
+	cordova.exec(successCB, failureCB, "Globalization", "getFirstDayOfWeek", []);
+};
+
+	
+/**
+* Returns a number formatted as a string according to the client's user preferences. 
+* It returns the formatted number string to the successCB callback with a properties object as a 
+* parameter. If there is an error formatting the number, then the errorCB callback is invoked.
+*
+* The defaults are: type="decimal"
+*
+* @param {Number} number
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			type {String}: 'decimal', "percent", or 'currency'
+* 
+* @return Object.value {String}: The formatted number string.
+*
+* @error GlobalizationError.FORMATTING_ERROR
+*
+* Example
+*	globalization.numberToString(3.25,
+*				function (number) {alert('number:' + number.value + '\n');},
+*				function () {},
+*				{type:'decimal'});
+*/	
+Globalization.prototype.numberToString = function(number, successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.numberToString Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.numberToString Error: failureCB is not a function");
+        return;
+    }
+	
+	if(typeof number == "number") {
+		cordova.exec(successCB, failureCB, "Globalization", "numberToString", [{"number": number, "options": options}]);
+	}
+	else {
+		console.log("Globalization.numberToString Error: number is not a number");
+	}
+};
+
+/**
+* Parses a number formatted as a string according to the client's user preferences and 
+* returns the corresponding number. It returns the number to the successCB callback with a
+* properties object as a parameter. If there is an error parsing the number string, then 
+* the errorCB callback is invoked.
+*
+* The defaults are: type="decimal"
+*
+* @param {String} numberString
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			type {String}: 'decimal', "percent", or 'currency'
+* 
+* @return Object.value {Number}: The parsed number.
+*
+* @error GlobalizationError.PARSING_ERROR
+*
+* Example
+*	globalization.stringToNumber('1234.56',
+*				function (number) {alert('Number:' + number.value + '\n');},
+*				function () { alert('Error parsing number');});
+*/	
+Globalization.prototype.stringToNumber = function(numberString, successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.stringToNumber Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.stringToNumber Error: failureCB is not a function");
+        return;
+    }
+	
+	if(typeof numberString == "string") {
+		cordova.exec(successCB, failureCB, "Globalization", "stringToNumber", [{"numberString": numberString, "options": options}]);
+	}
+	else {
+		console.log("Globalization.stringToNumber Error: numberString is not a string");
+	}
+};
+
+/**
+* Returns a pattern string for formatting and parsing numbers according to the client's user 
+* preferences. It returns the pattern to the successCB callback with a properties object as a 
+* parameter. If there is an error obtaining the pattern, then the errorCB callback is invoked.
+*
+* The defaults are: type="decimal"
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+*			type {String}: 'decimal', "percent", or 'currency'
+* 
+* @return	Object.pattern {String}: The number pattern for formatting and parsing numbers. 
+*									The patterns follow Unicode Technical Standard #35. 
+*									http://unicode.org/reports/tr35/tr35-4.html
+*			Object.symbol {String}: The symbol to be used when formatting and parsing 
+*									e.g., percent or currency symbol.
+*			Object.fraction {Number}: The number of fractional digits to use when parsing and 
+*									formatting numbers.
+*			Object.rounding {Number}: The rounding increment to use when parsing and formatting.
+*			Object.positive {String}: The symbol to use for positive numbers when parsing and formatting.
+*			Object.negative: {String}: The symbol to use for negative numbers when parsing and formatting.
+*			Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
+*			Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
+*
+* @error GlobalizationError.PATTERN_ERROR
+*
+* Example
+*	globalization.getNumberPattern(
+*				function (pattern) {alert('Pattern:' + pattern.pattern + '\n');},
+*				function () {});
+*/	
+Globalization.prototype.getNumberPattern = function(successCB, failureCB, options)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.getNumberPattern Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.getNumberPattern Error: failureCB is not a function");
+        return;
+    }
+	
+	cordova.exec(successCB, failureCB, "Globalization", "getNumberPattern", [{"options": options}]);	
+};
+
+/**
+* Returns a pattern string for formatting and parsing currency values according to the client's 
+* user preferences and ISO 4217 currency code. It returns the pattern to the successCB callback with a 
+* properties object as a parameter. If there is an error obtaining the pattern, then the errorCB 
+* callback is invoked.
+*
+* @param {String} currencyCode	
+* @param {Function} successCB
+* @param {Function} errorCB
+* 
+* @return	Object.pattern {String}: The currency pattern for formatting and parsing currency values. 
+*									The patterns follow Unicode Technical Standard #35 
+*									http://unicode.org/reports/tr35/tr35-4.html
+*			Object.code {String}: The ISO 4217 currency code for the pattern.
+*			Object.fraction {Number}: The number of fractional digits to use when parsing and 
+*									formatting currency.
+*			Object.rounding {Number}: The rounding increment to use when parsing and formatting.
+*			Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
+*			Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
+*
+* @error GlobalizationError.FORMATTING_ERROR
+*
+* Example
+*	globalization.getCurrencyPattern('EUR',
+*				function (currency) {alert('Pattern:' + currency.pattern + '\n');}
+*				function () {});
+*/	
+Globalization.prototype.getCurrencyPattern = function(currencyCode, successCB, failureCB)
+{
+	// successCallback required
+	if (typeof successCB != "function") {
+        console.log("Globalization.getCurrencyPattern Error: successCB is not a function");
+        return;
+    }
+	
+    // errorCallback required
+    if (typeof failureCB != "function") {
+        console.log("Globalization.getCurrencyPattern Error: failureCB is not a function");
+        return;
+    }
+	
+	if(typeof currencyCode == "string") {
+		cordova.exec(successCB, failureCB, "Globalization", "getCurrencyPattern", [{"currencyCode": currencyCode}]);
+	}
+	else {
+		console.log("Globalization.getCurrencyPattern Error: currencyCode is not a currency code");
+	}
+};
+
+GlobalizationError = function() {
+	this.code = null;
+}
+
 // Globalization error codes
 GlobalizationError.UNKNOWN_ERROR = 0;
 GlobalizationError.FORMATTING_ERROR = 1;
 GlobalizationError.PARSING_ERROR = 2;
 GlobalizationError.PATTERN_ERROR = 3;
+
+
+if(!window.plugins) {
+    window.plugins = {};
+}
+if (!window.plugins.globalization) {
+    window.plugins.globalization = new Globalization();
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 9ec9830..929fced 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-This code is completely dependent on the Apache Cordova (formerly PhoneGap) project, hosted on GitHub ( github.com/callback )
+This code is completely dependent on the Apache Cordova (formerly PhoneGap) project, hosted on [GitHub](http://github.com/apache)
 
 Plugins will (for the time being) reside in this repository as pulling them into the Callback project would require all plugin contributors to meet stricter code licensing requirements pursuant to Apache's guidelines.
 
diff --git a/WindowsPhone/BarcodeScanner/deploy/BarcodeScanner.cs b/WindowsPhone/BarcodeScanner/deploy/BarcodeScanner.cs
new file mode 100644
index 0000000..a681690
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/deploy/BarcodeScanner.cs
@@ -0,0 +1,62 @@
+using System.Runtime.Serialization;
+using WP7CordovaClassLib.Cordova;
+using WP7CordovaClassLib.Cordova.Commands;
+using WP7CordovaClassLib.Cordova.JSON;
+using Microsoft.Phone.Shell;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Tasks;
+using System.Windows;
+
+namespace org.apache.cordova
+{
+    public class barcodeScanner : WP7CordovaClassLib.Cordova.Commands.BaseCommand
+    {
+        public void scan(string options) 
+        {
+            Deployment.Current.Dispatcher.BeginInvoke(() =>
+            {
+                var root = Application.Current.RootVisual as PhoneApplicationFrame;
+
+                root.Navigated += new System.Windows.Navigation.NavigatedEventHandler(root_Navigated);
+
+                root.Navigate(new System.Uri("/BarcodeScanner;component/Scanner.xaml?dummy=" + Guid.NewGuid().ToString(), UriKind.Relative));
+            });
+        }
+
+        void root_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+        {
+            if (!(e.Content is BarcodeScanner.Scanner)) return;
+
+            (Application.Current.RootVisual as PhoneApplicationFrame).Navigated -= root_Navigated;
+
+            BarcodeScanner.Scanner scanner = (BarcodeScanner.Scanner)e.Content;
+
+            if (scanner != null)
+            {
+                scanner.Completed += new EventHandler(scanner_Completed);
+            }
+        }
+
+        void scanner_Completed(object sender, BarcodeScanner.ScannerResult e)
+        {
+            if (e.TaskResult == TaskResult.OK)
+            {
+                string result = String.Format("\"cancelled\":{0},\"text\":\"{1}\"", 0, e.ScanCode);
+                result = "{" + result + "}";
+                DispatchCommandResult(new PluginResult(PluginResult.Status.OK, result));
+            }
+            else
+            {
+                DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Failed to scan QR code"));
+            }
+        }
+
+        public void encode(string options)
+        {
+            DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Not implemented"));
+        }
+    }
+}
diff --git a/WindowsPhone/BarcodeScanner/deploy/BarcodeScanner.dll b/WindowsPhone/BarcodeScanner/deploy/BarcodeScanner.dll
new file mode 100644
index 0000000..e5ba5f7
Binary files /dev/null and b/WindowsPhone/BarcodeScanner/deploy/BarcodeScanner.dll differ
diff --git a/WindowsPhone/BarcodeScanner/deploy/README.md b/WindowsPhone/BarcodeScanner/deploy/README.md
new file mode 100644
index 0000000..226e271
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/deploy/README.md
@@ -0,0 +1,10 @@
+This is port of BarcodeScanner plugin for Windows Phone 7 platform.
+
+Usage:
+1) Reference both DLL by your project.
+2) Add BarcodeScanner.cs to your project
+3) Add barcodescanner.js to html
+
+Currently only 'scan' method is implemented.
+
+Have fun
diff --git a/WindowsPhone/BarcodeScanner/deploy/ZXingVer1_7.dll b/WindowsPhone/BarcodeScanner/deploy/ZXingVer1_7.dll
new file mode 100644
index 0000000..bbf1d8d
Binary files /dev/null and b/WindowsPhone/BarcodeScanner/deploy/ZXingVer1_7.dll differ
diff --git a/WindowsPhone/BarcodeScanner/deploy/barcodescanner.js b/WindowsPhone/BarcodeScanner/deploy/barcodescanner.js
new file mode 100644
index 0000000..4eb2ac0
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/deploy/barcodescanner.js
@@ -0,0 +1,79 @@
+/**
+ * PhoneGap/Cordova is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) Matt Kane 2010
+ * Copyright (c) 2010, IBM Corporation
+ */
+
+;(function(){
+
+//-------------------------------------------------------------------
+var BarcodeScanner = function() {
+}
+
+//-------------------------------------------------------------------
+BarcodeScanner.Encode = {
+        TEXT_TYPE:     "TEXT_TYPE",
+        EMAIL_TYPE:    "EMAIL_TYPE",
+        PHONE_TYPE:    "PHONE_TYPE",
+        SMS_TYPE:      "SMS_TYPE",
+        CONTACT_TYPE:  "CONTACT_TYPE",
+        LOCATION_TYPE: "LOCATION_TYPE"
+}
+
+//-------------------------------------------------------------------
+BarcodeScanner.prototype.scan = function(success, fail, options) {
+    function successWrapper(result) {
+        result.cancelled = (result.cancelled == 1)
+        success.call(null, result)
+    }
+
+    if (!fail) { fail = function() {}}
+
+    if (typeof fail != "function")  {
+        console.log("BarcodeScanner.scan failure: failure parameter not a function")
+        return
+    }
+
+    if (typeof success != "function") {
+        fail("success callback parameter must be a function")
+        return
+    }
+  
+    if ( null == options ) 
+      options = []
+
+    return Cordova.exec(successWrapper, fail, "org.apache.cordova.barcodeScanner", "scan", options)
+}
+
+//-------------------------------------------------------------------
+BarcodeScanner.prototype.encode = function(type, data, success, fail, options) {
+    if (!fail) { fail = function() {}}
+
+    if (typeof fail != "function")  {
+        console.log("BarcodeScanner.scan failure: failure parameter not a function")
+        return
+    }
+
+    if (typeof success != "function") {
+        fail("success callback parameter must be a function")
+        return
+    }
+
+    return Cordova.exec(success, fail, "org.apache.cordova.barcodeScanner", "encode", [{type: type, data: data, options: options}])
+}
+
+//-------------------------------------------------------------------
+Cordova.addConstructor(function() {
+    if (!window.plugins) window.plugins = {}
+
+    if (!window.plugins.barcodeScanner) {
+        window.plugins.barcodeScanner = new BarcodeScanner()
+    }
+    else {
+        console.log("Not installing barcodeScanner: window.plugins.barcodeScanner already exists")
+    }
+})
+
+})();
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/BarcodeScanner.csproj b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/BarcodeScanner.csproj
new file mode 100644
index 0000000..6ad2aa5
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/BarcodeScanner.csproj
@@ -0,0 +1,82 @@
+
+
+  
+    Debug
+    AnyCPU
+    10.0.20506
+    2.0
+    {6B367A3F-E2A0-4425-B5DF-A91452C2F222}
+    {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+    Library
+    Properties
+    BarcodeScanner
+    BarcodeScanner
+    v4.0
+    $(TargetFrameworkVersion)
+    WindowsPhone71
+    Silverlight
+    false
+    true
+    true
+  
+  
+    true
+    full
+    false
+    Bin\Debug
+    DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
+    true
+    true
+    prompt
+    4
+  
+  
+    pdbonly
+    true
+    Bin\Release
+    TRACE;SILVERLIGHT;WINDOWS_PHONE
+    true
+    true
+    prompt
+    4
+  
+  
+    
+    
+    
+    
+    
+    
+    
+  
+  
+    
+      Scanner.xaml
+    
+    
+    
+    
+  
+  
+    
+      {6431CF13-7A7B-4602-B96A-47CDA6F0B008}
+      ZXingVer1_7
+    
+  
+  
+    
+      MSBuild:Compile
+      Designer
+    
+  
+  
+  
+  
+  
+
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/BarcodeScanner.csproj.user b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/BarcodeScanner.csproj.user
new file mode 100644
index 0000000..cba5519
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/BarcodeScanner.csproj.user
@@ -0,0 +1,12 @@
+
+
+  
+    
+      
+        
+          True
+        
+      
+    
+  
+
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/PhotoCameraLuminanceSource.cs b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/PhotoCameraLuminanceSource.cs
new file mode 100644
index 0000000..8ec6ad7
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/PhotoCameraLuminanceSource.cs
@@ -0,0 +1,34 @@
+using System;
+using com.google.zxing;
+
+namespace BarcodeScanner
+{
+    public class PhotoCameraLuminanceSource : LuminanceSource
+    {
+        public byte[] PreviewBufferY { get; private set; }
+
+        public PhotoCameraLuminanceSource(int width, int height)
+            : base(width, height)
+        {
+            PreviewBufferY = new byte[width * height];
+        }
+
+        public override sbyte[] Matrix
+        {
+            get { return (sbyte[])(Array)PreviewBufferY; }
+        }
+
+        public override sbyte[] getRow(int y, sbyte[] row)
+        {
+            if (row == null || row.Length < Width)
+            {
+                row = new sbyte[Width];
+            }
+
+            for (int i = 0; i < Height; i++)
+                row[i] = (sbyte)PreviewBufferY[i * Width + y];
+
+            return row;
+        }
+    }
+}
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Properties/AssemblyInfo.cs b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..753281c
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Resources;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BarcodeScanner")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("BarcodeScanner")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("cd79b6da-fa53-4baa-82b4-72fb6d34ebc8")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: NeutralResourcesLanguageAttribute("en-US")]
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Scanner.xaml b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Scanner.xaml
new file mode 100644
index 0000000..d46b583
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Scanner.xaml
@@ -0,0 +1,49 @@
+
+
+    
+    
+        
+            
+            
+        
+
+        
+            
+                
+                    
+                        
+                    
+                
+            
+        
+        
+        
+        
+    
+ 
+
+
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Scanner.xaml.cs b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Scanner.xaml.cs
new file mode 100644
index 0000000..1df90c8
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/Scanner.xaml.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+
+using Microsoft.Phone.Tasks;
+using System.Windows.Threading;
+using Microsoft.Devices;
+using com.google.zxing;
+using com.google.zxing.common;
+using com.google.zxing.qrcode;
+
+namespace BarcodeScanner
+{
+    public partial class Scanner : PhoneApplicationPage
+    {
+        /// 
+        /// Occurs when a video recording task is completed.
+        /// 
+        public event EventHandler Completed;
+        private ScannerResult result = new ScannerResult(TaskResult.Cancel);
+
+        public Scanner()
+        {
+            InitializeComponent();
+        }
+
+        // opening
+        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
+        {
+            base.OnNavigatedTo(e);
+
+            _photoCamera = new PhotoCamera();
+            _photoCamera.Initialized += OnPhotoCameraInitialized;
+            _previewVideo.SetSource(_photoCamera);
+
+            CameraButtons.ShutterKeyHalfPressed += (o, arg) => _photoCamera.Focus();
+        }
+
+        // closing
+        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
+        {
+            stopCamera();
+
+            if (this.Completed != null)
+            {
+                this.Completed(this, result);
+            }
+
+            base.OnNavigatedFrom(e);
+        }
+
+        private DispatcherTimer _timer;
+
+        private PhotoCameraLuminanceSource _luminance;
+        private QRCodeReader _reader;
+        private PhotoCamera _photoCamera;
+
+        private void OnPhotoCameraInitialized(object sender, CameraOperationCompletedEventArgs e)
+        {
+            Dispatcher.BeginInvoke(() =>
+            {
+                _timer = new DispatcherTimer();
+                _timer.Interval = TimeSpan.FromMilliseconds(250);
+                _timer.Tick += (o, arg) => ScanPreviewBuffer();
+
+                int width = Convert.ToInt32(_photoCamera.PreviewResolution.Width);
+                int height = Convert.ToInt32(_photoCamera.PreviewResolution.Height);
+
+                _luminance = new PhotoCameraLuminanceSource(width, height);
+                _reader = new QRCodeReader();
+            });
+
+            Dispatcher.BeginInvoke(() =>
+            {
+                _previewTransform.Rotation = _photoCamera.Orientation;
+                _timer.Start();
+            });
+        }
+
+        private void ScanPreviewBuffer()
+        {
+            try
+            {
+                _photoCamera.GetPreviewBufferY(_luminance.PreviewBufferY);
+                var binarizer = new HybridBinarizer(_luminance);
+                var binBitmap = new BinaryBitmap(binarizer);
+                var result = _reader.decode(binBitmap);
+
+                Dispatcher.BeginInvoke(() => DisplayResult(result.Text));
+            }
+            catch
+            {
+            }
+        }
+
+        private void DisplayResult(string text)
+        {
+            result = new ScannerResult(Microsoft.Phone.Tasks.TaskResult.OK);
+            result.ScanCode = text;
+            stopCamera();
+            if (this.NavigationService.CanGoBack)
+            {
+                this.NavigationService.GoBack();
+            }
+        }
+
+        private bool stopCamera()
+        {
+            if (_photoCamera == null)
+            {
+                return false;
+            }
+            _timer.Stop();
+            _timer = null;
+            _luminance = null;
+            _reader = null;
+            _photoCamera.Dispose();
+            _photoCamera = null;
+
+            return true;
+        }
+
+        private void _focusButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (_photoCamera != null && _photoCamera.IsFocusSupported)
+            {
+                try
+                {
+                    _photoCamera.CancelFocus();
+                    _photoCamera.Focus();
+                }
+                catch
+                {
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/ScannerResult.cs b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/ScannerResult.cs
new file mode 100644
index 0000000..6e2226c
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/BarcodeScanner/ScannerResult.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+using Microsoft.Phone.Tasks;
+
+namespace BarcodeScanner
+{
+    public class ScannerResult : TaskEventArgs
+    {
+        public ScannerResult() : base()
+        {
+        }
+
+        public ScannerResult(TaskResult result) : base(result)
+        {
+        }
+        /// 
+        /// Scanned QR Code
+        /// 
+        public String ScanCode { get; internal set; }
+    }
+}
diff --git a/WindowsPhone/BarcodeScanner/sources/README.md b/WindowsPhone/BarcodeScanner/sources/README.md
new file mode 100644
index 0000000..8a06b71
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/README.md
@@ -0,0 +1,7 @@
+If you wish to compile BarcodeScanner from sources, attach both projects to your solution and reference ZXingVer1_7 to BarcodeScanner, and your app to BarcodeScanner.
+
+Bugfixes and implementation of 'encode' method is welcome ( i didn't need it so i didn't make it )
+
+BarcodeScanner sources are free to use. ZXingVer1_7 has its own license in source files.
+
+Have fun.
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/AssemblyInfo.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/AssemblyInfo.cs
new file mode 100644
index 0000000..aaf882e
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/AssemblyInfo.cs
@@ -0,0 +1,62 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+// TODO: Review the values of the assembly attributes
+
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Revision
+//      Build Number
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the 
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing. 
+//
+// Notes: 
+//   (*) If no key is specified, the assembly is not signed.
+//   (*) KeyName refers to a key that has been installed in the Crypto Service
+//       Provider (CSP) on your machine. KeyFile refers to a file which contains
+//       a key.
+//   (*) If the KeyFile and the KeyName values are both specified, the 
+//       following processing occurs:
+//       (1) If the KeyName can be found in the CSP, that key is used.
+//       (2) If the KeyName does not exist and the KeyFile does exist, the key 
+//           in the KeyFile is installed into the CSP and used.
+//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+//       When specifying the KeyFile, the location of the KeyFile should be
+//       relative to the project output directory which is
+//       %Project Directory%\obj\. For example, if your KeyFile is
+//       located in the project directory, you would specify the AssemblyKeyFile 
+//       attribute as [assembly: AssemblyKeyFile("..\..\mykey.snk")]
+//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+//       documentation for more information on this.
+//
+
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
+
+
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/BarcodeFormat.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/BarcodeFormat.cs
new file mode 100644
index 0000000..fad4911
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/BarcodeFormat.cs
@@ -0,0 +1,110 @@
+/*
+* Copyright 2007 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  Enumerates barcode formats known to this package.
+	/// 
+	/// 
+	///   Sean Owen
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+	public sealed class BarcodeFormat
+	{
+		public System.String Name
+		{
+			get
+			{
+				return name;
+			}
+			
+		}
+		
+		// No, we can't use an enum here. J2ME doesn't support it.
+		
+		//UPGRADE_NOTE: Final was removed from the declaration of 'VALUES '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+        // private static readonly System.Collections.Hashtable VALUES = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); // commented by .net follower (http://dotnetfollower.com)
+        private static readonly System.Collections.Generic.Dictionary VALUES = new System.Collections.Generic.Dictionary(); // added by .net follower (http://dotnetfollower.com)
+		
+		/// QR Code 2D barcode format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'QR_CODE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat QR_CODE = new BarcodeFormat("QR_CODE");
+		
+		/// DataMatrix 2D barcode format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'DATAMATRIX '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat DATAMATRIX = new BarcodeFormat("DATAMATRIX");
+		
+		/// UPC-E 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'UPC_E '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat UPC_E = new BarcodeFormat("UPC_E");
+		
+		/// UPC-A 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'UPC_A '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat UPC_A = new BarcodeFormat("UPC_A");
+		
+		/// EAN-8 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'EAN_8 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat EAN_8 = new BarcodeFormat("EAN_8");
+		
+		/// EAN-13 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'EAN_13 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13");
+		
+		/// Code 128 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'CODE_128 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128");
+		
+		/// Code 39 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'CODE_39 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39");
+		
+		/// ITF (Interleaved Two of Five) 1D format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'ITF '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat ITF = new BarcodeFormat("ITF");
+		
+		/// PDF417 format. 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'PDF417 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly BarcodeFormat PDF417 = new BarcodeFormat("PDF417");
+		
+		//UPGRADE_NOTE: Final was removed from the declaration of 'name '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		private System.String name;
+		
+		private BarcodeFormat(System.String name)
+		{
+			this.name = name;
+			VALUES[name] = this;
+		}
+		
+		public override System.String ToString()
+		{
+			return name;
+		}
+		
+		public static BarcodeFormat valueOf(System.String name)
+		{
+            // BarcodeFormat format = (BarcodeFormat) VALUES[name]; // commented by .net follower (http://dotnetfollower.com)
+            BarcodeFormat format = VALUES.ContainsKey(name) ? (BarcodeFormat)VALUES[name] : null; // added by .net follower (http://dotnetfollower.com)
+			if (format == null)
+			{
+				throw new System.ArgumentException();
+			}
+			return format;
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Binarizer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Binarizer.cs
new file mode 100644
index 0000000..5e5b928
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Binarizer.cs
@@ -0,0 +1,93 @@
+/*
+* Copyright 2009 ZXing authors
+*
+* 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.
+*/
+using System;
+using BitArray = com.google.zxing.common.BitArray;
+using BitMatrix = com.google.zxing.common.BitMatrix;
+namespace com.google.zxing
+{
+	
+	///  This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
+	/// It allows the algorithm to vary polymorphically, for example allowing a very expensive
+	/// thresholding technique for servers and a fast one for mobile. It also permits the implementation
+	/// to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
+	/// 
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+	public abstract class Binarizer
+	{
+		virtual public LuminanceSource LuminanceSource
+		{
+			get
+			{
+				return source;
+			}
+			
+		}
+		///  Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
+		/// and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
+		/// may not apply sharpening. Therefore, a row from this matrix may not be identical to one
+		/// fetched using getBlackRow(), so don't mix and match between them.
+		/// 
+		/// 
+		///  The 2D array of bits for the image (true means black).
+		/// 
+		public abstract BitMatrix BlackMatrix{get;}
+		
+		//UPGRADE_NOTE: Final was removed from the declaration of 'source '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		private LuminanceSource source;
+		
+		protected internal Binarizer(LuminanceSource source)
+		{
+			if (source == null)
+			{
+				throw new System.ArgumentException("Source must be non-null.");
+			}
+			this.source = source;
+		}
+		
+		///  Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
+		/// cached data. Callers should assume this method is expensive and call it as seldom as possible.
+		/// This method is intended for decoding 1D barcodes and may choose to apply sharpening.
+		/// For callers which only examine one row of pixels at a time, the same BitArray should be reused
+		/// and passed in with each call for performance. However it is legal to keep more than one row
+		/// at a time if needed.
+		/// 
+		/// 
+		/// The row to fetch, 0 <= y < bitmap height.
+		/// 
+		/// An optional preallocated array. If null or too small, it will be ignored.
+		/// If used, the Binarizer will call BitArray.clear(). Always use the returned object.
+		/// 
+		///  The array of bits for this row (true means black).
+		/// 
+		public abstract BitArray getBlackRow(int y, BitArray row);
+		
+		///  Creates a new object with the same type as this Binarizer implementation, but with pristine
+		/// state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
+		/// of 1 bit data. See Effective Java for why we can't use Java's clone() method.
+		/// 
+		/// 
+		/// The LuminanceSource this Binarizer will operate on.
+		/// 
+		///  A new concrete Binarizer implementation object.
+		/// 
+		public abstract Binarizer createBinarizer(LuminanceSource source);
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/BinaryBitmap.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/BinaryBitmap.cs
new file mode 100644
index 0000000..aee771b
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/BinaryBitmap.cs
@@ -0,0 +1,161 @@
+/*
+* Copyright 2009 ZXing authors
+*
+* 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.
+*/
+using System;
+using BitArray = com.google.zxing.common.BitArray;
+using BitMatrix = com.google.zxing.common.BitMatrix;
+namespace com.google.zxing
+{
+	
+	///  This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
+	/// accept a BinaryBitmap and attempt to decode it.
+	/// 
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+	
+	public sealed class BinaryBitmap
+	{
+		///  The width of the bitmap.
+		/// 
+		public int Width
+		{
+			get
+			{
+				return binarizer.LuminanceSource.Width;
+			}
+			
+		}
+		///  The height of the bitmap.
+		/// 
+		public int Height
+		{
+			get
+			{
+				return binarizer.LuminanceSource.Height;
+			}
+			
+		}
+		///  Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
+		/// and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
+		/// may not apply sharpening. Therefore, a row from this matrix may not be identical to one
+		/// fetched using getBlackRow(), so don't mix and match between them.
+		/// 
+		/// 
+		///  The 2D array of bits for the image (true means black).
+		/// 
+		public BitMatrix BlackMatrix
+		{
+			get
+			{
+				// The matrix is created on demand the first time it is requested, then cached. There are two
+				// reasons for this:
+				// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
+				//    1D Reader finds a barcode before the 2D Readers run.
+				// 2. This work will only be done once even if the caller installs multiple 2D Readers.
+				if (matrix == null)
+				{
+					matrix = binarizer.BlackMatrix;
+				}
+				return matrix;
+			}
+			
+		}
+		///  Whether this bitmap can be cropped.
+		/// 
+		public bool CropSupported
+		{
+			get
+			{
+				return binarizer.LuminanceSource.CropSupported;
+			}
+			
+		}
+		///  Whether this bitmap supports counter-clockwise rotation.
+		/// 
+		public bool RotateSupported
+		{
+			get
+			{
+				return binarizer.LuminanceSource.RotateSupported;
+			}
+			
+		}
+		
+		//UPGRADE_NOTE: Final was removed from the declaration of 'binarizer '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		private Binarizer binarizer;
+		private BitMatrix matrix;
+		
+		public BinaryBitmap(Binarizer binarizer)
+		{
+			if (binarizer == null)
+			{
+				throw new System.ArgumentException("Binarizer must be non-null.");
+			}
+			this.binarizer = binarizer;
+			matrix = null;
+		}
+		
+		///  Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
+		/// cached data. Callers should assume this method is expensive and call it as seldom as possible.
+		/// This method is intended for decoding 1D barcodes and may choose to apply sharpening.
+		/// 
+		/// 
+		/// The row to fetch, 0 <= y < bitmap height.
+		/// 
+		/// An optional preallocated array. If null or too small, it will be ignored.
+		/// If used, the Binarizer will call BitArray.clear(). Always use the returned object.
+		/// 
+		///  The array of bits for this row (true means black).
+		/// 
+		public BitArray getBlackRow(int y, BitArray row)
+		{
+			return binarizer.getBlackRow(y, row);
+		}
+		
+		///  Returns a new object with cropped image data. Implementations may keep a reference to the
+		/// original data rather than a copy. Only callable if isCropSupported() is true.
+		/// 
+		/// 
+		/// The left coordinate, 0 <= left < getWidth().
+		/// 
+		/// The top coordinate, 0 <= top <= getHeight().
+		/// 
+		/// The width of the rectangle to crop.
+		/// 
+		/// The height of the rectangle to crop.
+		/// 
+		///  A cropped version of this object.
+		/// 
+		public BinaryBitmap crop(int left, int top, int width, int height)
+		{
+			LuminanceSource newSource = binarizer.LuminanceSource.crop(left, top, width, height);
+			return new BinaryBitmap(binarizer.createBinarizer(newSource));
+		}
+		
+		///  Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
+		/// 
+		/// 
+		///  A rotated version of this object.
+		/// 
+		public BinaryBitmap rotateCounterClockwise()
+		{
+			LuminanceSource newSource = binarizer.LuminanceSource.rotateCounterClockwise();
+			return new BinaryBitmap(binarizer.createBinarizer(newSource));
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/DecodeHintType.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/DecodeHintType.cs
new file mode 100644
index 0000000..ec55fac
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/DecodeHintType.cs
@@ -0,0 +1,78 @@
+/*
+* Copyright 2007 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  Encapsulates a type of hint that a caller may pass to a barcode reader to help it
+	/// more quickly or accurately decode it. It is up to implementations to decide what,
+	/// if anything, to do with the information that is supplied.
+	/// 
+	/// 
+	///   Sean Owen
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+	/// 
+	/// 
+	public sealed class DecodeHintType
+	{
+		
+		// No, we can't use an enum here. J2ME doesn't support it.
+		
+		///  Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
+		//UPGRADE_NOTE: Final was removed from the declaration of 'OTHER '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType OTHER = new DecodeHintType();
+		
+		///  Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
+		/// use {@link Boolean#TRUE}.
+		/// 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'PURE_BARCODE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType PURE_BARCODE = new DecodeHintType();
+		
+		///  Image is known to be of one of a few possible formats.
+		/// Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
+		/// 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'POSSIBLE_FORMATS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
+		
+		///  Spend more time to try to find a barcode; optimize for accuracy, not speed.
+		/// Doesn't matter what it maps to; use {@link Boolean#TRUE}.
+		/// 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'TRY_HARDER '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType TRY_HARDER = new DecodeHintType();
+		
+		///  Allowed lengths of encoded data -- reject anything else. Maps to an int[].
+		//UPGRADE_NOTE: Final was removed from the declaration of 'ALLOWED_LENGTHS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType ALLOWED_LENGTHS = new DecodeHintType();
+		
+		///  Assume Code 39 codes employ a check digit. Maps to {@link Boolean}.
+		//UPGRADE_NOTE: Final was removed from the declaration of 'ASSUME_CODE_39_CHECK_DIGIT '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType();
+		
+		///  The caller needs to be notified via callback when a possible {@link ResultPoint}
+		/// is found. Maps to a {@link ResultPointCallback}.
+		/// 
+		//UPGRADE_NOTE: Final was removed from the declaration of 'NEED_RESULT_POINT_CALLBACK '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType();
+		
+		private DecodeHintType()
+		{
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/EncodeHintType.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/EncodeHintType.cs
new file mode 100644
index 0000000..9060bdc
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/EncodeHintType.cs
@@ -0,0 +1,43 @@
+/*
+* Copyright 2008 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  These are a set of hints that you may pass to Writers to specify their behavior.
+	/// 
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+	public sealed class EncodeHintType
+	{
+		
+		///  Specifies what degree of error correction to use, for example in QR Codes (type Integer).
+		//UPGRADE_NOTE: Final was removed from the declaration of 'ERROR_CORRECTION '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly EncodeHintType ERROR_CORRECTION = new EncodeHintType();
+		
+		///  Specifies what character encoding to use where applicable (type String)
+		//UPGRADE_NOTE: Final was removed from the declaration of 'CHARACTER_SET '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		public static readonly EncodeHintType CHARACTER_SET = new EncodeHintType();
+		
+		private EncodeHintType()
+		{
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/LuminanceSource.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/LuminanceSource.cs
new file mode 100644
index 0000000..e87b428
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/LuminanceSource.cs
@@ -0,0 +1,140 @@
+/*
+* Copyright 2009 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  The purpose of this class hierarchy is to abstract different bitmap implementations across
+	/// platforms into a standard interface for requesting greyscale luminance values. The interface
+	/// only provides immutable methods; therefore crop and rotation create copies. This is to ensure
+	/// that one Reader does not modify the original luminance source and leave it in an unknown state
+	/// for other Readers in the chain.
+	/// 
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+	public abstract class LuminanceSource
+	{
+		///  Fetches luminance data for the underlying bitmap. Values should be fetched using:
+		/// int luminance = array[y * width + x] & 0xff;
+		/// 
+		/// 
+		///  A row-major 2D array of luminance values. Do not use result.length as it may be
+		/// larger than width * height bytes on some platforms. Do not modify the contents
+		/// of the result.
+		/// 
+		public abstract sbyte[] Matrix{get;}
+		///  The width of the bitmap.
+		/// 
+		virtual public int Width
+		{
+			get
+			{
+				return width;
+			}
+			
+		}
+		///  The height of the bitmap.
+		/// 
+		virtual public int Height
+		{
+			get
+			{
+				return height;
+			}
+			
+		}
+		///  Whether this subclass supports cropping.
+		/// 
+		virtual public bool CropSupported
+		{
+			get
+			{
+				return false;
+			}
+			
+		}
+		///  Whether this subclass supports counter-clockwise rotation.
+		/// 
+		virtual public bool RotateSupported
+		{
+			get
+			{
+				return false;
+			}
+			
+		}
+		
+		//UPGRADE_NOTE: Final was removed from the declaration of 'width '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		private int width;
+		//UPGRADE_NOTE: Final was removed from the declaration of 'height '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		private int height;
+		
+		protected internal LuminanceSource(int width, int height)
+		{
+			this.width = width;
+			this.height = height;
+		}
+		
+		///  Fetches one row of luminance data from the underlying platform's bitmap. Values range from
+		/// 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
+		/// to bitwise and with 0xff for each value. It is preferable for implementations of this method
+		/// to only fetch this row rather than the whole image, since no 2D Readers may be installed and
+		/// getMatrix() may never be called.
+		/// 
+		/// 
+		/// The row to fetch, 0 <= y < getHeight().
+		/// 
+		/// An optional preallocated array. If null or too small, it will be ignored.
+		/// Always use the returned object, and ignore the .length of the array.
+		/// 
+		///  An array containing the luminance data.
+		/// 
+		public abstract sbyte[] getRow(int y, sbyte[] row);
+		
+		///  Returns a new object with cropped image data. Implementations may keep a reference to the
+		/// original data rather than a copy. Only callable if isCropSupported() is true.
+		/// 
+		/// 
+		/// The left coordinate, 0 <= left < getWidth().
+		/// 
+		/// The top coordinate, 0 <= top <= getHeight().
+		/// 
+		/// The width of the rectangle to crop.
+		/// 
+		/// The height of the rectangle to crop.
+		/// 
+		///  A cropped version of this object.
+		/// 
+		public virtual LuminanceSource crop(int left, int top, int width, int height)
+		{
+			throw new System.SystemException("This luminance source does not support cropping.");
+		}
+		
+		///  Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
+		/// 
+		/// 
+		///  A rotated version of this object.
+		/// 
+		public virtual LuminanceSource rotateCounterClockwise()
+		{
+			throw new System.SystemException("This luminance source does not support rotation.");
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/MultiFormatReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/MultiFormatReader.cs
new file mode 100644
index 0000000..b376ac4
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/MultiFormatReader.cs
@@ -0,0 +1,184 @@
+/*
+* Copyright 2007 ZXing authors
+*
+* 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.
+*/
+using System;
+using MultiFormatOneDReader = com.google.zxing.oned.MultiFormatOneDReader;
+using PDF417Reader = com.google.zxing.pdf417.PDF417Reader;
+using QRCodeReader = com.google.zxing.qrcode.QRCodeReader;
+using DataMatrixReader = com.google.zxing.datamatrix.DataMatrixReader;
+namespace com.google.zxing
+{
+	
+	///  MultiFormatReader is a convenience class and the main entry point into the library for most uses.
+	/// By default it attempts to decode all barcode formats that the library supports. Optionally, you
+	/// can provide a hints object to request different behavior, for example only decoding QR codes.
+	/// 
+	/// 
+	///   Sean Owen
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+	public sealed class MultiFormatReader : Reader
+	{
+		///  This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls
+		/// to decodeWithState(image) can reuse the same set of readers without reallocating memory. This
+		/// is important for performance in continuous scan clients.
+		/// 
+		/// 
+		/// The set of hints to use for subsequent calls to decode(image)
+		/// 
+        // public System.Collections.Hashtable Hints // commented by .net follower (http://dotnetfollower.com)
+        public System.Collections.Generic.Dictionary Hints // added by .net follower (http://dotnetfollower.com)
+		{
+			set
+			{
+				this.hints = value;
+				
+				bool tryHarder = value != null && value.ContainsKey(DecodeHintType.TRY_HARDER);
+                // System.Collections.ArrayList formats = value == null?null:(System.Collections.ArrayList) value[DecodeHintType.POSSIBLE_FORMATS]; // commented by .net follower (http://dotnetfollower.com)
+                System.Collections.Generic.List formats = null; // added by .net follower (http://dotnetfollower.com)
+                if (value != null && value.ContainsKey(DecodeHintType.POSSIBLE_FORMATS)) // added by .net follower (http://dotnetfollower.com)
+                    formats = (System.Collections.Generic.List)value[DecodeHintType.POSSIBLE_FORMATS]; // added by .net follower (http://dotnetfollower.com)
+
+                // readers = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com)
+                readers = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com)
+				if (formats != null)
+				{
+					bool addOneDReader = formats.Contains(BarcodeFormat.UPC_A) || formats.Contains(BarcodeFormat.UPC_E) || formats.Contains(BarcodeFormat.EAN_13) || formats.Contains(BarcodeFormat.EAN_8) || formats.Contains(BarcodeFormat.CODE_39) || formats.Contains(BarcodeFormat.CODE_128) || formats.Contains(BarcodeFormat.ITF);
+					// Put 1D readers upfront in "normal" mode
+					if (addOneDReader && !tryHarder)
+					{
+						readers.Add(new MultiFormatOneDReader(value));
+					}
+					if (formats.Contains(BarcodeFormat.QR_CODE))
+					{
+						readers.Add(new QRCodeReader());
+					}
+					if (formats.Contains(BarcodeFormat.DATAMATRIX))
+					{
+						readers.Add(new DataMatrixReader());
+					}
+					if (formats.Contains(BarcodeFormat.PDF417))
+					{
+						readers.Add(new PDF417Reader());
+					}
+					// At end in "try harder" mode
+					if (addOneDReader && tryHarder)
+					{
+						readers.Add(new MultiFormatOneDReader(value));
+					}
+				}
+				if ((readers.Count == 0))
+				{
+					if (!tryHarder)
+					{
+						readers.Add(new MultiFormatOneDReader(value));
+					}
+					readers.Add(new QRCodeReader());
+					
+					// TODO re-enable once Data Matrix is ready
+					// readers.addElement(new DataMatrixReader());
+					
+					// TODO: Enable once PDF417 has passed QA
+					//readers.addElement(new PDF417Reader());
+					
+					if (tryHarder)
+					{
+						readers.Add(new MultiFormatOneDReader(value));
+					}
+				}
+			}
+			
+		}
+
+        // private System.Collections.Hashtable hints; // commented by .net follower (http://dotnetfollower.com)
+        private System.Collections.Generic.Dictionary hints; // added by .net follower (http://dotnetfollower.com)
+        // private System.Collections.ArrayList readers; // commented by .net follower (http://dotnetfollower.com)
+        private System.Collections.Generic.List readers; // added by .net follower (http://dotnetfollower.com)
+		
+		///  This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it
+		/// passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly.
+		/// Use setHints() followed by decodeWithState() for continuous scan applications.
+		/// 
+		/// 
+		/// The pixel data to decode
+		/// 
+		///  The contents of the image
+		/// 
+		///   ReaderException Any errors which occurred 
+		public Result decode(BinaryBitmap image)
+		{
+			Hints = null;
+			return decodeInternal(image);
+		}
+		
+		///  Decode an image using the hints provided. Does not honor existing state.
+		/// 
+		/// 
+		/// The pixel data to decode
+		/// 
+		/// The hints to use, clearing the previous state.
+		/// 
+		///  The contents of the image
+		/// 
+		///   ReaderException Any errors which occurred 
+        // public Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com)
+        public Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com)
+		{
+			Hints = hints;
+			return decodeInternal(image);
+		}
+		
+		///  Decode an image using the state set up by calling setHints() previously. Continuous scan
+		/// clients will get a large speed increase by using this instead of decode().
+		/// 
+		/// 
+		/// The pixel data to decode
+		/// 
+		///  The contents of the image
+		/// 
+		///   ReaderException Any errors which occurred 
+		public Result decodeWithState(BinaryBitmap image)
+		{
+			// Make sure to set up the default state so we don't crash
+			if (readers == null)
+			{
+				Hints = null;
+			}
+			return decodeInternal(image);
+		}
+		
+		private Result decodeInternal(BinaryBitmap image)
+		{
+			int size = readers.Count;
+			for (int i = 0; i < size; i++)
+			{
+				Reader reader = (Reader) readers[i];
+				try
+				{
+					return reader.decode(image, hints);
+				}
+				catch (ReaderException re)
+				{
+					// continue
+				}
+			}
+			
+			throw ReaderException.Instance;
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/MultiFormatWriter.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/MultiFormatWriter.cs
new file mode 100644
index 0000000..274b5cb
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/MultiFormatWriter.cs
@@ -0,0 +1,64 @@
+/*
+* Copyright 2008 ZXing authors
+*
+* 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.
+*/
+using System;
+using ByteMatrix = com.google.zxing.common.ByteMatrix;
+using EAN13Writer = com.google.zxing.oned.EAN13Writer;
+using EAN8Writer = com.google.zxing.oned.EAN8Writer;
+using QRCodeWriter = com.google.zxing.qrcode.QRCodeWriter;
+namespace com.google.zxing
+{
+	
+	///  This is a factory class which finds the appropriate Writer subclass for the BarcodeFormat
+	/// requested and encodes the barcode with the supplied contents.
+	/// 
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+	public sealed class MultiFormatWriter : Writer
+	{
+		
+		public ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height)
+		{
+			
+			return encode(contents, format, width, height, null);
+		}
+
+        // public ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com)
+        public ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com)
+		{
+			
+			if (format == BarcodeFormat.EAN_8)
+			{
+				return new EAN8Writer().encode(contents, format, width, height, hints);
+			}
+			else if (format == BarcodeFormat.EAN_13)
+			{
+				return new EAN13Writer().encode(contents, format, width, height, hints);
+			}
+			else if (format == BarcodeFormat.QR_CODE)
+			{
+				return new QRCodeWriter().encode(contents, format, width, height, hints);
+			}
+			else
+			{
+				throw new System.ArgumentException("No encoder available for format " + format);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Properties/AssemblyInfo.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..14cb369
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ZXingVer1_7")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ZXingVer1_7")]
+[assembly: AssemblyCopyright("Copyright ©  2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a2fff5dc-a308-495f-ac5a-97cb94860601")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/RGBLuminanceSource.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/RGBLuminanceSource.cs
new file mode 100644
index 0000000..da3ebd3
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/RGBLuminanceSource.cs
@@ -0,0 +1,174 @@
+using com.google.zxing;
+using com.google.zxing.common;
+// using System.Drawing.Imaging; // commented by .net follower (http://dotnetfollower.com)
+// using System.Drawing; // commented by .net follower (http://dotnetfollower.com)
+using System;
+
+public class RGBLuminanceSource : LuminanceSource
+{
+
+    private sbyte[] luminances;
+    private bool isRotated = false;
+    private bool __isRegionSelect = false;
+    // private Rectangle __Region; // commented by .net follower (http://dotnetfollower.com)
+    private System.Windows.Rect __Region; // added by .net follower (http://dotnetfollower.com)
+
+    override public int Height
+    {
+        get
+        {
+            if (!isRotated)
+                return __height;
+            else
+                return __width;
+        }
+
+    }
+    override public int Width
+    {
+        get
+        {
+            if (!isRotated)
+                return __width;
+            else
+                return __height;
+        }
+
+    }
+    private int __height;
+    private int __width;
+
+    public RGBLuminanceSource(byte[] d, int W, int H)
+        : base(W, H)
+    {
+        __width = W;
+        __height = H;
+        int width = W;
+        int height = H;
+        // In order to measure pure decoding speed, we convert the entire image to a greyscale array
+        // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
+        luminances = new sbyte[width * height];
+        for (int y = 0; y < height; y++)
+        {
+            int offset = y * width;
+            for (int x = 0; x < width; x++)
+            {
+                int r = d[offset * 3 + x * 3];
+                int g = d[offset * 3 + x * 3 + 1];
+                int b = d[offset * 3 + x * 3 + 2];
+                if (r == g && g == b)
+                {
+                    // Image is already greyscale, so pick any channel.
+                    luminances[offset + x] = (sbyte)r;
+                }
+                else
+                {
+                    // Calculate luminance cheaply, favoring green.
+                    luminances[offset + x] = (sbyte)((r + g + g + b) >> 2);
+                }
+            }
+        }
+    }
+    public RGBLuminanceSource(byte[] d, int W, int H,bool Is8Bit)
+        : base(W, H)
+    {
+        __width = W;
+        __height = H;
+        luminances = new sbyte[W * H];
+        Buffer.BlockCopy(d,0, luminances,0, W * H);
+    }
+
+    // public RGBLuminanceSource(byte[] d, int W, int H, bool Is8Bit,Rectangle Region) // commented by .net follower (http://dotnetfollower.com)
+    public RGBLuminanceSource(byte[] d, int W, int H, bool Is8Bit, System.Windows.Rect Region) // added by .net follower (http://dotnetfollower.com)
+        : base(W, H)
+    {
+        // __width = Region.Width; // commented by .net follower (http://dotnetfollower.com)
+        // __height = Region.Height; // commented by .net follower (http://dotnetfollower.com)
+        __width = (int)Region.Width; // added by .net follower (http://dotnetfollower.com)
+        __height = (int)Region.Height; // added by .net follower (http://dotnetfollower.com)
+        __Region = Region;
+        __isRegionSelect = true;
+        //luminances = Red.Imaging.Filters.CropArea(d, W, H, Region);
+    }
+
+
+    // public RGBLuminanceSource(Bitmap d, int W, int H) // commented by .net follower (http://dotnetfollower.com)
+    public RGBLuminanceSource(System.Windows.Media.Imaging.WriteableBitmap d, int W, int H) // added by .net follower (http://dotnetfollower.com)        
+        : base(W, H)
+    {
+        int width = __width = W;
+        int height = __height = H;
+        // In order to measure pure decoding speed, we convert the entire image to a greyscale array
+        // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
+        luminances = new sbyte[width * height];
+        //if (format == PixelFormat.Format8bppIndexed)
+        {
+            // Color c; // commented by .net follower (http://dotnetfollower.com)
+            System.Windows.Media.Color c; // added by .net follower (http://dotnetfollower.com)        
+            for (int y = 0; y < height; y++)
+            {
+                int offset = y * width;
+                for (int x = 0; x < width; x++)
+                {
+                    // c = d.GetPixel(x, y); // commented by .net follower (http://dotnetfollower.com)
+                    int srcPixel = d.Pixels[x + (width * y)]; // added by .net follower (http://dotnetfollower.com)        
+                    c = System.Windows.Media.Color.FromArgb((byte)((srcPixel >> 0x18) & 0xff), // added by .net follower (http://dotnetfollower.com)        
+                          (byte)((srcPixel >> 0x10) & 0xff), // added by .net follower (http://dotnetfollower.com)        
+                          (byte)((srcPixel >> 8) & 0xff), // added by .net follower (http://dotnetfollower.com)        
+                          (byte)(srcPixel & 0xff)); // added by .net follower (http://dotnetfollower.com)        
+                    luminances[offset + x] = (sbyte)(((int)c.R) << 16 | ((int)c.G) << 8 | ((int)c.B));
+                }
+            }
+        }
+    }
+    override public sbyte[] getRow(int y, sbyte[] row)
+    {
+        if (isRotated == false)
+        {
+            int width = Width;
+            if (row == null || row.Length < width)
+            {
+                row = new sbyte[width];
+            }
+            for (int i = 0; i < width; i++)
+                row[i] = luminances[y * width + i];
+            //System.arraycopy(luminances, y * width, row, 0, width);
+            return row;
+        }
+        else
+        {
+            int width = __width;
+            int height = __height;
+            if (row == null || row.Length < height)
+            {
+                row = new sbyte[height];
+            }
+            for (int i = 0; i < height; i++)
+                row[i] = luminances[i * width + y];
+            //System.arraycopy(luminances, y * width, row, 0, width);
+            return row;
+        }
+    }
+    public override sbyte[] Matrix
+    {
+        get { return luminances; }
+    }
+
+    public override LuminanceSource crop(int left, int top, int width, int height)
+    {
+        return base.crop(left, top, width, height);
+    }
+    public override LuminanceSource rotateCounterClockwise()
+    {
+        isRotated = true;
+        return this;
+    }
+    public override bool RotateSupported
+    {
+        get
+        {
+            return true;
+        }
+
+    }
+}
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Reader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Reader.cs
new file mode 100644
index 0000000..2afe5b5
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Reader.cs
@@ -0,0 +1,66 @@
+/*
+* Copyright 2007 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  Implementations of this interface can decode an image of a barcode in some format into
+	/// the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
+	/// decode a QR code. The decoder may optionally receive hints from the caller which may help
+	/// it decode more quickly or accurately.
+	/// 
+	/// See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode
+	/// format is present within the image as well, and then decodes it accordingly.
+	/// 
+	/// 
+	///   Sean Owen
+	/// 
+	///   dswitkin@google.com (Daniel Switkin)
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+	public interface Reader
+	{
+		
+		///  Locates and decodes a barcode in some format within an image.
+		/// 
+		/// 
+		/// image of barcode to decode
+		/// 
+		///  String which the barcode encodes
+		/// 
+		///   ReaderException if the barcode cannot be located or decoded for any reason 
+		Result decode(BinaryBitmap image);
+		
+		///  Locates and decodes a barcode in some format within an image. This method also accepts
+		/// hints, each possibly associated to some data, which may help the implementation decode.
+		/// 
+		/// 
+		/// image of barcode to decode
+		/// 
+		/// passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType}
+		/// to arbitrary data. The
+		/// meaning of the data depends upon the hint type. The implementation may or may not do
+		/// anything with these hints.
+		/// 
+		///  String which the barcode encodes
+		/// 
+		///   ReaderException if the barcode cannot be located or decoded for any reason 
+        // Result decode(BinaryBitmap image, System.Collections.Hashtable hints); // commented by .net follower (http://dotnetfollower.com)
+        Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints); // added by .net follower (http://dotnetfollower.com)
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ReaderException.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ReaderException.cs
new file mode 100644
index 0000000..33f00ae
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ReaderException.cs
@@ -0,0 +1,113 @@
+/*
+* Copyright 2007 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  The general exception class throw when something goes wrong during decoding of a barcode.
+	/// This includes, but is not limited to, failing checksums / error correction algorithms, being
+	/// unable to locate finder timing patterns, and so on.
+	/// 
+	/// 
+	///   Sean Owen
+	/// 
+	/// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
+	/// 
+
+    // [Serializable] // commented by .net follower (http://dotnetfollower.com)
+	public sealed class ReaderException:System.Exception
+	{
+		public static ReaderException Instance
+		{
+			get
+			{
+				//    Exception e = new Exception();
+				//    // Take the stack frame before this one.
+				//    StackTraceElement stack = e.getStackTrace()[1];
+				//    String key = stack.getClassName() + "." + stack.getMethodName() + "(), line " +
+				//        stack.getLineNumber();
+				//    if (throwers.containsKey(key)) {
+				//      Integer value = throwers.get(key);
+				//      value++;
+				//      throwers.put(key, value);
+				//    } else {
+				//      throwers.put(key, 1);
+				//    }
+				//    exceptionCount++;
+				
+				return instance;
+			}
+			
+		}
+		
+		// TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before
+		// rejecting it. This involves a lot of overhead and memory allocation, and affects both performance
+		// and latency on continuous scan clients. In the future, we should change all the decoders not to
+		// throw exceptions for routine events, like not finding a barcode on a given row. Instead, we
+		// should return error codes back to the callers, and simply delete this class. In the mean time, I
+		// have altered this class to be as lightweight as possible, by ignoring the exception string, and
+		// by disabling the generation of stack traces, which is especially time consuming. These are just
+		// temporary measures, pending the big cleanup.
+		
+		//UPGRADE_NOTE: Final was removed from the declaration of 'instance '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
+		private static readonly ReaderException instance = new ReaderException();
+		
+		// EXCEPTION TRACKING SUPPORT
+		// Identifies who is throwing exceptions and how often. To use:
+		//
+		// 1. Uncomment these lines and the code below which uses them.
+		// 2. Uncomment the two corresponding lines in j2se/CommandLineRunner.decode()
+		// 3. Change core to build as Java 1.5 temporarily
+		//  private static int exceptionCount = 0;
+		//  private static Map throwers = new HashMap(32);
+		
+		private ReaderException()
+		{
+			// do nothing
+		}
+		
+		//  public static int getExceptionCountAndReset() {
+		//    int temp = exceptionCount;
+		//    exceptionCount = 0;
+		//    return temp;
+		//  }
+		//
+		//  public static String getThrowersAndReset() {
+		//    StringBuilder builder = new StringBuilder(1024);
+		//    Object[] keys = throwers.keySet().toArray();
+		//    for (int x = 0; x < keys.length; x++) {
+		//      String key = (String) keys[x];
+		//      Integer value = throwers.get(key);
+		//      builder.append(key);
+		//      builder.append(": ");
+		//      builder.append(value);
+		//      builder.append("\n");
+		//    }
+		//    throwers.clear();
+		//    return builder.toString();
+		//  }
+		
+		// Prevent stack traces from being taken
+		// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
+		// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
+		//UPGRADE_NOTE: Exception 'java.lang.Throwable' was converted to 'System.Exception' which has different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1100'"
+		//UPGRADE_NOTE: The equivalent of method 'java.lang.Throwable.fillInStackTrace' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
+		public System.Exception fillInStackTrace()
+		{
+			return null;
+		}
+	}
+}
\ No newline at end of file
diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Result.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Result.cs
new file mode 100644
index 0000000..c1552fb
--- /dev/null
+++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Result.cs
@@ -0,0 +1,132 @@
+/*
+* Copyright 2007 ZXing authors
+*
+* 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.
+*/
+using System;
+namespace com.google.zxing
+{
+	
+	///  

Encapsulates the result of decoding a barcode within an image.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public sealed class Result + { + /// raw text encoded by the barcode, if applicable, otherwise null + /// + public System.String Text + { + get + { + return text; + } + + } + /// raw bytes encoded by the barcode, if applicable, otherwise null + /// + public sbyte[] RawBytes + { + get + { + return rawBytes; + } + + } + /// points related to the barcode in the image. These are typically points + /// identifying finder patterns or the corners of the barcode. The exact meaning is + /// specific to the type of barcode that was decoded. + /// + public ResultPoint[] ResultPoints + { + get + { + return resultPoints; + } + + } + /// {@link BarcodeFormat} representing the format of the barcode that was decoded + /// + public BarcodeFormat BarcodeFormat + { + get + { + return format; + } + + } + /// {@link Hashtable} mapping {@link ResultMetadataType} keys to values. May be + /// null. This contains optional metadata about what was detected about the barcode, + /// like orientation. + /// + // public System.Collections.Hashtable ResultMetadata // commented by .net follower (http://dotnetfollower.com) + public System.Collections.Generic.Dictionary ResultMetadata // added by .net follower (http://dotnetfollower.com) + { + get + { + return resultMetadata; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'text '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String text; + //UPGRADE_NOTE: Final was removed from the declaration of 'rawBytes '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[] rawBytes; + //UPGRADE_NOTE: Final was removed from the declaration of 'resultPoints '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPoint[] resultPoints; + //UPGRADE_NOTE: Final was removed from the declaration of 'format '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BarcodeFormat format; + // private System.Collections.Hashtable resultMetadata; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.Dictionary resultMetadata; // added by .net follower (http://dotnetfollower.com) + + public Result(System.String text, sbyte[] rawBytes, ResultPoint[] resultPoints, BarcodeFormat format) + { + if (text == null && rawBytes == null) + { + throw new System.ArgumentException("Text and bytes are null"); + } + this.text = text; + this.rawBytes = rawBytes; + this.resultPoints = resultPoints; + this.format = format; + this.resultMetadata = null; + } + + public void putMetadata(ResultMetadataType type, System.Object value_Renamed) + { + if (resultMetadata == null) + { + // resultMetadata = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable(3)); // commented by .net follower (http://dotnetfollower.com) + resultMetadata = new System.Collections.Generic.Dictionary(3); // added by .net follower (http://dotnetfollower.com) + } + resultMetadata[type] = value_Renamed; + } + + public override System.String ToString() + { + if (text == null) + { + return "[" + rawBytes.Length + " bytes]"; + } + else + { + return text; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultMetadataType.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultMetadataType.cs new file mode 100644 index 0000000..d57ae27 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultMetadataType.cs @@ -0,0 +1,68 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing +{ + + /// Represents some type of metadata about the result of the decoding that the decoder + /// wishes to communicate back to the caller. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public sealed class ResultMetadataType + { + + // No, we can't use an enum here. J2ME doesn't support it. + + /// Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. + //UPGRADE_NOTE: Final was removed from the declaration of 'OTHER '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ResultMetadataType OTHER = new ResultMetadataType(); + + /// Denotes the likely approximate orientation of the barcode in the image. This value + /// is given as degrees rotated clockwise from the normal, upright orientation. + /// For example a 1D barcode which was found by reading top-to-bottom would be + /// said to have orientation "90". This key maps to an {@link Integer} whose + /// value is in the range [0,360). + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'ORIENTATION '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ResultMetadataType ORIENTATION = new ResultMetadataType(); + + ///

2D barcode formats typically encode text, but allow for a sort of 'byte mode' + /// which is sometimes used to encode binary data. While {@link Result} makes available + /// the complete raw bytes in the barcode for these formats, it does not offer the bytes + /// from the byte segments alone.

+ /// + ///

This maps to a {@link java.util.Vector} of byte arrays corresponding to the + /// raw bytes in the byte segments in the barcode, in order.

+ ///
+ //UPGRADE_NOTE: Final was removed from the declaration of 'BYTE_SEGMENTS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType(); + + /// Error correction level used, if applicable. The value type depends on the + /// format, but is typically a String. + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'ERROR_CORRECTION_LEVEL '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType(); + + private ResultMetadataType() + { + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultPoint.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultPoint.cs new file mode 100644 index 0000000..def8e2f --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultPoint.cs @@ -0,0 +1,157 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing +{ + + ///

Encapsulates a point of interest in an image containing a barcode. Typically, this + /// would be the location of a finder pattern or the corner of the barcode, for example.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public class ResultPoint + { + virtual public float X + { + get + { + return x; + } + + } + virtual public float Y + { + get + { + return y; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'x '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private float x; + //UPGRADE_NOTE: Final was removed from the declaration of 'y '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private float y; + + public ResultPoint(float x, float y) + { + this.x = x; + this.y = y; + } + + public override bool Equals(System.Object other) + { + if (other is ResultPoint) + { + ResultPoint otherPoint = (ResultPoint) other; + return x == otherPoint.x && y == otherPoint.y; + } + return false; + } + + public override int GetHashCode() + { + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // Commented function body + + //return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y); + return 0; + } + + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(25); + result.Append('('); + result.Append(x); + result.Append(','); + result.Append(y); + result.Append(')'); + return result.ToString(); + } + + ///

Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and + /// BC < AC and the angle between BC and BA is less than 180 degrees. + ///

+ public static void orderBestPatterns(ResultPoint[] patterns) + { + + // Find distances between pattern centers + float zeroOneDistance = distance(patterns[0], patterns[1]); + float oneTwoDistance = distance(patterns[1], patterns[2]); + float zeroTwoDistance = distance(patterns[0], patterns[2]); + + ResultPoint pointA, pointB, pointC; + // Assume one closest to other two is B; A and C will just be guesses at first + if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) + { + pointB = patterns[0]; + pointA = patterns[1]; + pointC = patterns[2]; + } + else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) + { + pointB = patterns[1]; + pointA = patterns[0]; + pointC = patterns[2]; + } + else + { + pointB = patterns[2]; + pointA = patterns[0]; + pointC = patterns[1]; + } + + // Use cross product to figure out whether A and C are correct or flipped. + // This asks whether BC x BA has a positive z component, which is the arrangement + // we want for A, B, C. If it's negative, then we've got it flipped around and + // should swap A and C. + if (crossProductZ(pointA, pointB, pointC) < 0.0f) + { + ResultPoint temp = pointA; + pointA = pointC; + pointC = temp; + } + + patterns[0] = pointA; + patterns[1] = pointB; + patterns[2] = pointC; + } + + + /// distance between two points + /// + public static float distance(ResultPoint pattern1, ResultPoint pattern2) + { + float xDiff = pattern1.X - pattern2.X; + float yDiff = pattern1.Y - pattern2.Y; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (float) System.Math.Sqrt((double) (xDiff * xDiff + yDiff * yDiff)); + } + + /// Returns the z component of the cross product between vectors BC and BA. + private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) + { + float bX = pointB.x; + float bY = pointB.y; + return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX)); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultPointCallback.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultPointCallback.cs new file mode 100644 index 0000000..e736c0a --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ResultPointCallback.cs @@ -0,0 +1,31 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing +{ + + /// Callback which is invoked when a possible result point (significant + /// point in the barcode image such as a corner) is found. + /// + /// + /// + /// + public interface ResultPointCallback + { + + void foundPossibleResultPoint(ResultPoint point); + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/SupportClass.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/SupportClass.cs new file mode 100644 index 0000000..b5a423c --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/SupportClass.cs @@ -0,0 +1,216 @@ +// +// In order to convert some functionality to Visual C#, the Java Language Conversion Assistant +// creates "support classes" that duplicate the original functionality. +// +// Support classes replicate the functionality of the original code, but in some cases they are +// substantially different architecturally. Although every effort is made to preserve the +// original architecture of the application in the converted project, the user should be aware that +// the primary goal of these support classes is to replicate functionality, and that at times +// the architecture of the resulting solution may differ somewhat. +// + +using System; + +/// +/// Contains conversion support elements such as classes, interfaces and static methods. +/// +public class SupportClass +{ + /// + /// Converts an array of sbytes to an array of bytes + /// + /// The array of sbytes to be converted + /// The new array of bytes + public static byte[] ToByteArray(sbyte[] sbyteArray) + { + byte[] byteArray = null; + + if (sbyteArray != null) + { + byteArray = new byte[sbyteArray.Length]; + for(int index=0; index < sbyteArray.Length; index++) + byteArray[index] = (byte) sbyteArray[index]; + } + return byteArray; + } + + /// + /// Converts a string to an array of bytes + /// + /// The string to be converted + /// The new array of bytes + public static byte[] ToByteArray(System.String sourceString) + { + return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString); + } + + /// + /// Converts a array of object-type instances to a byte-type array. + /// + /// Array to convert. + /// An array of byte type elements. + public static byte[] ToByteArray(System.Object[] tempObjectArray) + { + byte[] byteArray = null; + if (tempObjectArray != null) + { + byteArray = new byte[tempObjectArray.Length]; + for (int index = 0; index < tempObjectArray.Length; index++) + byteArray[index] = (byte)tempObjectArray[index]; + } + return byteArray; + } + + /*******************************/ + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, int bits) + { + if ( number >= 0) + return number >> bits; + else + return (number >> bits) + (2 << ~bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, long bits) + { + return URShift(number, (int)bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, int bits) + { + if ( number >= 0) + return number >> bits; + else + return (number >> bits) + (2L << ~bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, long bits) + { + return URShift(number, (int)bits); + } + + /*******************************/ + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static long Identity(long literal) + { + return literal; + } + + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static ulong Identity(ulong literal) + { + return literal; + } + + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static float Identity(float literal) + { + return literal; + } + + /// + /// This method returns the literal value received + /// + /// The literal to return + /// The received value + public static double Identity(double literal) + { + return literal; + } + + /*******************************/ + /// + /// Copies an array of chars obtained from a String into a specified array of chars + /// + /// The String to get the chars from + /// Position of the String to start getting the chars + /// Position of the String to end getting the chars + /// Array to return the chars + /// Position of the destination array of chars to start storing the chars + /// An array of chars + public static void GetCharsFromString(System.String sourceString, int sourceStart, int sourceEnd, char[] destinationArray, int destinationStart) + { + int sourceCounter; + int destinationCounter; + sourceCounter = sourceStart; + destinationCounter = destinationStart; + while (sourceCounter < sourceEnd) + { + destinationArray[destinationCounter] = (char) sourceString[sourceCounter]; + sourceCounter++; + destinationCounter++; + } + } + + /*******************************/ + /// + /// Sets the capacity for the specified ArrayList + /// + /// The ArrayList which capacity will be set + /// The new capacity value + // public static void SetCapacity(System.Collections.ArrayList vector, int newCapacity) // commented by .net follower (http://dotnetfollower.com) + public static void SetCapacity(System.Collections.Generic.List vector, int newCapacity) // added by .net follower (http://dotnetfollower.com) + { + if (newCapacity > vector.Count) + vector.AddRange(new Array[newCapacity-vector.Count]); + else if (newCapacity < vector.Count) + vector.RemoveRange(newCapacity, vector.Count - newCapacity); + vector.Capacity = newCapacity; + } + + + + /*******************************/ + /// + /// Receives a byte array and returns it transformed in an sbyte array + /// + /// Byte array to process + /// The transformed array + public static sbyte[] ToSByteArray(byte[] byteArray) + { + sbyte[] sbyteArray = null; + if (byteArray != null) + { + sbyteArray = new sbyte[byteArray.Length]; + for(int index=0; index < byteArray.Length; index++) + sbyteArray[index] = (sbyte) byteArray[index]; + } + return sbyteArray; + } + +} diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Writer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Writer.cs new file mode 100644 index 0000000..fa48f43 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/Writer.cs @@ -0,0 +1,63 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +namespace com.google.zxing +{ + + /// The base class for all objects which encode/generate a barcode image. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public interface Writer + { + + /// Encode a barcode using the default settings. + /// + /// + /// The contents to encode in the barcode + /// + /// The barcode format to generate + /// + /// The preferred width in pixels + /// + /// The preferred height in pixels + /// + /// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) + /// + ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height); + + /// + /// The contents to encode in the barcode + /// + /// The barcode format to generate + /// + /// The preferred width in pixels + /// + /// The preferred height in pixels + /// + /// Additional parameters to supply to the encoder + /// + /// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) + /// + // ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Hashtable hints); // commented by .net follower (http://dotnetfollower.com) + ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints); // added by .net follower (http://dotnetfollower.com) + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/WriterException.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/WriterException.cs new file mode 100644 index 0000000..1188165 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/WriterException.cs @@ -0,0 +1,40 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing +{ + + /// A base class which covers the range of exceptions which may occur when encoding a barcode using + /// the Writer framework. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + // [Serializable] // commented by .net follower (http://dotnetfollower.com) + public sealed class WriterException:System.Exception + { + + public WriterException():base() + { + } + + public WriterException(System.String message):base(message) + { + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ZXingVer1_7.csproj b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ZXingVer1_7.csproj new file mode 100644 index 0000000..326911d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/ZXingVer1_7.csproj @@ -0,0 +1,203 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {6431CF13-7A7B-4602-B96A-47CDA6F0B008} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + ZXingVer1_7 + ZXingVer1_7 + v4.0 + $(TargetFrameworkVersion) + WindowsPhone + Silverlight + false + true + true + + + + + + + + + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AbstractDoCoMoResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AbstractDoCoMoResultParser.cs new file mode 100644 index 0000000..c364098 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AbstractDoCoMoResultParser.cs @@ -0,0 +1,45 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + ///

See + /// + /// DoCoMo's documentation about the result types represented by subclasses of this class.

+ /// + ///

Thanks to Jeff Griffin for proposing rewrite of these classes that relies less + /// on exception-based mechanisms during parsing.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + abstract class AbstractDoCoMoResultParser:ResultParser + { + + internal static System.String[] matchDoCoMoPrefixedField(System.String prefix, System.String rawText, bool trim) + { + return matchPrefixedField(prefix, rawText, ';', trim); + } + + internal static System.String matchSingleDoCoMoPrefixedField(System.String prefix, System.String rawText, bool trim) + { + return matchSinglePrefixedField(prefix, rawText, ';', trim); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookAUResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookAUResultParser.cs new file mode 100644 index 0000000..85a5a4d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookAUResultParser.cs @@ -0,0 +1,81 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Implements KDDI AU's address book format. See + /// + /// http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html. + /// (Thanks to Yuzo for translating!) + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class AddressBookAUResultParser:ResultParser + { + + public static AddressBookParsedResult parse(Result result) + { + System.String rawText = result.Text; + // MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF + if (rawText == null || rawText.IndexOf("MEMORY") < 0 || rawText.IndexOf("\r\n") < 0) + { + return null; + } + + // NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively. + // Therefore we treat them specially instead of as an array of names. + System.String name = matchSinglePrefixedField("NAME1:", rawText, '\r', true); + System.String pronunciation = matchSinglePrefixedField("NAME2:", rawText, '\r', true); + + System.String[] phoneNumbers = matchMultipleValuePrefix("TEL", 3, rawText, true); + System.String[] emails = matchMultipleValuePrefix("MAIL", 3, rawText, true); + System.String note = matchSinglePrefixedField("MEMORY:", rawText, '\r', false); + System.String address = matchSinglePrefixedField("ADD:", rawText, '\r', true); + System.String[] addresses = address == null?null:new System.String[]{address}; + return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note, addresses, null, null, null, null); + } + + private static System.String[] matchMultipleValuePrefix(System.String prefix, int max, System.String rawText, bool trim) + { + // System.Collections.ArrayList values = null; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List values = null; // added by .net follower (http://dotnetfollower.com) + for (int i = 1; i <= max; i++) + { + System.String value_Renamed = matchSinglePrefixedField(prefix + i + ':', rawText, '\r', trim); + if (value_Renamed == null) + { + break; + } + if (values == null) + { + // values = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(max)); // lazy init // commented by .net follower (http://dotnetfollower.com) + values = new System.Collections.Generic.List(max); // lazy init // added by .net follower (http://dotnetfollower.com) + } + values.Add(value_Renamed); + } + if (values == null) + { + return null; + } + return toStringArray(values); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookDoCoMoResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookDoCoMoResultParser.cs new file mode 100644 index 0000000..68b5f23 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookDoCoMoResultParser.cs @@ -0,0 +1,85 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Implements the "MECARD" address book entry format. + /// + /// Supported keys: N, SOUND, TEL, EMAIL, NOTE, ADR, BDAY, URL, plus ORG + /// Unsupported keys: TEL-AV, NICKNAME + /// + /// Except for TEL, multiple values for keys are also not supported; + /// the first one found takes precedence. + /// + /// Our understanding of the MECARD format is based on this document: + /// + /// http://www.mobicode.org.tw/files/OMIA%20Mobile%20Bar%20Code%20Standard%20v3.2.1.doc + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class AddressBookDoCoMoResultParser:AbstractDoCoMoResultParser + { + + public static AddressBookParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || !rawText.StartsWith("MECARD:")) + { + return null; + } + System.String[] rawName = matchDoCoMoPrefixedField("N:", rawText, true); + if (rawName == null) + { + return null; + } + System.String name = parseName(rawName[0]); + System.String pronunciation = matchSingleDoCoMoPrefixedField("SOUND:", rawText, true); + System.String[] phoneNumbers = matchDoCoMoPrefixedField("TEL:", rawText, true); + System.String[] emails = matchDoCoMoPrefixedField("EMAIL:", rawText, true); + System.String note = matchSingleDoCoMoPrefixedField("NOTE:", rawText, false); + System.String[] addresses = matchDoCoMoPrefixedField("ADR:", rawText, true); + System.String birthday = matchSingleDoCoMoPrefixedField("BDAY:", rawText, true); + if (birthday != null && !isStringOfDigits(birthday, 8)) + { + // No reason to throw out the whole card because the birthday is formatted wrong. + birthday = null; + } + System.String url = matchSingleDoCoMoPrefixedField("URL:", rawText, true); + + // Although ORG may not be strictly legal in MECARD, it does exist in VCARD and we might as well + // honor it when found in the wild. + System.String org = matchSingleDoCoMoPrefixedField("ORG:", rawText, true); + + return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note, addresses, org, birthday, null, url); + } + + private static System.String parseName(System.String name) + { + int comma = name.IndexOf(','); + if (comma >= 0) + { + // Format may be last,first; switch it around + return name.Substring(comma + 1) + ' ' + name.Substring(0, (comma) - (0)); + } + return name; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookParsedResult.cs new file mode 100644 index 0000000..d262327 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/AddressBookParsedResult.cs @@ -0,0 +1,169 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class AddressBookParsedResult:ParsedResult + { + public System.String[] Names + { + get + { + return names; + } + + } + /// In Japanese, the name is written in kanji, which can have multiple readings. Therefore a hint + /// is often provided, called furigana, which spells the name phonetically. + /// + /// + /// The pronunciation of the getNames() field, often in hiragana or katakana. + /// + public System.String Pronunciation + { + get + { + return pronunciation; + } + + } + public System.String[] PhoneNumbers + { + get + { + return phoneNumbers; + } + + } + public System.String[] Emails + { + get + { + return emails; + } + + } + public System.String Note + { + get + { + return note; + } + + } + public System.String[] Addresses + { + get + { + return addresses; + } + + } + public System.String Title + { + get + { + return title; + } + + } + public System.String Org + { + get + { + return org; + } + + } + public System.String URL + { + get + { + return url; + } + + } + /// birthday formatted as yyyyMMdd (e.g. 19780917) + /// + public System.String Birthday + { + get + { + return birthday; + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(100); + maybeAppend(names, result); + maybeAppend(pronunciation, result); + maybeAppend(title, result); + maybeAppend(org, result); + maybeAppend(addresses, result); + maybeAppend(phoneNumbers, result); + maybeAppend(emails, result); + maybeAppend(url, result); + maybeAppend(birthday, result); + maybeAppend(note, result); + return result.ToString(); + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'names '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String[] names; + //UPGRADE_NOTE: Final was removed from the declaration of 'pronunciation '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String pronunciation; + //UPGRADE_NOTE: Final was removed from the declaration of 'phoneNumbers '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String[] phoneNumbers; + //UPGRADE_NOTE: Final was removed from the declaration of 'emails '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String[] emails; + //UPGRADE_NOTE: Final was removed from the declaration of 'note '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String note; + //UPGRADE_NOTE: Final was removed from the declaration of 'addresses '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String[] addresses; + //UPGRADE_NOTE: Final was removed from the declaration of 'org '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String org; + //UPGRADE_NOTE: Final was removed from the declaration of 'birthday '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String birthday; + //UPGRADE_NOTE: Final was removed from the declaration of 'title '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String title; + //UPGRADE_NOTE: Final was removed from the declaration of 'url '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String url; + + public AddressBookParsedResult(System.String[] names, System.String pronunciation, System.String[] phoneNumbers, System.String[] emails, System.String note, System.String[] addresses, System.String org, System.String birthday, System.String title, System.String url):base(ParsedResultType.ADDRESSBOOK) + { + this.names = names; + this.pronunciation = pronunciation; + this.phoneNumbers = phoneNumbers; + this.emails = emails; + this.note = note; + this.addresses = addresses; + this.org = org; + this.birthday = birthday; + this.title = title; + this.url = url; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/BizcardResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/BizcardResultParser.cs new file mode 100644 index 0000000..4543f01 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/BizcardResultParser.cs @@ -0,0 +1,99 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Implements the "BIZCARD" address book entry format, though this has been + /// largely reverse-engineered from examples observed in the wild -- still + /// looking for a definitive reference. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class BizcardResultParser:AbstractDoCoMoResultParser + { + + // Yes, we extend AbstractDoCoMoResultParser since the format is very much + // like the DoCoMo MECARD format, but this is not technically one of + // DoCoMo's proposed formats + + public static AddressBookParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || !rawText.StartsWith("BIZCARD:")) + { + return null; + } + System.String firstName = matchSingleDoCoMoPrefixedField("N:", rawText, true); + System.String lastName = matchSingleDoCoMoPrefixedField("X:", rawText, true); + System.String fullName = buildName(firstName, lastName); + System.String title = matchSingleDoCoMoPrefixedField("T:", rawText, true); + System.String org = matchSingleDoCoMoPrefixedField("C:", rawText, true); + System.String[] addresses = matchDoCoMoPrefixedField("A:", rawText, true); + System.String phoneNumber1 = matchSingleDoCoMoPrefixedField("B:", rawText, true); + System.String phoneNumber2 = matchSingleDoCoMoPrefixedField("M:", rawText, true); + System.String phoneNumber3 = matchSingleDoCoMoPrefixedField("F:", rawText, true); + System.String email = matchSingleDoCoMoPrefixedField("E:", rawText, true); + + return new AddressBookParsedResult(maybeWrap(fullName), null, buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3), maybeWrap(email), null, addresses, org, null, title, null); + } + + private static System.String[] buildPhoneNumbers(System.String number1, System.String number2, System.String number3) + { + // System.Collections.ArrayList numbers = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(3)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List numbers = new System.Collections.Generic.List(3); // added by .net follower (http://dotnetfollower.com) + if (number1 != null) + { + numbers.Add(number1); + } + if (number2 != null) + { + numbers.Add(number2); + } + if (number3 != null) + { + numbers.Add(number3); + } + int size = numbers.Count; + if (size == 0) + { + return null; + } + System.String[] result = new System.String[size]; + for (int i = 0; i < size; i++) + { + result[i] = ((System.String) numbers[i]); + } + return result; + } + + private static System.String buildName(System.String firstName, System.String lastName) + { + if (firstName == null) + { + return lastName; + } + else + { + return lastName == null?firstName:firstName + ' ' + lastName; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/BookmarkDoCoMoResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/BookmarkDoCoMoResultParser.cs new file mode 100644 index 0000000..da70093 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/BookmarkDoCoMoResultParser.cs @@ -0,0 +1,53 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class BookmarkDoCoMoResultParser:AbstractDoCoMoResultParser + { + + private BookmarkDoCoMoResultParser() + { + } + + public static URIParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || !rawText.StartsWith("MEBKM:")) + { + return null; + } + System.String title = matchSingleDoCoMoPrefixedField("TITLE:", rawText, true); + System.String[] rawUri = matchDoCoMoPrefixedField("URL:", rawText, true); + if (rawUri == null) + { + return null; + } + System.String uri = rawUri[0]; + if (!URIResultParser.isBasicallyValidURI(uri)) + { + return null; + } + return new URIParsedResult(uri, title); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/CalendarParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/CalendarParsedResult.cs new file mode 100644 index 0000000..e0786cd --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/CalendarParsedResult.cs @@ -0,0 +1,172 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class CalendarParsedResult:ParsedResult + { + public System.String Summary + { + get + { + return summary; + } + + } + ///

We would return the start and end date as a {@link java.util.Date} except that this code + /// needs to work under JavaME / MIDP and there is no date parsing library available there, such + /// as java.text.SimpleDateFormat.

See validateDate() for the return format. + /// + ///
+ /// start time formatted as a RFC 2445 DATE or DATE-TIME.

+ ///
+ public System.String Start + { + get + { + return start; + } + + } + /// + /// + public System.String End + { + get + { + return end; + } + + } + public System.String Location + { + get + { + return location; + } + + } + public System.String Attendee + { + get + { + return attendee; + } + + } + public System.String Title + { + get + { + return title; + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(100); + maybeAppend(summary, result); + maybeAppend(start, result); + maybeAppend(end, result); + maybeAppend(location, result); + maybeAppend(attendee, result); + maybeAppend(title, result); + return result.ToString(); + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'summary '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String summary; + //UPGRADE_NOTE: Final was removed from the declaration of 'start '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String start; + //UPGRADE_NOTE: Final was removed from the declaration of 'end '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String end; + //UPGRADE_NOTE: Final was removed from the declaration of 'location '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String location; + //UPGRADE_NOTE: Final was removed from the declaration of 'attendee '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String attendee; + //UPGRADE_NOTE: Final was removed from the declaration of 'title '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String title; + + public CalendarParsedResult(System.String summary, System.String start, System.String end, System.String location, System.String attendee, System.String title):base(ParsedResultType.CALENDAR) + { + // Start is required, end is not + if (start == null) + { + throw new System.ArgumentException(); + } + validateDate(start); + validateDate(end); + this.summary = summary; + this.start = start; + this.end = end; + this.location = location; + this.attendee = attendee; + this.title = title; + } + + /// RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) or DATE-TIME + /// (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC). + /// + /// + /// The string to validate + /// + private static void validateDate(System.String date) + { + if (date != null) + { + int length = date.Length; + if (length != 8 && length != 15 && length != 16) + { + throw new System.ArgumentException(); + } + for (int i = 0; i < 8; i++) + { + if (!System.Char.IsDigit(date[i])) + { + throw new System.ArgumentException(); + } + } + if (length > 8) + { + if (date[8] != 'T') + { + throw new System.ArgumentException(); + } + for (int i = 9; i < 15; i++) + { + if (!System.Char.IsDigit(date[i])) + { + throw new System.ArgumentException(); + } + } + if (length == 16 && date[15] != 'Z') + { + throw new System.ArgumentException(); + } + } + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailAddressParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailAddressParsedResult.cs new file mode 100644 index 0000000..3322012 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailAddressParsedResult.cs @@ -0,0 +1,88 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class EmailAddressParsedResult:ParsedResult + { + public System.String EmailAddress + { + get + { + return emailAddress; + } + + } + public System.String Subject + { + get + { + return subject; + } + + } + public System.String Body + { + get + { + return body; + } + + } + public System.String MailtoURI + { + get + { + return mailtoURI; + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(30); + maybeAppend(emailAddress, result); + maybeAppend(subject, result); + maybeAppend(body, result); + return result.ToString(); + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'emailAddress '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String emailAddress; + //UPGRADE_NOTE: Final was removed from the declaration of 'subject '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String subject; + //UPGRADE_NOTE: Final was removed from the declaration of 'body '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String body; + //UPGRADE_NOTE: Final was removed from the declaration of 'mailtoURI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String mailtoURI; + + internal EmailAddressParsedResult(System.String emailAddress, System.String subject, System.String body, System.String mailtoURI):base(ParsedResultType.EMAIL_ADDRESS) + { + this.emailAddress = emailAddress; + this.subject = subject; + this.body = body; + this.mailtoURI = mailtoURI; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailAddressResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailAddressResultParser.cs new file mode 100644 index 0000000..b4d866e --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailAddressResultParser.cs @@ -0,0 +1,75 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Represents a result that encodes an e-mail address, either as a plain address + /// like "joe@example.org" or a mailto: URL like "mailto:joe@example.org". + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class EmailAddressResultParser:ResultParser + { + + public static EmailAddressParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null) + { + return null; + } + System.String emailAddress; + if (rawText.StartsWith("mailto:") || rawText.StartsWith("MAILTO:")) + { + // If it starts with mailto:, assume it is definitely trying to be an email address + emailAddress = rawText.Substring(7); + int queryStart = emailAddress.IndexOf('?'); + if (queryStart >= 0) + { + emailAddress = emailAddress.Substring(0, (queryStart) - (0)); + } + // System.Collections.Hashtable nameValues = parseNameValuePairs(rawText); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.Dictionary nameValues = parseNameValuePairs(rawText); // added by .net follower (http://dotnetfollower.com) + System.String subject = null; + System.String body = null; + if (nameValues != null) + { + if (emailAddress.Length == 0) + { + emailAddress = ((System.String) nameValues["to"]); + } + subject = ((System.String) nameValues["subject"]); + body = ((System.String) nameValues["body"]); + } + return new EmailAddressParsedResult(emailAddress, subject, body, rawText); + } + else + { + if (!EmailDoCoMoResultParser.isBasicallyValidEmailAddress(rawText)) + { + return null; + } + emailAddress = rawText; + return new EmailAddressParsedResult(emailAddress, null, null, "mailto:" + emailAddress); + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailDoCoMoResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailDoCoMoResultParser.cs new file mode 100644 index 0000000..adb065f --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/EmailDoCoMoResultParser.cs @@ -0,0 +1,101 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Implements the "MATMSG" email message entry format. + /// + /// Supported keys: TO, SUB, BODY + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class EmailDoCoMoResultParser:AbstractDoCoMoResultParser + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'ATEXT_SYMBOLS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] ATEXT_SYMBOLS = new char[]{'@', '.', '!', '#', '$', '%', '&', '\'', '*', '+', '-', '/', '=', '?', '^', '_', '`', '{', '|', '}', '~'}; + + public static EmailAddressParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || !rawText.StartsWith("MATMSG:")) + { + return null; + } + System.String[] rawTo = matchDoCoMoPrefixedField("TO:", rawText, true); + if (rawTo == null) + { + return null; + } + System.String to = rawTo[0]; + if (!isBasicallyValidEmailAddress(to)) + { + return null; + } + System.String subject = matchSingleDoCoMoPrefixedField("SUB:", rawText, false); + System.String body = matchSingleDoCoMoPrefixedField("BODY:", rawText, false); + return new EmailAddressParsedResult(to, subject, body, "mailto:" + to); + } + + /// This implements only the most basic checking for an email address's validity -- that it contains + /// an '@' contains no characters disallowed by RFC 2822. This is an overly lenient definition of + /// validity. We want to generally be lenient here since this class is only intended to encapsulate what's + /// in a barcode, not "judge" it. + /// + internal static bool isBasicallyValidEmailAddress(System.String email) + { + if (email == null) + { + return false; + } + bool atFound = false; + for (int i = 0; i < email.Length; i++) + { + char c = email[i]; + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && !isAtextSymbol(c)) + { + return false; + } + if (c == '@') + { + if (atFound) + { + return false; + } + atFound = true; + } + } + return atFound; + } + + private static bool isAtextSymbol(char c) + { + for (int i = 0; i < ATEXT_SYMBOLS.Length; i++) + { + if (c == ATEXT_SYMBOLS[i]) + { + return true; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/GeoParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/GeoParsedResult.cs new file mode 100644 index 0000000..da4f217 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/GeoParsedResult.cs @@ -0,0 +1,131 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class GeoParsedResult:ParsedResult + { + public System.String GeoURI + { + get + { + return geoURI; + } + + } + /// latitude in degrees + /// + public double Latitude + { + get + { + return latitude; + } + + } + /// longitude in degrees + /// + public double Longitude + { + get + { + return longitude; + } + + } + /// altitude in meters. If not specified, in the geo URI, returns 0.0 + /// + public double Altitude + { + get + { + return altitude; + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(50); + result.Append(latitude); + result.Append(", "); + result.Append(longitude); + if (altitude > 0.0f) + { + result.Append(", "); + result.Append(altitude); + result.Append('m'); + } + return result.ToString(); + } + + /// a URI link to Google Maps which display the point on the Earth described + /// by this instance, and sets the zoom level in a way that roughly reflects the + /// altitude, if specified + /// + /* + public String getGoogleMapsURI() { + StringBuffer result = new StringBuffer(50); + result.append("http://maps.google.com/?ll="); + result.append(latitude); + result.append(','); + result.append(longitude); + if (altitude > 0.0f) { + // Map altitude to zoom level, cleverly. Roughly, zoom level 19 is like a + // view from 1000ft, 18 is like 2000ft, 17 like 4000ft, and so on. + double altitudeInFeet = altitude * 3.28; + int altitudeInKFeet = (int) (altitudeInFeet / 1000.0); + // No Math.log() available here, so compute log base 2 the old fashioned way + // Here logBaseTwo will take on a value between 0 and 18 actually + int logBaseTwo = 0; + while (altitudeInKFeet > 1 && logBaseTwo < 18) { + altitudeInKFeet >>= 1; + logBaseTwo++; + } + int zoom = 19 - logBaseTwo; + result.append("&z="); + result.append(zoom); + } + return result.toString(); + } + */ + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'geoURI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String geoURI; + //UPGRADE_NOTE: Final was removed from the declaration of 'latitude '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private double latitude; + //UPGRADE_NOTE: Final was removed from the declaration of 'longitude '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private double longitude; + //UPGRADE_NOTE: Final was removed from the declaration of 'altitude '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private double altitude; + + internal GeoParsedResult(System.String geoURI, double latitude, double longitude, double altitude):base(ParsedResultType.GEO) + { + this.geoURI = geoURI; + this.latitude = latitude; + this.longitude = longitude; + this.altitude = altitude; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/GeoResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/GeoResultParser.cs new file mode 100644 index 0000000..ebfbc5b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/GeoResultParser.cs @@ -0,0 +1,78 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Parses a "geo:" URI result, which specifies a location on the surface of + /// the Earth as well as an optional altitude above the surface. See + /// + /// http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class GeoResultParser:ResultParser + { + + private GeoResultParser() + { + } + + public static GeoParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || (!rawText.StartsWith("geo:") && !rawText.StartsWith("GEO:"))) + { + return null; + } + // Drop geo, query portion + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int queryStart = rawText.IndexOf('?', 4); + System.String geoURIWithoutQuery = queryStart < 0?rawText.Substring(4):rawText.Substring(4, (queryStart) - (4)); + int latitudeEnd = geoURIWithoutQuery.IndexOf(','); + if (latitudeEnd < 0) + { + return null; + } + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int longitudeEnd = geoURIWithoutQuery.IndexOf(',', latitudeEnd + 1); + double latitude, longitude, altitude; + try + { + latitude = System.Double.Parse(geoURIWithoutQuery.Substring(0, (latitudeEnd) - (0))); + if (longitudeEnd < 0) + { + longitude = System.Double.Parse(geoURIWithoutQuery.Substring(latitudeEnd + 1)); + altitude = 0.0; + } + else + { + longitude = System.Double.Parse(geoURIWithoutQuery.Substring(latitudeEnd + 1, (longitudeEnd) - (latitudeEnd + 1))); + altitude = System.Double.Parse(geoURIWithoutQuery.Substring(longitudeEnd + 1)); + } + } + catch (System.FormatException nfe) + { + return null; + } + return new GeoParsedResult(rawText.StartsWith("GEO:")?"geo:" + rawText.Substring(4):rawText, latitude, longitude, altitude); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ISBNParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ISBNParsedResult.cs new file mode 100644 index 0000000..75b5f8b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ISBNParsedResult.cs @@ -0,0 +1,51 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// jbreiden@google.com (Jeff Breidenbach) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ISBNParsedResult:ParsedResult + { + public System.String ISBN + { + get + { + return isbn; + } + + } + override public System.String DisplayResult + { + get + { + return isbn; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'isbn '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String isbn; + + internal ISBNParsedResult(System.String isbn):base(ParsedResultType.ISBN) + { + this.isbn = isbn; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ISBNResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ISBNResultParser.cs new file mode 100644 index 0000000..54f26d6 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ISBNResultParser.cs @@ -0,0 +1,63 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Parses strings of digits that represent a ISBN. + /// + /// + /// jbreiden@google.com (Jeff Breidenbach) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public class ISBNResultParser:ResultParser + { + + private ISBNResultParser() + { + } + + // ISBN-13 For Dummies + // http://www.bisg.org/isbn-13/for.dummies.html + public static ISBNParsedResult parse(Result result) + { + BarcodeFormat format = result.BarcodeFormat; + if (!BarcodeFormat.EAN_13.Equals(format)) + { + return null; + } + System.String rawText = result.Text; + if (rawText == null) + { + return null; + } + int length = rawText.Length; + if (length != 13) + { + return null; + } + if (!rawText.StartsWith("978") && !rawText.StartsWith("979")) + { + return null; + } + + return new ISBNParsedResult(rawText); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ParsedResult.cs new file mode 100644 index 0000000..5c522d6 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ParsedResult.cs @@ -0,0 +1,90 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + ///

Abstract class representing the result of decoding a barcode, as more than + /// a String -- as some type of structured data. This might be a subclass which represents + /// a URL, or an e-mail address. {@link ResultParser#parseResult(Result)} will turn a raw + /// decoded string into the most appropriate type of structured representation.

+ /// + ///

Thanks to Jeff Griffin for proposing rewrite of these classes that relies less + /// on exception-based mechanisms during parsing.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class ParsedResult + { + virtual public ParsedResultType Type + { + get + { + return type; + } + + } + public abstract System.String DisplayResult{get;} + + //UPGRADE_NOTE: Final was removed from the declaration of 'type '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ParsedResultType type; + + protected internal ParsedResult(ParsedResultType type) + { + this.type = type; + } + + public override System.String ToString() + { + return DisplayResult; + } + + public static void maybeAppend(System.String value_Renamed, System.Text.StringBuilder result) + { + if (value_Renamed != null && value_Renamed.Length > 0) + { + // Don't add a newline before the first value + if (result.Length > 0) + { + result.Append('\n'); + } + result.Append(value_Renamed); + } + } + + public static void maybeAppend(System.String[] value_Renamed, System.Text.StringBuilder result) + { + if (value_Renamed != null) + { + for (int i = 0; i < value_Renamed.Length; i++) + { + if (value_Renamed[i] != null && value_Renamed[i].Length > 0) + { + if (result.Length > 0) + { + result.Append('\n'); + } + result.Append(value_Renamed[i]); + } + } + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ParsedResultType.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ParsedResultType.cs new file mode 100644 index 0000000..fd962b5 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ParsedResultType.cs @@ -0,0 +1,72 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Represents the type of data encoded by a barcode -- from plain text, to a + /// URI, to an e-mail address, etc. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ParsedResultType + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'ADDRESSBOOK '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType ADDRESSBOOK = new ParsedResultType("ADDRESSBOOK"); + //UPGRADE_NOTE: Final was removed from the declaration of 'EMAIL_ADDRESS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType EMAIL_ADDRESS = new ParsedResultType("EMAIL_ADDRESS"); + //UPGRADE_NOTE: Final was removed from the declaration of 'PRODUCT '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType PRODUCT = new ParsedResultType("PRODUCT"); + //UPGRADE_NOTE: Final was removed from the declaration of 'URI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType URI = new ParsedResultType("URI"); + //UPGRADE_NOTE: Final was removed from the declaration of 'TEXT '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType TEXT = new ParsedResultType("TEXT"); + //UPGRADE_NOTE: Final was removed from the declaration of 'ANDROID_INTENT '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType ANDROID_INTENT = new ParsedResultType("ANDROID_INTENT"); + //UPGRADE_NOTE: Final was removed from the declaration of 'GEO '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType GEO = new ParsedResultType("GEO"); + //UPGRADE_NOTE: Final was removed from the declaration of 'TEL '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType TEL = new ParsedResultType("TEL"); + //UPGRADE_NOTE: Final was removed from the declaration of 'SMS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType SMS = new ParsedResultType("SMS"); + //UPGRADE_NOTE: Final was removed from the declaration of 'CALENDAR '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType CALENDAR = new ParsedResultType("CALENDAR"); + // "optional" types + //UPGRADE_NOTE: Final was removed from the declaration of 'NDEF_SMART_POSTER '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType NDEF_SMART_POSTER = new ParsedResultType("NDEF_SMART_POSTER"); + //UPGRADE_NOTE: Final was removed from the declaration of 'MOBILETAG_RICH_WEB '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType MOBILETAG_RICH_WEB = new ParsedResultType("MOBILETAG_RICH_WEB"); + //UPGRADE_NOTE: Final was removed from the declaration of 'ISBN '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ParsedResultType ISBN = new ParsedResultType("ISBN"); + + //UPGRADE_NOTE: Final was removed from the declaration of 'name '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String name; + + private ParsedResultType(System.String name) + { + this.name = name; + } + + public override System.String ToString() + { + return name; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ProductParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ProductParsedResult.cs new file mode 100644 index 0000000..3781f93 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ProductParsedResult.cs @@ -0,0 +1,66 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ProductParsedResult:ParsedResult + { + public System.String ProductID + { + get + { + return productID; + } + + } + public System.String NormalizedProductID + { + get + { + return normalizedProductID; + } + + } + override public System.String DisplayResult + { + get + { + return productID; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'productID '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String productID; + //UPGRADE_NOTE: Final was removed from the declaration of 'normalizedProductID '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String normalizedProductID; + + internal ProductParsedResult(System.String productID):this(productID, productID) + { + } + + internal ProductParsedResult(System.String productID, System.String normalizedProductID):base(ParsedResultType.PRODUCT) + { + this.productID = productID; + this.normalizedProductID = normalizedProductID; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ProductResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ProductResultParser.cs new file mode 100644 index 0000000..8a80928 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ProductResultParser.cs @@ -0,0 +1,77 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using Result = com.google.zxing.Result; +using UPCEReader = com.google.zxing.oned.UPCEReader; +namespace com.google.zxing.client.result +{ + + /// Parses strings of digits that represent a UPC code. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class ProductResultParser:ResultParser + { + + private ProductResultParser() + { + } + + // Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes. + public static ProductParsedResult parse(Result result) + { + BarcodeFormat format = result.BarcodeFormat; + if (!(BarcodeFormat.UPC_A.Equals(format) || BarcodeFormat.UPC_E.Equals(format) || BarcodeFormat.EAN_8.Equals(format) || BarcodeFormat.EAN_13.Equals(format))) + { + return null; + } + // Really neither of these should happen: + System.String rawText = result.Text; + if (rawText == null) + { + return null; + } + + int length = rawText.Length; + for (int x = 0; x < length; x++) + { + char c = rawText[x]; + if (c < '0' || c > '9') + { + return null; + } + } + // Not actually checking the checksum again here + + System.String normalizedProductID; + // Expand UPC-E for purposes of searching + if (BarcodeFormat.UPC_E.Equals(format)) + { + normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText); + } + else + { + normalizedProductID = rawText; + } + + return new ProductParsedResult(rawText, normalizedProductID); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ResultParser.cs new file mode 100644 index 0000000..3f695bc --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/ResultParser.cs @@ -0,0 +1,421 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + ///

Abstract class representing the result of decoding a barcode, as more than + /// a String -- as some type of structured data. This might be a subclass which represents + /// a URL, or an e-mail address. {@link #parseResult(com.google.zxing.Result)} will turn a raw + /// decoded string into the most appropriate type of structured representation.

+ /// + ///

Thanks to Jeff Griffin for proposing rewrite of these classes that relies less + /// on exception-based mechanisms during parsing.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class ResultParser + { + + public static ParsedResult parseResult(Result theResult) + { + // This is a bit messy, but given limited options in MIDP / CLDC, this may well be the simplest + // way to go about this. For example, we have no reflection available, really. + // Order is important here. + ParsedResult result; + if ((result = BookmarkDoCoMoResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = AddressBookDoCoMoResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = EmailDoCoMoResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = AddressBookAUResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = VCardResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = BizcardResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = VEventResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = EmailAddressResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = TelResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = SMSMMSResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = GeoResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = URLTOResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = URIResultParser.parse(theResult)) != null) + { + return result; + } + else if ((result = ISBNResultParser.parse(theResult)) != null) + { + // We depend on ISBN parsing coming before UPC, as it is a subset. + return result; + } + else if ((result = ProductResultParser.parse(theResult)) != null) + { + return result; + } + return new TextParsedResult(theResult.Text, null); + } + + protected internal static void maybeAppend(System.String value_Renamed, System.Text.StringBuilder result) + { + if (value_Renamed != null) + { + result.Append('\n'); + result.Append(value_Renamed); + } + } + + protected internal static void maybeAppend(System.String[] value_Renamed, System.Text.StringBuilder result) + { + if (value_Renamed != null) + { + for (int i = 0; i < value_Renamed.Length; i++) + { + result.Append('\n'); + result.Append(value_Renamed[i]); + } + } + } + + protected internal static System.String[] maybeWrap(System.String value_Renamed) + { + return value_Renamed == null?null:new System.String[]{value_Renamed}; + } + + protected internal static System.String unescapeBackslash(System.String escaped) + { + if (escaped != null) + { + int backslash = escaped.IndexOf('\\'); + if (backslash >= 0) + { + int max = escaped.Length; + System.Text.StringBuilder unescaped = new System.Text.StringBuilder(max - 1); + unescaped.Append(escaped.ToCharArray(), 0, backslash); + bool nextIsEscaped = false; + for (int i = backslash; i < max; i++) + { + char c = escaped[i]; + if (nextIsEscaped || c != '\\') + { + unescaped.Append(c); + nextIsEscaped = false; + } + else + { + nextIsEscaped = true; + } + } + return unescaped.ToString(); + } + } + return escaped; + } + + private static System.String urlDecode(System.String escaped) + { + + // No we can't use java.net.URLDecoder here. JavaME doesn't have it. + if (escaped == null) + { + return null; + } + char[] escapedArray = escaped.ToCharArray(); + + int first = findFirstEscape(escapedArray); + if (first < 0) + { + return escaped; + } + + int max = escapedArray.Length; + // final length is at most 2 less than original due to at least 1 unescaping + System.Text.StringBuilder unescaped = new System.Text.StringBuilder(max - 2); + // Can append everything up to first escape character + unescaped.Append(escapedArray, 0, first); + + for (int i = first; i < max; i++) + { + char c = escapedArray[i]; + if (c == '+') + { + // + is translated directly into a space + unescaped.Append(' '); + } + else if (c == '%') + { + // Are there even two more chars? if not we will just copy the escaped sequence and be done + if (i >= max - 2) + { + unescaped.Append('%'); // append that % and move on + } + else + { + int firstDigitValue = parseHexDigit(escapedArray[++i]); + int secondDigitValue = parseHexDigit(escapedArray[++i]); + if (firstDigitValue < 0 || secondDigitValue < 0) + { + // bad digit, just move on + unescaped.Append('%'); + unescaped.Append(escapedArray[i - 1]); + unescaped.Append(escapedArray[i]); + } + unescaped.Append((char) ((firstDigitValue << 4) + secondDigitValue)); + } + } + else + { + unescaped.Append(c); + } + } + return unescaped.ToString(); + } + + private static int findFirstEscape(char[] escapedArray) + { + int max = escapedArray.Length; + for (int i = 0; i < max; i++) + { + char c = escapedArray[i]; + if (c == '+' || c == '%') + { + return i; + } + } + return - 1; + } + + private static int parseHexDigit(char c) + { + if (c >= 'a') + { + if (c <= 'f') + { + return 10 + (c - 'a'); + } + } + else if (c >= 'A') + { + if (c <= 'F') + { + return 10 + (c - 'A'); + } + } + else if (c >= '0') + { + if (c <= '9') + { + return c - '0'; + } + } + return - 1; + } + + protected internal static bool isStringOfDigits(System.String value_Renamed, int length) + { + if (value_Renamed == null) + { + return false; + } + int stringLength = value_Renamed.Length; + if (length != stringLength) + { + return false; + } + for (int i = 0; i < length; i++) + { + char c = value_Renamed[i]; + if (c < '0' || c > '9') + { + return false; + } + } + return true; + } + + protected internal static bool isSubstringOfDigits(System.String value_Renamed, int offset, int length) + { + if (value_Renamed == null) + { + return false; + } + int stringLength = value_Renamed.Length; + int max = offset + length; + if (stringLength < max) + { + return false; + } + for (int i = offset; i < max; i++) + { + char c = value_Renamed[i]; + if (c < '0' || c > '9') + { + return false; + } + } + return true; + } + + //internal static System.Collections.Hashtable parseNameValuePairs(System.String uri) // commented by .net follower (http://dotnetfollower.com) + internal static System.Collections.Generic.Dictionary parseNameValuePairs(System.String uri) // added by .net follower (http://dotnetfollower.com) + { + int paramStart = uri.IndexOf('?'); + if (paramStart < 0) + { + return null; + } + //System.Collections.Hashtable result = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable(3)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.Dictionary result = new System.Collections.Generic.Dictionary(3); // added by .net follower (http://dotnetfollower.com) + paramStart++; + int paramEnd; + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + while ((paramEnd = uri.IndexOf('&', paramStart)) >= 0) + { + appendKeyValue(uri, paramStart, paramEnd, result); + paramStart = paramEnd + 1; + } + appendKeyValue(uri, paramStart, uri.Length, result); + return result; + } + + //private static void appendKeyValue(System.String uri, int paramStart, int paramEnd, System.Collections.Hashtable result) // commented by .net follower (http://dotnetfollower.com) + private static void appendKeyValue(System.String uri, int paramStart, int paramEnd, System.Collections.Generic.Dictionary result) // added by .net follower (http://dotnetfollower.com) + { + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int separator = uri.IndexOf('=', paramStart); + if (separator >= 0) + { + // key = value + System.String key = uri.Substring(paramStart, (separator) - (paramStart)); + System.String value_Renamed = uri.Substring(separator + 1, (paramEnd) - (separator + 1)); + value_Renamed = urlDecode(value_Renamed); + result[key] = value_Renamed; + } + // Can't put key, null into a hashtable + } + + internal static System.String[] matchPrefixedField(System.String prefix, System.String rawText, char endChar, bool trim) + { + // System.Collections.ArrayList matches = null; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List matches = null; // added by .net follower (http://dotnetfollower.com) + int i = 0; + int max = rawText.Length; + while (i < max) + { + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + i = rawText.IndexOf(prefix, i); + if (i < 0) + { + break; + } + i += prefix.Length; // Skip past this prefix we found to start + int start = i; // Found the start of a match here + bool done = false; + while (!done) + { + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + i = rawText.IndexOf((System.Char) endChar, i); + if (i < 0) + { + // No terminating end character? uh, done. Set i such that loop terminates and break + i = rawText.Length; + done = true; + } + else if (rawText[i - 1] == '\\') + { + // semicolon was escaped so continue + i++; + } + else + { + // found a match + if (matches == null) + { + // matches = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(3)); // lazy init // commented by .net follower (http://dotnetfollower.com) + matches = new System.Collections.Generic.List(3); // lazy init // added by .net follower (http://dotnetfollower.com) + } + System.String element = unescapeBackslash(rawText.Substring(start, (i) - (start))); + if (trim) + { + element = element.Trim(); + } + matches.Add(element); + i++; + done = true; + } + } + } + if (matches == null || (matches.Count == 0)) + { + return null; + } + return toStringArray(matches); + } + + internal static System.String matchSinglePrefixedField(System.String prefix, System.String rawText, char endChar, bool trim) + { + System.String[] matches = matchPrefixedField(prefix, rawText, endChar, trim); + return matches == null?null:matches[0]; + } + + // internal static System.String[] toStringArray(System.Collections.ArrayList strings) // commented by .net follower (http://dotnetfollower.com) + internal static System.String[] toStringArray(System.Collections.Generic.List strings) // added by .net follower (http://dotnetfollower.com) + { + int size = strings.Count; + System.String[] result = new System.String[size]; + for (int j = 0; j < size; j++) + { + result[j] = ((System.String) strings[j]); + } + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/SMSMMSResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/SMSMMSResultParser.cs new file mode 100644 index 0000000..cb85307 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/SMSMMSResultParser.cs @@ -0,0 +1,124 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + ///

Parses an "sms:" URI result, which specifies a number to SMS and optional + /// "via" number. See + /// the IETF draft on this.

+ /// + ///

This actually also parses URIs starting with "mms:", "smsto:", "mmsto:", "SMSTO:", and + /// "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI + /// for purposes of forwarding to the platform.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class SMSMMSResultParser:ResultParser + { + + private SMSMMSResultParser() + { + } + + public static SMSParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null) + { + return null; + } + int prefixLength; + if (rawText.StartsWith("sms:") || rawText.StartsWith("SMS:") || rawText.StartsWith("mms:") || rawText.StartsWith("MMS:")) + { + prefixLength = 4; + } + else if (rawText.StartsWith("smsto:") || rawText.StartsWith("SMSTO:") || rawText.StartsWith("mmsto:") || rawText.StartsWith("MMSTO:")) + { + prefixLength = 6; + } + else + { + return null; + } + + // Check up front if this is a URI syntax string with query arguments + // System.Collections.Hashtable nameValuePairs = parseNameValuePairs(rawText); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.Dictionary nameValuePairs = parseNameValuePairs(rawText); // added by .net follower (http://dotnetfollower.com) + System.String subject = null; + System.String body = null; + bool querySyntax = false; + if (nameValuePairs != null && !(nameValuePairs.Count == 0)) + { + subject = ((System.String) nameValuePairs["subject"]); + body = ((System.String) nameValuePairs["body"]); + querySyntax = true; + } + + // Drop sms, query portion + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int queryStart = rawText.IndexOf('?', prefixLength); + System.String smsURIWithoutQuery; + // If it's not query syntax, the question mark is part of the subject or message + if (queryStart < 0 || !querySyntax) + { + smsURIWithoutQuery = rawText.Substring(prefixLength); + } + else + { + smsURIWithoutQuery = rawText.Substring(prefixLength, (queryStart) - (prefixLength)); + } + int numberEnd = smsURIWithoutQuery.IndexOf(';'); + System.String number; + System.String via; + if (numberEnd < 0) + { + number = smsURIWithoutQuery; + via = null; + } + else + { + number = smsURIWithoutQuery.Substring(0, (numberEnd) - (0)); + System.String maybeVia = smsURIWithoutQuery.Substring(numberEnd + 1); + if (maybeVia.StartsWith("via=")) + { + via = maybeVia.Substring(4); + } + else + { + via = null; + } + } + + // Thanks to dominik.wild for suggesting this enhancement to support + // smsto:number:body URIs + if (body == null) + { + int bodyStart = number.IndexOf(':'); + if (bodyStart >= 0) + { + body = number.Substring(bodyStart + 1); + number = number.Substring(0, (bodyStart) - (0)); + } + } + return new SMSParsedResult("sms:" + number, number, via, subject, body, null); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/SMSParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/SMSParsedResult.cs new file mode 100644 index 0000000..c42eb28 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/SMSParsedResult.cs @@ -0,0 +1,112 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class SMSParsedResult:ParsedResult + { + public System.String SMSURI + { + get + { + return smsURI; + } + + } + public System.String Number + { + get + { + return number; + } + + } + public System.String Via + { + get + { + return via; + } + + } + public System.String Subject + { + get + { + return subject; + } + + } + public System.String Body + { + get + { + return body; + } + + } + public System.String Title + { + get + { + return title; + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(100); + maybeAppend(number, result); + maybeAppend(via, result); + maybeAppend(subject, result); + maybeAppend(body, result); + maybeAppend(title, result); + return result.ToString(); + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'smsURI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String smsURI; + //UPGRADE_NOTE: Final was removed from the declaration of 'number '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String number; + //UPGRADE_NOTE: Final was removed from the declaration of 'via '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String via; + //UPGRADE_NOTE: Final was removed from the declaration of 'subject '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String subject; + //UPGRADE_NOTE: Final was removed from the declaration of 'body '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String body; + //UPGRADE_NOTE: Final was removed from the declaration of 'title '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String title; + + public SMSParsedResult(System.String smsURI, System.String number, System.String via, System.String subject, System.String body, System.String title):base(ParsedResultType.SMS) + { + this.smsURI = smsURI; + this.number = number; + this.via = via; + this.subject = subject; + this.body = body; + this.title = title; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TelParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TelParsedResult.cs new file mode 100644 index 0000000..6c00ed4 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TelParsedResult.cs @@ -0,0 +1,76 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class TelParsedResult:ParsedResult + { + public System.String Number + { + get + { + return number; + } + + } + public System.String TelURI + { + get + { + return telURI; + } + + } + public System.String Title + { + get + { + return title; + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(20); + maybeAppend(number, result); + maybeAppend(title, result); + return result.ToString(); + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'number '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String number; + //UPGRADE_NOTE: Final was removed from the declaration of 'telURI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String telURI; + //UPGRADE_NOTE: Final was removed from the declaration of 'title '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String title; + + public TelParsedResult(System.String number, System.String telURI, System.String title):base(ParsedResultType.TEL) + { + this.number = number; + this.telURI = telURI; + this.title = title; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TelResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TelResultParser.cs new file mode 100644 index 0000000..0a472db --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TelResultParser.cs @@ -0,0 +1,51 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Parses a "tel:" URI result, which specifies a phone number. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class TelResultParser:ResultParser + { + + private TelResultParser() + { + } + + public static TelParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || (!rawText.StartsWith("tel:") && !rawText.StartsWith("TEL:"))) + { + return null; + } + // Normalize "TEL:" to "tel:" + System.String telURI = rawText.StartsWith("TEL:")?"tel:" + rawText.Substring(4):rawText; + // Drop tel, query portion + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int queryStart = rawText.IndexOf('?', 4); + System.String number = queryStart < 0?rawText.Substring(4):rawText.Substring(4, (queryStart) - (4)); + return new TelParsedResult(number, telURI, null); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TextParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TextParsedResult.cs new file mode 100644 index 0000000..806eb2c --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/TextParsedResult.cs @@ -0,0 +1,66 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// A simple result type encapsulating a string that has no further + /// interpretation. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class TextParsedResult:ParsedResult + { + public System.String Text + { + get + { + return text; + } + + } + public System.String Language + { + get + { + return language; + } + + } + override public System.String DisplayResult + { + get + { + return text; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'text '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String text; + //UPGRADE_NOTE: Final was removed from the declaration of 'language '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String language; + + public TextParsedResult(System.String text, System.String language):base(ParsedResultType.TEXT) + { + this.text = text; + this.language = language; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URIParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URIParsedResult.cs new file mode 100644 index 0000000..ea8cdff --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URIParsedResult.cs @@ -0,0 +1,148 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class URIParsedResult:ParsedResult + { + public System.String URI + { + get + { + return uri; + } + + } + public System.String Title + { + get + { + return title; + } + + } + /// true if the URI contains suspicious patterns that may suggest it intends to + /// mislead the user about its true nature. At the moment this looks for the presence + /// of user/password syntax in the host/authority portion of a URI which may be used + /// in attempts to make the URI's host appear to be other than it is. Example: + /// http://yourbank.com@phisher.com This URI connects to phisher.com but may appear + /// to connect to yourbank.com at first glance. + /// + public bool PossiblyMaliciousURI + { + get + { + return containsUser(); + } + + } + override public System.String DisplayResult + { + get + { + System.Text.StringBuilder result = new System.Text.StringBuilder(30); + maybeAppend(title, result); + maybeAppend(uri, result); + return result.ToString(); + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'uri '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String uri; + //UPGRADE_NOTE: Final was removed from the declaration of 'title '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String title; + + public URIParsedResult(System.String uri, System.String title):base(ParsedResultType.URI) + { + this.uri = massageURI(uri); + this.title = title; + } + + private bool containsUser() + { + // This method is likely not 100% RFC compliant yet + int hostStart = uri.IndexOf(':'); // we should always have scheme at this point + hostStart++; + // Skip slashes preceding host + int uriLength = uri.Length; + while (hostStart < uriLength && uri[hostStart] == '/') + { + hostStart++; + } + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int hostEnd = uri.IndexOf('/', hostStart); + if (hostEnd < 0) + { + hostEnd = uriLength; + } + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int at = uri.IndexOf('@', hostStart); + return at >= hostStart && at < hostEnd; + } + + /// Transforms a string that represents a URI into something more proper, by adding or canonicalizing + /// the protocol. + /// + private static System.String massageURI(System.String uri) + { + int protocolEnd = uri.IndexOf(':'); + if (protocolEnd < 0) + { + // No protocol, assume http + uri = "http://" + uri; + } + else if (isColonFollowedByPortNumber(uri, protocolEnd)) + { + // Found a colon, but it looks like it is after the host, so the protocol is still missing + uri = "http://" + uri; + } + else + { + // Lowercase protocol to avoid problems + uri = uri.Substring(0, (protocolEnd) - (0)).ToLower() + uri.Substring(protocolEnd); + } + return uri; + } + + private static bool isColonFollowedByPortNumber(System.String uri, int protocolEnd) + { + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int nextSlash = uri.IndexOf('/', protocolEnd + 1); + if (nextSlash < 0) + { + nextSlash = uri.Length; + } + if (nextSlash <= protocolEnd + 1) + { + return false; + } + for (int x = protocolEnd + 1; x < nextSlash; x++) + { + if (uri[x] < '0' || uri[x] > '9') + { + return false; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URIResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URIResultParser.cs new file mode 100644 index 0000000..498513c --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URIResultParser.cs @@ -0,0 +1,106 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Tries to parse results that are a URI of some kind. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class URIResultParser:ResultParser + { + + private URIResultParser() + { + } + + public static URIParsedResult parse(Result result) + { + System.String rawText = result.Text; + // We specifically handle the odd "URL" scheme here for simplicity + if (rawText != null && rawText.StartsWith("URL:")) + { + rawText = rawText.Substring(4); + } + if (!isBasicallyValidURI(rawText)) + { + return null; + } + return new URIParsedResult(rawText, null); + } + + /// Determines whether a string is not obviously not a URI. This implements crude checks; this class does not + /// intend to strictly check URIs as its only function is to represent what is in a barcode, but, it does + /// need to know when a string is obviously not a URI. + /// + internal static bool isBasicallyValidURI(System.String uri) + { + if (uri == null || uri.IndexOf(' ') >= 0 || uri.IndexOf('\n') >= 0) + { + return false; + } + // Look for period in a domain but followed by at least a two-char TLD + // Forget strings that don't have a valid-looking protocol + int period = uri.IndexOf('.'); + if (period >= uri.Length - 2) + { + return false; + } + int colon = uri.IndexOf(':'); + if (period < 0 && colon < 0) + { + return false; + } + if (colon >= 0) + { + if (period < 0 || period > colon) + { + // colon ends the protocol + for (int i = 0; i < colon; i++) + { + char c = uri[i]; + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) + { + return false; + } + } + } + else + { + // colon starts the port; crudely look for at least two numbers + if (colon >= uri.Length - 2) + { + return false; + } + for (int i = colon + 1; i < colon + 3; i++) + { + char c = uri[i]; + if (c < '0' || c > '9') + { + return false; + } + } + } + } + return true; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URLTOResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URLTOResultParser.cs new file mode 100644 index 0000000..9f3eea6 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/URLTOResultParser.cs @@ -0,0 +1,55 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Parses the "URLTO" result format, which is of the form "URLTO:[title]:[url]". + /// This seems to be used sometimes, but I am not able to find documentation + /// on its origin or official format? + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class URLTOResultParser + { + + private URLTOResultParser() + { + } + + public static URIParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null || (!rawText.StartsWith("urlto:") && !rawText.StartsWith("URLTO:"))) + { + return null; + } + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + int titleEnd = rawText.IndexOf(':', 6); + if (titleEnd < 0) + { + return null; + } + System.String title = titleEnd <= 6?null:rawText.Substring(6, (titleEnd) - (6)); + System.String uri = rawText.Substring(titleEnd + 1); + return new URIParsedResult(uri, title); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/VCardResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/VCardResultParser.cs new file mode 100644 index 0000000..d231213 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/VCardResultParser.cs @@ -0,0 +1,232 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Parses contact information formatted according to the VCard (2.1) format. This is not a complete + /// implementation but should parse information as commonly encoded in 2D barcodes. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class VCardResultParser:ResultParser + { + + private VCardResultParser() + { + } + + public static AddressBookParsedResult parse(Result result) + { + // Although we should insist on the raw text ending with "END:VCARD", there's no reason + // to throw out everything else we parsed just because this was omitted. In fact, Eclair + // is doing just that, and we can't parse its contacts without this leniency. + System.String rawText = result.Text; + if (rawText == null || !rawText.StartsWith("BEGIN:VCARD")) + { + return null; + } + System.String[] names = matchVCardPrefixedField("FN", rawText, true); + if (names == null) + { + // If no display names found, look for regular name fields and format them + names = matchVCardPrefixedField("N", rawText, true); + formatNames(names); + } + System.String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText, true); + System.String[] emails = matchVCardPrefixedField("EMAIL", rawText, true); + System.String note = matchSingleVCardPrefixedField("NOTE", rawText, false); + System.String[] addresses = matchVCardPrefixedField("ADR", rawText, true); + if (addresses != null) + { + for (int i = 0; i < addresses.Length; i++) + { + addresses[i] = formatAddress(addresses[i]); + } + } + System.String org = matchSingleVCardPrefixedField("ORG", rawText, true); + System.String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true); + if (!isLikeVCardDate(birthday)) + { + birthday = null; + } + System.String title = matchSingleVCardPrefixedField("TITLE", rawText, true); + System.String url = matchSingleVCardPrefixedField("URL", rawText, true); + return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, addresses, org, birthday, title, url); + } + + private static System.String[] matchVCardPrefixedField(System.String prefix, System.String rawText, bool trim) + { + // System.Collections.ArrayList matches = null; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List matches = null; // added by .net follower (http://dotnetfollower.com) + int i = 0; + int max = rawText.Length; + while (i < max) + { + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + i = rawText.IndexOf(prefix, i); + if (i < 0) + { + break; + } + if (i > 0 && rawText[i - 1] != '\n') + { + // then this didn't start a new token, we matched in the middle of something + i++; + continue; + } + i += prefix.Length; // Skip past this prefix we found to start + if (rawText[i] != ':' && rawText[i] != ';') + { + continue; + } + while (rawText[i] != ':') + { + // Skip until a colon + i++; + } + i++; // skip colon + int start = i; // Found the start of a match here + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + i = rawText.IndexOf('\n', i); // Really, ends in \r\n + if (i < 0) + { + // No terminating end character? uh, done. Set i such that loop terminates and break + i = max; + } + else if (i > start) + { + // found a match + if (matches == null) + { + // matches = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(3)); // lazy init // commented by .net follower (http://dotnetfollower.com) + matches = new System.Collections.Generic.List(3); // lazy init // added by .net follower (http://dotnetfollower.com) + } + System.String element = rawText.Substring(start, (i) - (start)); + if (trim) + { + element = element.Trim(); + } + matches.Add(element); + i++; + } + else + { + i++; + } + } + if (matches == null || (matches.Count == 0)) + { + return null; + } + return toStringArray(matches); + } + + internal static System.String matchSingleVCardPrefixedField(System.String prefix, System.String rawText, bool trim) + { + System.String[] values = matchVCardPrefixedField(prefix, rawText, trim); + return values == null?null:values[0]; + } + + private static bool isLikeVCardDate(System.String value_Renamed) + { + if (value_Renamed == null) + { + return true; + } + // Not really sure this is true but matches practice + // Mach YYYYMMDD + if (isStringOfDigits(value_Renamed, 8)) + { + return true; + } + // or YYYY-MM-DD + return value_Renamed.Length == 10 && value_Renamed[4] == '-' && value_Renamed[7] == '-' && isSubstringOfDigits(value_Renamed, 0, 4) && isSubstringOfDigits(value_Renamed, 5, 2) && isSubstringOfDigits(value_Renamed, 8, 2); + } + + private static System.String formatAddress(System.String address) + { + if (address == null) + { + return null; + } + int length = address.Length; + System.Text.StringBuilder newAddress = new System.Text.StringBuilder(length); + for (int j = 0; j < length; j++) + { + char c = address[j]; + if (c == ';') + { + newAddress.Append(' '); + } + else + { + newAddress.Append(c); + } + } + return newAddress.ToString().Trim(); + } + + /// Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like + /// "Reverend John Q. Public III". + /// + /// + /// name values to format, in place + /// + private static void formatNames(System.String[] names) + { + if (names != null) + { + for (int i = 0; i < names.Length; i++) + { + System.String name = names[i]; + System.String[] components = new System.String[5]; + int start = 0; + int end; + int componentIndex = 0; + //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'" + while ((end = name.IndexOf(';', start)) > 0) + { + components[componentIndex] = name.Substring(start, (end) - (start)); + componentIndex++; + start = end + 1; + } + components[componentIndex] = name.Substring(start); + System.Text.StringBuilder newName = new System.Text.StringBuilder(100); + maybeAppendComponent(components, 3, newName); + maybeAppendComponent(components, 1, newName); + maybeAppendComponent(components, 2, newName); + maybeAppendComponent(components, 0, newName); + maybeAppendComponent(components, 4, newName); + names[i] = newName.ToString().Trim(); + } + } + } + + private static void maybeAppendComponent(System.String[] components, int i, System.Text.StringBuilder newName) + { + if (components[i] != null) + { + newName.Append(' '); + newName.Append(components[i]); + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/VEventResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/VEventResultParser.cs new file mode 100644 index 0000000..83cfa63 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/VEventResultParser.cs @@ -0,0 +1,67 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result +{ + + /// Partially implements the iCalendar format's "VEVENT" format for specifying a + /// calendar event. See RFC 2445. This supports SUMMARY, DTSTART and DTEND fields. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class VEventResultParser:ResultParser + { + + private VEventResultParser() + { + } + + public static CalendarParsedResult parse(Result result) + { + System.String rawText = result.Text; + if (rawText == null) + { + return null; + } + int vEventStart = rawText.IndexOf("BEGIN:VEVENT"); + if (vEventStart < 0) + { + return null; + } + int vEventEnd = rawText.IndexOf("END:VEVENT"); + if (vEventEnd < 0) + { + return null; + } + + System.String summary = VCardResultParser.matchSingleVCardPrefixedField("SUMMARY", rawText, true); + System.String start = VCardResultParser.matchSingleVCardPrefixedField("DTSTART", rawText, true); + System.String end = VCardResultParser.matchSingleVCardPrefixedField("DTEND", rawText, true); + try + { + return new CalendarParsedResult(summary, start, end, null, null, null); + } + catch (System.ArgumentException iae) + { + return null; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/AbstractNDEFResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/AbstractNDEFResultParser.cs new file mode 100644 index 0000000..890293d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/AbstractNDEFResultParser.cs @@ -0,0 +1,56 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ResultParser = com.google.zxing.client.result.ResultParser; +namespace com.google.zxing.client.result.optional +{ + + ///

Superclass for classes encapsulating results in the NDEF format. + /// See http://www.nfc-forum.org/specs/.

+ /// + ///

This code supports a limited subset of NDEF messages, ones that are plausibly + /// useful in 2D barcode formats. This generally includes 1-record messages, no chunking, + /// "short record" syntax, no ID field.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + abstract class AbstractNDEFResultParser:ResultParser + { + + internal static System.String bytesToString(sbyte[] bytes, int offset, int length, System.String encoding) + { + try + { + System.String tempStr; + //UPGRADE_TODO: The differences in the Format of parameters for constructor 'java.lang.String.String' may cause compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'" + // tempStr = System.Text.Encoding.GetEncoding(encoding).GetString(SupportClass.ToByteArray(bytes)); // commented by .net follower (http://dotnetfollower.com) + byte[] inputBytes = SupportClass.ToByteArray(bytes); // added by .net follower (http://dotnetfollower.com) + tempStr = System.Text.Encoding.GetEncoding(encoding).GetString(inputBytes, 0, inputBytes.Length); // added by .net follower (http://dotnetfollower.com) + return new System.String(tempStr.ToCharArray(), offset, length); + } + catch (System.IO.IOException uee) + { + // This should only be used when 'encoding' is an encoding that must necessarily + // be supported by the JVM, like UTF-8 + //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" + throw new System.SystemException("Platform does not support required encoding: " + uee); + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFRecord.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFRecord.cs new file mode 100644 index 0000000..18ecf47 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFRecord.cs @@ -0,0 +1,118 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.client.result.optional +{ + + ///

Represents a record in an NDEF message. This class only supports certain types + /// of records -- namely, non-chunked records, where ID length is omitted, and only + /// "short records".

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class NDEFRecord + { + internal bool MessageBegin + { + get + { + return (header & 0x80) != 0; + } + + } + internal bool MessageEnd + { + get + { + return (header & 0x40) != 0; + } + + } + internal System.String Type + { + get + { + return type; + } + + } + internal sbyte[] Payload + { + get + { + return payload; + } + + } + internal int TotalRecordLength + { + get + { + return totalRecordLength; + } + + } + + private const int SUPPORTED_HEADER_MASK = 0x3F; // 0 0 1 1 1 111 (the bottom 6 bits matter) + private const int SUPPORTED_HEADER = 0x11; // 0 0 0 1 0 001 + + public const System.String TEXT_WELL_KNOWN_TYPE = "T"; + public const System.String URI_WELL_KNOWN_TYPE = "U"; + public const System.String SMART_POSTER_WELL_KNOWN_TYPE = "Sp"; + public const System.String ACTION_WELL_KNOWN_TYPE = "act"; + + //UPGRADE_NOTE: Final was removed from the declaration of 'header '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int header; + //UPGRADE_NOTE: Final was removed from the declaration of 'type '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String type; + //UPGRADE_NOTE: Final was removed from the declaration of 'payload '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[] payload; + //UPGRADE_NOTE: Final was removed from the declaration of 'totalRecordLength '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int totalRecordLength; + + private NDEFRecord(int header, System.String type, sbyte[] payload, int totalRecordLength) + { + this.header = header; + this.type = type; + this.payload = payload; + this.totalRecordLength = totalRecordLength; + } + + internal static NDEFRecord readRecord(sbyte[] bytes, int offset) + { + int header = bytes[offset] & 0xFF; + // Does header match what we support in the bits we care about? + // XOR figures out where we differ, and if any of those are in the mask, fail + if (((header ^ SUPPORTED_HEADER) & SUPPORTED_HEADER_MASK) != 0) + { + return null; + } + int typeLength = bytes[offset + 1] & 0xFF; + + int payloadLength = bytes[offset + 2] & 0xFF; + + System.String type = AbstractNDEFResultParser.bytesToString(bytes, offset + 3, typeLength, "US-ASCII"); + + sbyte[] payload = new sbyte[payloadLength]; + Array.Copy(bytes, offset + 3 + typeLength, payload, 0, payloadLength); + + return new NDEFRecord(header, type, payload, 3 + typeLength + payloadLength); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFSmartPosterParsedResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFSmartPosterParsedResult.cs new file mode 100644 index 0000000..512e898 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFSmartPosterParsedResult.cs @@ -0,0 +1,87 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ParsedResult = com.google.zxing.client.result.ParsedResult; +using ParsedResultType = com.google.zxing.client.result.ParsedResultType; +namespace com.google.zxing.client.result.optional +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class NDEFSmartPosterParsedResult:ParsedResult + { + public System.String Title + { + get + { + return title; + } + + } + public System.String URI + { + get + { + return uri; + } + + } + public int Action + { + get + { + return action; + } + + } + override public System.String DisplayResult + { + get + { + if (title == null) + { + return uri; + } + else + { + return title + '\n' + uri; + } + } + + } + + public const int ACTION_UNSPECIFIED = - 1; + public const int ACTION_DO = 0; + public const int ACTION_SAVE = 1; + public const int ACTION_OPEN = 2; + + //UPGRADE_NOTE: Final was removed from the declaration of 'title '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String title; + //UPGRADE_NOTE: Final was removed from the declaration of 'uri '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String uri; + //UPGRADE_NOTE: Final was removed from the declaration of 'action '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int action; + + internal NDEFSmartPosterParsedResult(int action, System.String uri, System.String title):base(ParsedResultType.NDEF_SMART_POSTER) + { + this.action = action; + this.uri = uri; + this.title = title; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFSmartPosterResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFSmartPosterResultParser.cs new file mode 100644 index 0000000..19ff446 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFSmartPosterResultParser.cs @@ -0,0 +1,96 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +namespace com.google.zxing.client.result.optional +{ + + ///

Recognizes an NDEF message that encodes information according to the + /// "Smart Poster Record Type Definition" specification.

+ /// + ///

This actually only supports some parts of the Smart Poster format: title, + /// URI, and action records. Icon records are not supported because the size + /// of these records are infeasibly large for barcodes. Size and type records + /// are not supported. Multiple titles are not supported.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class NDEFSmartPosterResultParser:AbstractNDEFResultParser + { + + public static NDEFSmartPosterParsedResult parse(Result result) + { + sbyte[] bytes = result.RawBytes; + if (bytes == null) + { + return null; + } + NDEFRecord headerRecord = NDEFRecord.readRecord(bytes, 0); + // Yes, header record starts and ends a message + if (headerRecord == null || !headerRecord.MessageBegin || !headerRecord.MessageEnd) + { + return null; + } + if (!headerRecord.Type.Equals(NDEFRecord.SMART_POSTER_WELL_KNOWN_TYPE)) + { + return null; + } + + int offset = 0; + int recordNumber = 0; + NDEFRecord ndefRecord = null; + sbyte[] payload = headerRecord.Payload; + int action = NDEFSmartPosterParsedResult.ACTION_UNSPECIFIED; + System.String title = null; + System.String uri = null; + + while (offset < payload.Length && (ndefRecord = NDEFRecord.readRecord(payload, offset)) != null) + { + if (recordNumber == 0 && !ndefRecord.MessageBegin) + { + return null; + } + + System.String type = ndefRecord.Type; + if (NDEFRecord.TEXT_WELL_KNOWN_TYPE.Equals(type)) + { + System.String[] languageText = NDEFTextResultParser.decodeTextPayload(ndefRecord.Payload); + title = languageText[1]; + } + else if (NDEFRecord.URI_WELL_KNOWN_TYPE.Equals(type)) + { + uri = NDEFURIResultParser.decodeURIPayload(ndefRecord.Payload); + } + else if (NDEFRecord.ACTION_WELL_KNOWN_TYPE.Equals(type)) + { + action = ndefRecord.Payload[0]; + } + recordNumber++; + offset += ndefRecord.TotalRecordLength; + } + + if (recordNumber == 0 || (ndefRecord != null && !ndefRecord.MessageEnd)) + { + return null; + } + + return new NDEFSmartPosterParsedResult(action, uri, title); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFTextResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFTextResultParser.cs new file mode 100644 index 0000000..e38363b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFTextResultParser.cs @@ -0,0 +1,65 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +using TextParsedResult = com.google.zxing.client.result.TextParsedResult; +namespace com.google.zxing.client.result.optional +{ + + /// Recognizes an NDEF message that encodes text according to the + /// "Text Record Type Definition" specification. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class NDEFTextResultParser:AbstractNDEFResultParser + { + + public static TextParsedResult parse(Result result) + { + sbyte[] bytes = result.RawBytes; + if (bytes == null) + { + return null; + } + NDEFRecord ndefRecord = NDEFRecord.readRecord(bytes, 0); + if (ndefRecord == null || !ndefRecord.MessageBegin || !ndefRecord.MessageEnd) + { + return null; + } + if (!ndefRecord.Type.Equals(NDEFRecord.TEXT_WELL_KNOWN_TYPE)) + { + return null; + } + System.String[] languageText = decodeTextPayload(ndefRecord.Payload); + return new TextParsedResult(languageText[0], languageText[1]); + } + + internal static System.String[] decodeTextPayload(sbyte[] payload) + { + sbyte statusByte = payload[0]; + bool isUTF16 = (statusByte & 0x80) != 0; + int languageLength = statusByte & 0x1F; + // language is always ASCII-encoded: + System.String language = bytesToString(payload, 1, languageLength, "US-ASCII"); + System.String encoding = isUTF16?"UTF-16":"UTF8"; + System.String text = bytesToString(payload, 1 + languageLength, payload.Length - languageLength - 1, encoding); + return new System.String[]{language, text}; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFURIResultParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFURIResultParser.cs new file mode 100644 index 0000000..fabab7d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/client/result/optional/NDEFURIResultParser.cs @@ -0,0 +1,68 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +using URIParsedResult = com.google.zxing.client.result.URIParsedResult; +namespace com.google.zxing.client.result.optional +{ + + /// Recognizes an NDEF message that encodes a URI according to the + /// "URI Record Type Definition" specification. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class NDEFURIResultParser:AbstractNDEFResultParser + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'URI_PREFIXES'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly System.String[] URI_PREFIXES = new System.String[]{null, "http://www.", "https://www.", "http://", "https://", "tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.", "ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://", "news:", "telnet://", "imap:", "rtsp://", "urn:", "pop:", "sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://", "tcpobex://", "irdaobex://", "file://", "urn:epc:id:", "urn:epc:tag:", "urn:epc:pat:", "urn:epc:raw:", "urn:epc:", "urn:nfc:"}; + + public static URIParsedResult parse(Result result) + { + sbyte[] bytes = result.RawBytes; + if (bytes == null) + { + return null; + } + NDEFRecord ndefRecord = NDEFRecord.readRecord(bytes, 0); + if (ndefRecord == null || !ndefRecord.MessageBegin || !ndefRecord.MessageEnd) + { + return null; + } + if (!ndefRecord.Type.Equals(NDEFRecord.URI_WELL_KNOWN_TYPE)) + { + return null; + } + System.String fullURI = decodeURIPayload(ndefRecord.Payload); + return new URIParsedResult(fullURI, null); + } + + internal static System.String decodeURIPayload(sbyte[] payload) + { + int identifierCode = payload[0] & 0xFF; + System.String prefix = null; + if (identifierCode < URI_PREFIXES.Length) + { + prefix = URI_PREFIXES[identifierCode]; + } + System.String restOfURI = bytesToString(payload, 1, payload.Length - 1, "UTF8"); + return prefix == null?restOfURI:prefix + restOfURI; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitArray.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitArray.cs new file mode 100644 index 0000000..51f2776 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitArray.cs @@ -0,0 +1,208 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + ///

A simple, fast array of bits, represented compactly by an array of ints internally.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class BitArray + { + public int Size + { + get + { + return size; + } + + } + + // TODO: I have changed these members to be public so ProGuard can inline get() and set(). Ideally + // they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the + // resulting binary at runtime on Android. If we find a solution to this, these should be changed + // back to private. + public int[] bits; + //UPGRADE_NOTE: Final was removed from the declaration of 'size '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public int size; + + public BitArray(int size) + { + if (size < 1) + { + throw new System.ArgumentException("size must be at least 1"); + } + this.size = size; + this.bits = makeArray(size); + } + + /// bit to get + /// + /// true iff bit i is set + /// + public bool get_Renamed(int i) + { + return (bits[i >> 5] & (1 << (i & 0x1F))) != 0; + } + + /// Sets bit i. + /// + /// + /// bit to set + /// + public void set_Renamed(int i) + { + bits[i >> 5] |= 1 << (i & 0x1F); + } + + /// Flips bit i. + /// + /// + /// bit to set + /// + public void flip(int i) + { + bits[i >> 5] ^= 1 << (i & 0x1F); + } + + /// Sets a block of 32 bits, starting at bit i. + /// + /// + /// first bit to set + /// + /// the new value of the next 32 bits. Note again that the least-significant bit + /// corresponds to bit i, the next-least-significant to i+1, and so on. + /// + public void setBulk(int i, int newBits) + { + bits[i >> 5] = newBits; + } + + /// Clears all bits (sets to false). + public void clear() + { + int max = bits.Length; + for (int i = 0; i < max; i++) + { + bits[i] = 0; + } + } + + /// Efficient method to check if a range of bits is set, or not set. + /// + /// + /// start of range, inclusive. + /// + /// end of range, exclusive + /// + /// if true, checks that bits in range are set, otherwise checks that they are not set + /// + /// true iff all bits are set or not set in range, according to value argument + /// + /// IllegalArgumentException if end is less than or equal to start + public bool isRange(int start, int end, bool value_Renamed) + { + if (end < start) + { + throw new System.ArgumentException(); + } + if (end == start) + { + return true; // empty range matches + } + end--; // will be easier to treat this as the last actually set bit -- inclusive + int firstInt = start >> 5; + int lastInt = end >> 5; + for (int i = firstInt; i <= lastInt; i++) + { + int firstBit = i > firstInt?0:start & 0x1F; + int lastBit = i < lastInt?31:end & 0x1F; + int mask; + if (firstBit == 0 && lastBit == 31) + { + mask = - 1; + } + else + { + mask = 0; + for (int j = firstBit; j <= lastBit; j++) + { + mask |= 1 << j; + } + } + + // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is, + // equals the mask, or we're looking for 0s and the masked portion is not all 0s + if ((bits[i] & mask) != (value_Renamed?mask:0)) + { + return false; + } + } + return true; + } + + /// underlying array of ints. The first element holds the first 32 bits, and the least + /// significant bit is bit 0. + /// + public int[] getBitArray() + { + return bits; + } + + /// Reverses all bits in the array. + public void reverse() + { + int[] newBits = new int[bits.Length]; + int size = this.size; + for (int i = 0; i < size; i++) + { + if (get_Renamed(size - i - 1)) + { + newBits[i >> 5] |= 1 << (i & 0x1F); + } + } + bits = newBits; + } + + private static int[] makeArray(int size) + { + int arraySize = size >> 5; + if ((size & 0x1F) != 0) + { + arraySize++; + } + return new int[arraySize]; + } + + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(size); + for (int i = 0; i < size; i++) + { + if ((i & 0x07) == 0) + { + result.Append(' '); + } + result.Append(get_Renamed(i)?'X':'.'); + } + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitMatrix.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitMatrix.cs new file mode 100644 index 0000000..6612ff4 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitMatrix.cs @@ -0,0 +1,237 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + ///

Represents a 2D matrix of bits. In function arguments below, and throughout the common + /// module, x is the column position, and y is the row position. The ordering is always x, y. + /// The origin is at the top-left.

+ /// + ///

Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins + /// with a new int. This is done intentionally so that we can copy out a row into a BitArray very + /// efficiently.

+ /// + ///

The ordering of bits is row-major. Within each int, the least significant bits are used first, + /// meaning they represent lower x values. This is compatible with BitArray's implementation.

+ /// + ///
+ /// Sean Owen + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class BitMatrix + { + /// The width of the matrix + /// + public int Width + { + get + { + return width; + } + + } + /// The height of the matrix + /// + public int Height + { + get + { + return height; + } + + } + /// This method is for compatibility with older code. It's only logical to call if the matrix + /// is square, so I'm throwing if that's not the case. + /// + /// + /// row/column dimension of this matrix + /// + public int Dimension + { + get + { + if (width != height) + { + throw new System.SystemException("Can't call getDimension() on a non-square matrix"); + } + return width; + } + + } + + // TODO: Just like BitArray, these need to be public so ProGuard can inline them. + //UPGRADE_NOTE: Final was removed from the declaration of 'width '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public int width; + //UPGRADE_NOTE: Final was removed from the declaration of 'height '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public int height; + //UPGRADE_NOTE: Final was removed from the declaration of 'rowSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public int rowSize; + //UPGRADE_NOTE: Final was removed from the declaration of 'bits '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public int[] bits; + + // A helper to construct a square matrix. + public BitMatrix(int dimension):this(dimension, dimension) + { + } + + public BitMatrix(int width, int height) + { + if (width < 1 || height < 1) + { + throw new System.ArgumentException("Both dimensions must be greater than 0"); + } + this.width = width; + this.height = height; + int rowSize = width >> 5; + if ((width & 0x1f) != 0) + { + rowSize++; + } + this.rowSize = rowSize; + bits = new int[rowSize * height]; + } + + ///

Gets the requested bit, where true means black.

+ /// + ///
+ /// The horizontal component (i.e. which column) + /// + /// The vertical component (i.e. which row) + /// + /// value of given bit in matrix + /// + public bool get_Renamed(int x, int y) + { + int offset = y * rowSize + (x >> 5); + return ((SupportClass.URShift(bits[offset], (x & 0x1f))) & 1) != 0; + } + + ///

Sets the given bit to true.

+ /// + ///
+ /// The horizontal component (i.e. which column) + /// + /// The vertical component (i.e. which row) + /// + public void set_Renamed(int x, int y) + { + int offset = y * rowSize + (x >> 5); + bits[offset] |= 1 << (x & 0x1f); + } + + ///

Flips the given bit.

+ /// + ///
+ /// The horizontal component (i.e. which column) + /// + /// The vertical component (i.e. which row) + /// + public void flip(int x, int y) + { + int offset = y * rowSize + (x >> 5); + bits[offset] ^= 1 << (x & 0x1f); + } + + /// Clears all bits (sets to false). + public void clear() + { + int max = bits.Length; + for (int i = 0; i < max; i++) + { + bits[i] = 0; + } + } + + ///

Sets a square region of the bit matrix to true.

+ /// + ///
+ /// The horizontal position to begin at (inclusive) + /// + /// The vertical position to begin at (inclusive) + /// + /// The width of the region + /// + /// The height of the region + /// + public void setRegion(int left, int top, int width, int height) + { + if (top < 0 || left < 0) + { + throw new System.ArgumentException("Left and top must be nonnegative"); + } + if (height < 1 || width < 1) + { + throw new System.ArgumentException("Height and width must be at least 1"); + } + int right = left + width; + int bottom = top + height; + if (bottom > this.height || right > this.width) + { + throw new System.ArgumentException("The region must fit inside the matrix"); + } + for (int y = top; y < bottom; y++) + { + int offset = y * rowSize; + for (int x = left; x < right; x++) + { + bits[offset + (x >> 5)] |= 1 << (x & 0x1f); + } + } + } + + /// A fast method to retrieve one row of data from the matrix as a BitArray. + /// + /// + /// The row to retrieve + /// + /// An optional caller-allocated BitArray, will be allocated if null or too small + /// + /// The resulting BitArray - this reference should always be used even when passing + /// your own row + /// + public BitArray getRow(int y, BitArray row) + { + if (row == null || row.Size < width) + { + row = new BitArray(width); + } + int offset = y * rowSize; + for (int x = 0; x < rowSize; x++) + { + row.setBulk(x << 5, bits[offset + x]); + } + return row; + } + + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(height * (width + 1)); + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + result.Append(get_Renamed(x, y)?"X ":" "); + } + result.Append('\n'); + } + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitSource.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitSource.cs new file mode 100644 index 0000000..983caa7 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/BitSource.cs @@ -0,0 +1,109 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + ///

This provides an easy abstraction to read bits at a time from a sequence of bytes, where the + /// number of bits read is not often a multiple of 8.

+ /// + ///

This class is thread-safe but not reentrant. Unless the caller modifies the bytes array + /// it passed in, in which case all bets are off.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class BitSource + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'bytes '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[] bytes; + private int byteOffset; + private int bitOffset; + + /// bytes from which this will read bits. Bits will be read from the first byte first. + /// Bits are read within a byte from most-significant to least-significant bit. + /// + public BitSource(sbyte[] bytes) + { + this.bytes = bytes; + } + + /// number of bits to read + /// + /// int representing the bits read. The bits will appear as the least-significant + /// bits of the int + /// + /// IllegalArgumentException if numBits isn't in [1,32] + public int readBits(int numBits) + { + if (numBits < 1 || numBits > 32) + { + throw new System.ArgumentException(); + } + + int result = 0; + + // First, read remainder from current byte + if (bitOffset > 0) + { + int bitsLeft = 8 - bitOffset; + int toRead = numBits < bitsLeft?numBits:bitsLeft; + int bitsToNotRead = bitsLeft - toRead; + int mask = (0xFF >> (8 - toRead)) << bitsToNotRead; + result = (bytes[byteOffset] & mask) >> bitsToNotRead; + numBits -= toRead; + bitOffset += toRead; + if (bitOffset == 8) + { + bitOffset = 0; + byteOffset++; + } + } + + // Next read whole bytes + if (numBits > 0) + { + while (numBits >= 8) + { + result = (result << 8) | (bytes[byteOffset] & 0xFF); + byteOffset++; + numBits -= 8; + } + + // Finally read a partial byte + if (numBits > 0) + { + int bitsToNotRead = 8 - numBits; + int mask = (0xFF >> bitsToNotRead) << bitsToNotRead; + result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead); + bitOffset += numBits; + } + } + + return result; + } + + /// number of bits that can be read successfully + /// + public int available() + { + return 8 * (bytes.Length - byteOffset) - bitOffset; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ByteArray.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ByteArray.cs new file mode 100644 index 0000000..ada385d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ByteArray.cs @@ -0,0 +1,116 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + /// This class implements an array of unsigned bytes. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ByteArray + { + public bool Empty + { + get + { + return size_Renamed_Field == 0; + } + + } + + private const int INITIAL_SIZE = 32; + + private sbyte[] bytes; + private int size_Renamed_Field; + + public ByteArray() + { + bytes = null; + size_Renamed_Field = 0; + } + + public ByteArray(int size) + { + bytes = new sbyte[size]; + this.size_Renamed_Field = size; + } + + public ByteArray(sbyte[] byteArray) + { + bytes = byteArray; + size_Renamed_Field = bytes.Length; + } + + /// Access an unsigned byte at location index. + /// The index in the array to access. + /// + /// The unsigned value of the byte as an int. + /// + public int at(int index) + { + return bytes[index] & 0xff; + } + + public void set_Renamed(int index, int value_Renamed) + { + bytes[index] = (sbyte) value_Renamed; + } + + public int size() + { + return size_Renamed_Field; + } + + public void appendByte(int value_Renamed) + { + if (size_Renamed_Field == 0 || size_Renamed_Field >= bytes.Length) + { + int newSize = System.Math.Max(INITIAL_SIZE, size_Renamed_Field << 1); + reserve(newSize); + } + bytes[size_Renamed_Field] = (sbyte) value_Renamed; + size_Renamed_Field++; + } + + public void reserve(int capacity) + { + if (bytes == null || bytes.Length < capacity) + { + sbyte[] newArray = new sbyte[capacity]; + if (bytes != null) + { + Array.Copy(bytes, 0, newArray, 0, bytes.Length); + } + bytes = newArray; + } + } + + // Copy count bytes from array source starting at offset. + public void set_Renamed(sbyte[] source, int offset, int count) + { + bytes = new sbyte[count]; + size_Renamed_Field = count; + for (int x = 0; x < count; x++) + { + bytes[x] = source[offset + x]; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ByteMatrix.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ByteMatrix.cs new file mode 100644 index 0000000..c656d40 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ByteMatrix.cs @@ -0,0 +1,131 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + /// A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a + /// unsigned container, it's up to you to do byteValue & 0xff at each location. + /// + /// JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned + /// -1, 0, and 1, I'm going to use less memory and go with bytes. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ByteMatrix + { + public int Height + { + get + { + return height; + } + + } + public int Width + { + get + { + return width; + } + + } + public sbyte[][] Array + { + get + { + return bytes; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'bytes '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[][] bytes; + //UPGRADE_NOTE: Final was removed from the declaration of 'width '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int width; + //UPGRADE_NOTE: Final was removed from the declaration of 'height '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int height; + + public ByteMatrix(int width, int height) + { + bytes = new sbyte[height][]; + for (int i = 0; i < height; i++) + { + bytes[i] = new sbyte[width]; + } + this.width = width; + this.height = height; + } + + public sbyte get_Renamed(int x, int y) + { + return bytes[y][x]; + } + + public void set_Renamed(int x, int y, sbyte value_Renamed) + { + bytes[y][x] = value_Renamed; + } + + public void set_Renamed(int x, int y, int value_Renamed) + { + bytes[y][x] = (sbyte) value_Renamed; + } + + public void clear(sbyte value_Renamed) + { + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + bytes[y][x] = value_Renamed; + } + } + } + + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(2 * width * height + 2); + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + switch (bytes[y][x]) + { + + case 0: + result.Append(" 0"); + break; + + case 1: + result.Append(" 1"); + break; + + default: + result.Append(" "); + break; + + } + } + result.Append('\n'); + } + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/CharacterSetECI.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/CharacterSetECI.cs new file mode 100644 index 0000000..c952630 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/CharacterSetECI.cs @@ -0,0 +1,130 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + /// Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 + /// of ISO 18004. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class CharacterSetECI:ECI + { + public System.String EncodingName + { + get + { + return encodingName; + } + + } + + // private static System.Collections.Hashtable VALUE_TO_ECI; // commented by .net follower (http://dotnetfollower.com) + private static System.Collections.Generic.Dictionary VALUE_TO_ECI; // added by .net follower (http://dotnetfollower.com) + // private static System.Collections.Hashtable NAME_TO_ECI; // commented by .net follower (http://dotnetfollower.com) + private static System.Collections.Generic.Dictionary NAME_TO_ECI; // added by .net follower (http://dotnetfollower.com) + + private static void initialize() + { + // VALUE_TO_ECI = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable(29)); // commented by .net follower (http://dotnetfollower.com) + VALUE_TO_ECI = new System.Collections.Generic.Dictionary(29); // added by .net follower (http://dotnetfollower.com) + // NAME_TO_ECI = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable(29)); // commented by .net follower (http://dotnetfollower.com) + NAME_TO_ECI = new System.Collections.Generic.Dictionary(29); // added by .net follower (http://dotnetfollower.com) + // TODO figure out if these values are even right! + addCharacterSet(0, "Cp437"); + addCharacterSet(1, new System.String[]{"ISO8859_1", "ISO-8859-1"}); + addCharacterSet(2, "Cp437"); + addCharacterSet(3, new System.String[]{"ISO8859_1", "ISO-8859-1"}); + addCharacterSet(4, "ISO8859_2"); + addCharacterSet(5, "ISO8859_3"); + addCharacterSet(6, "ISO8859_4"); + addCharacterSet(7, "ISO8859_5"); + addCharacterSet(8, "ISO8859_6"); + addCharacterSet(9, "ISO8859_7"); + addCharacterSet(10, "ISO8859_8"); + addCharacterSet(11, "ISO8859_9"); + addCharacterSet(12, "ISO8859_10"); + addCharacterSet(13, "ISO8859_11"); + addCharacterSet(15, "ISO8859_13"); + addCharacterSet(16, "ISO8859_14"); + addCharacterSet(17, "ISO8859_15"); + addCharacterSet(18, "ISO8859_16"); + addCharacterSet(20, new System.String[]{"SJIS", "Shift_JIS"}); + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'encodingName '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String encodingName; + + private CharacterSetECI(int value_Renamed, System.String encodingName):base(value_Renamed) + { + this.encodingName = encodingName; + } + + private static void addCharacterSet(int value_Renamed, System.String encodingName) + { + CharacterSetECI eci = new CharacterSetECI(value_Renamed, encodingName); + VALUE_TO_ECI[(System.Int32) value_Renamed] = eci; // can't use valueOf + NAME_TO_ECI[encodingName] = eci; + } + + private static void addCharacterSet(int value_Renamed, System.String[] encodingNames) + { + CharacterSetECI eci = new CharacterSetECI(value_Renamed, encodingNames[0]); + VALUE_TO_ECI[(System.Int32) value_Renamed] = eci; // can't use valueOf + for (int i = 0; i < encodingNames.Length; i++) + { + NAME_TO_ECI[encodingNames[i]] = eci; + } + } + + /// character set ECI value + /// + /// {@link CharacterSetECI} representing ECI of given value, or null if it is legal but + /// unsupported + /// + /// IllegalArgumentException if ECI value is invalid + public static CharacterSetECI getCharacterSetECIByValue(int value_Renamed) + { + if (VALUE_TO_ECI == null) + { + initialize(); + } + if (value_Renamed < 0 || value_Renamed >= 900) + { + throw new System.ArgumentException("Bad ECI value: " + value_Renamed); + } + return (CharacterSetECI) VALUE_TO_ECI[(System.Int32) value_Renamed]; + } + + /// character set ECI encoding name + /// + /// {@link CharacterSetECI} representing ECI for character encoding, or null if it is legal + /// but unsupported + /// + public static CharacterSetECI getCharacterSetECIByName(System.String name) + { + if (NAME_TO_ECI == null) + { + initialize(); + } + return (CharacterSetECI) NAME_TO_ECI[name]; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/Collections.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/Collections.cs new file mode 100644 index 0000000..a9d79a9 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/Collections.cs @@ -0,0 +1,61 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + ///

This is basically a substitute for java.util.Collections, which is not + /// present in MIDP 2.0 / CLDC 1.1.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Collections + { + + private Collections() + { + } + + /// Sorts its argument (destructively) using insert sort; in the context of this package + /// insertion sort is simple and efficient given its relatively small inputs. + /// + /// + /// vector to sort + /// + /// comparator to define sort ordering + /// + // public static void insertionSort(System.Collections.ArrayList vector, Comparator comparator) // commented by .net follower (http://dotnetfollower.com) + public static void insertionSort(System.Collections.Generic.List vector, Comparator comparator) // added by .net follower (http://dotnetfollower.com) + { + int max = vector.Count; + for (int i = 1; i < max; i++) + { + System.Object value_Renamed = vector[i]; + int j = i - 1; + System.Object valueB; + while (j >= 0 && comparator.compare((valueB = vector[j]), value_Renamed) > 0) + { + vector[j + 1] = valueB; + j--; + } + vector[j + 1] = value_Renamed; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/Comparator.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/Comparator.cs new file mode 100644 index 0000000..450be93 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/Comparator.cs @@ -0,0 +1,28 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + /// This is merely a clone of Comparator since it is not available in + /// CLDC 1.1 / MIDP 2.0. + /// + public interface Comparator + { + + int compare(System.Object o1, System.Object o2); + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DecoderResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DecoderResult.cs new file mode 100644 index 0000000..8874059 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DecoderResult.cs @@ -0,0 +1,89 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +namespace com.google.zxing.common +{ + + ///

Encapsulates the result of decoding a matrix of bits. This typically + /// applies to 2D barcode formats. For now it contains the raw bytes obtained, + /// as well as a String interpretation of those bytes, if applicable.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class DecoderResult + { + public sbyte[] RawBytes + { + get + { + return rawBytes; + } + + } + public System.String Text + { + get + { + return text; + } + + } + // public System.Collections.ArrayList ByteSegments // commented by .net follower (http://dotnetfollower.com) + public System.Collections.Generic.List ByteSegments // added by .net follower (http://dotnetfollower.com) + { + get + { + return byteSegments; + } + + } + public ErrorCorrectionLevel ECLevel + { + get + { + return ecLevel; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'rawBytes '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[] rawBytes; + //UPGRADE_NOTE: Final was removed from the declaration of 'text '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String text; + //UPGRADE_NOTE: Final was removed from the declaration of 'byteSegments '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + // private System.Collections.ArrayList byteSegments; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.List byteSegments; // added by .net follower (http://dotnetfollower.com) + //UPGRADE_NOTE: Final was removed from the declaration of 'ecLevel '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ErrorCorrectionLevel ecLevel; + + // public DecoderResult(sbyte[] rawBytes, System.String text, System.Collections.ArrayList byteSegments, ErrorCorrectionLevel ecLevel) // commented by .net follower (http://dotnetfollower.com) + public DecoderResult(sbyte[] rawBytes, System.String text, System.Collections.Generic.List byteSegments, ErrorCorrectionLevel ecLevel) // added by .net follower (http://dotnetfollower.com) + { + if (rawBytes == null && text == null) + { + throw new System.ArgumentException(); + } + this.rawBytes = rawBytes; + this.text = text; + this.byteSegments = byteSegments; + this.ecLevel = ecLevel; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DefaultGridSampler.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DefaultGridSampler.cs new file mode 100644 index 0000000..f884dbf --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DefaultGridSampler.cs @@ -0,0 +1,82 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +namespace com.google.zxing.common +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class DefaultGridSampler:GridSampler + { + + public override BitMatrix sampleGrid(BitMatrix image, int dimension, float p1ToX, float p1ToY, float p2ToX, float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX, float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) + { + + PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); + + return sampleGrid(image, dimension, transform); + } + + public override BitMatrix sampleGrid(BitMatrix image, int dimension, PerspectiveTransform transform) + { + BitMatrix bits = new BitMatrix(dimension); + float[] points = new float[dimension << 1]; + for (int y = 0; y < dimension; y++) + { + int max = points.Length; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float iValue = (float) y + 0.5f; + for (int x = 0; x < max; x += 2) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + points[x] = (float) (x >> 1) + 0.5f; + points[x + 1] = iValue; + } + transform.transformPoints(points); + // Quick check to see if points transformed to something inside the image; + // sufficient to check the endpoints + checkAndNudgePoints(image, points); + try + { + for (int x = 0; x < max; x += 2) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + if (image.get_Renamed((int) points[x], (int) points[x + 1])) + { + // Black(-ish) pixel + bits.set_Renamed(x >> 1, y); + } + } + } + catch (System.IndexOutOfRangeException aioobe) + { + // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting + // transform gets "twisted" such that it maps a straight line of points to a set of points + // whose endpoints are in bounds, but others are not. There is probably some mathematical + // way to detect this about the transformation that I don't know yet. + // This results in an ugly runtime exception despite our clever checks above -- can't have + // that. We could check each point's coordinates but that feels duplicative. We settle for + // catching and wrapping ArrayIndexOutOfBoundsException. + throw ReaderException.Instance; + } + } + return bits; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DetectorResult.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DetectorResult.cs new file mode 100644 index 0000000..9a327a4 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/DetectorResult.cs @@ -0,0 +1,60 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ResultPoint = com.google.zxing.ResultPoint; +namespace com.google.zxing.common +{ + + ///

Encapsulates the result of detecting a barcode in an image. This includes the raw + /// matrix of black/white pixels corresponding to the barcode, and possibly points of interest + /// in the image, like the location of finder patterns or corners of the barcode in the image.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class DetectorResult + { + public BitMatrix Bits + { + get + { + return bits; + } + + } + public ResultPoint[] Points + { + get + { + return points; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'bits '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix bits; + //UPGRADE_NOTE: Final was removed from the declaration of 'points '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPoint[] points; + + public DetectorResult(BitMatrix bits, ResultPoint[] points) + { + this.bits = bits; + this.points = points; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ECI.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ECI.cs new file mode 100644 index 0000000..27ff679 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/ECI.cs @@ -0,0 +1,66 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + /// Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations" + /// 5.3 of ISO 18004. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class ECI + { + virtual public int Value + { + get + { + return value_Renamed; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'value '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int value_Renamed; + + internal ECI(int value_Renamed) + { + this.value_Renamed = value_Renamed; + } + + /// ECI value + /// + /// {@link ECI} representing ECI of given value, or null if it is legal but unsupported + /// + /// IllegalArgumentException if ECI value is invalid + public static ECI getECIByValue(int value_Renamed) + { + if (value_Renamed < 0 || value_Renamed > 999999) + { + throw new System.ArgumentException("Bad ECI value: " + value_Renamed); + } + if (value_Renamed < 900) + { + // Character set ECIs use 000000 - 000899 + return CharacterSetECI.getCharacterSetECIByValue(value_Renamed); + } + return null; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/GlobalHistogramBinarizer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/GlobalHistogramBinarizer.cs new file mode 100644 index 0000000..f6d0ae2 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/GlobalHistogramBinarizer.cs @@ -0,0 +1,250 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using Binarizer = com.google.zxing.Binarizer; +using LuminanceSource = com.google.zxing.LuminanceSource; +using ReaderException = com.google.zxing.ReaderException; +namespace com.google.zxing.common +{ + + /// This Binarizer implementation uses the old ZXing global histogram approach. It is suitable + /// for low-end mobile devices which don't have enough CPU or memory to use a local thresholding + /// algorithm. However, because it picks a global black point, it cannot handle difficult shadows + /// and gradients. + /// + /// Faster mobile devices and all desktop applications should probably use HybridBinarizer instead. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public class GlobalHistogramBinarizer:Binarizer + { + override public BitMatrix BlackMatrix + { + // Does not sharpen the data, as this call is intended to only be used by 2D Readers. + + get + { + LuminanceSource source = LuminanceSource; + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // Added + // START + sbyte[] localLuminances; + //END + + int width = source.Width; + int height = source.Height; + BitMatrix matrix = new BitMatrix(width, height); + + // Quickly calculates the histogram by sampling four rows from the image. This proved to be + // more robust on the blackbox tests than sampling a diagonal as we used to do. + initArrays(width); + int[] localBuckets = buckets; + for (int y = 1; y < 5; y++) + { + int row = height * y / 5; + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // Commented & Added + // START + //sbyte[] localLuminances = source.getRow(row, luminances); + localLuminances = source.getRow(row, luminances); + // END + int right = (width << 2) / 5; + for (int x = width / 5; x < right; x++) + { + int pixel = localLuminances[x] & 0xff; + localBuckets[pixel >> LUMINANCE_SHIFT]++; + } + } + int blackPoint = estimateBlackPoint(localBuckets); + + // We delay reading the entire image luminance until the black point estimation succeeds. + // Although we end up reading four rows twice, it is consistent with our motto of + // "fail quickly" which is necessary for continuous scanning. + + localLuminances = source.Matrix; // Govinda : Removed sbyte [] + for (int y = 0; y < height; y++) + { + int offset = y * width; + for (int x = 0; x < width; x++) + { + int pixel = localLuminances[offset + x] & 0xff; + if (pixel < blackPoint) + { + matrix.set_Renamed(x, y); + } + } + } + + return matrix; + } + + } + + private const int LUMINANCE_BITS = 5; + //UPGRADE_NOTE: Final was removed from the declaration of 'LUMINANCE_SHIFT '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS; + //UPGRADE_NOTE: Final was removed from the declaration of 'LUMINANCE_BUCKETS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS; + + private sbyte[] luminances = null; + private int[] buckets = null; + + public GlobalHistogramBinarizer(LuminanceSource source):base(source) + { + } + + // Applies simple sharpening to the row data to improve performance of the 1D Readers. + public override BitArray getBlackRow(int y, BitArray row) + { + LuminanceSource source = LuminanceSource; + int width = source.Width; + if (row == null || row.Size < width) + { + row = new BitArray(width); + } + else + { + row.clear(); + } + + initArrays(width); + sbyte[] localLuminances = source.getRow(y, luminances); + int[] localBuckets = buckets; + for (int x = 0; x < width; x++) + { + int pixel = localLuminances[x] & 0xff; + localBuckets[pixel >> LUMINANCE_SHIFT]++; + } + int blackPoint = estimateBlackPoint(localBuckets); + + int left = localLuminances[0] & 0xff; + int center = localLuminances[1] & 0xff; + for (int x = 1; x < width - 1; x++) + { + int right = localLuminances[x + 1] & 0xff; + // A simple -1 4 -1 box filter with a weight of 2. + int luminance = ((center << 2) - left - right) >> 1; + if (luminance < blackPoint) + { + row.set_Renamed(x); + } + left = center; + center = right; + } + return row; + } + + public override Binarizer createBinarizer(LuminanceSource source) + { + return new GlobalHistogramBinarizer(source); + } + + private void initArrays(int luminanceSize) + { + if (luminances == null || luminances.Length < luminanceSize) + { + luminances = new sbyte[luminanceSize]; + } + if (buckets == null) + { + buckets = new int[LUMINANCE_BUCKETS]; + } + else + { + for (int x = 0; x < LUMINANCE_BUCKETS; x++) + { + buckets[x] = 0; + } + } + } + + private static int estimateBlackPoint(int[] buckets) + { + // Find the tallest peak in the histogram. + int numBuckets = buckets.Length; + int maxBucketCount = 0; + int firstPeak = 0; + int firstPeakSize = 0; + for (int x = 0; x < numBuckets; x++) + { + if (buckets[x] > firstPeakSize) + { + firstPeak = x; + firstPeakSize = buckets[x]; + } + if (buckets[x] > maxBucketCount) + { + maxBucketCount = buckets[x]; + } + } + + // Find the second-tallest peak which is somewhat far from the tallest peak. + int secondPeak = 0; + int secondPeakScore = 0; + for (int x = 0; x < numBuckets; x++) + { + int distanceToBiggest = x - firstPeak; + // Encourage more distant second peaks by multiplying by square of distance. + int score = buckets[x] * distanceToBiggest * distanceToBiggest; + if (score > secondPeakScore) + { + secondPeak = x; + secondPeakScore = score; + } + } + + // Make sure firstPeak corresponds to the black peak. + if (firstPeak > secondPeak) + { + int temp = firstPeak; + firstPeak = secondPeak; + secondPeak = temp; + } + + // If there is too little contrast in the image to pick a meaningful black point, throw rather + // than waste time trying to decode the image, and risk false positives. + // TODO: It might be worth comparing the brightest and darkest pixels seen, rather than the + // two peaks, to determine the contrast. + if (secondPeak - firstPeak <= numBuckets >> 4) + { + throw ReaderException.Instance; + } + + // Find a valley between them that is low and closer to the white peak. + int bestValley = secondPeak - 1; + int bestValleyScore = - 1; + for (int x = secondPeak - 1; x > firstPeak; x--) + { + int fromFirst = x - firstPeak; + int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]); + if (score > bestValleyScore) + { + bestValley = x; + bestValleyScore = score; + } + } + + return bestValley << LUMINANCE_SHIFT; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/GridSampler.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/GridSampler.cs new file mode 100644 index 0000000..bec46b7 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/GridSampler.cs @@ -0,0 +1,193 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +namespace com.google.zxing.common +{ + + /// Implementations of this class can, given locations of finder patterns for a QR code in an + /// image, sample the right points in the image to reconstruct the QR code, accounting for + /// perspective distortion. It is abstracted since it is relatively expensive and should be allowed + /// to take advantage of platform-specific optimized implementations, like Sun's Java Advanced + /// Imaging library, but which may not be available in other environments such as J2ME, and vice + /// versa. + /// + /// The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)} + /// with an instance of a class which implements this interface. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class GridSampler + { + /// the current implementation of {@link GridSampler} + /// + public static GridSampler Instance + { + get + { + return gridSampler; + } + + } + + private static GridSampler gridSampler = new DefaultGridSampler(); + + /// Sets the implementation of {@link GridSampler} used by the library. One global + /// instance is stored, which may sound problematic. But, the implementation provided + /// ought to be appropriate for the entire platform, and all uses of this library + /// in the whole lifetime of the JVM. For instance, an Android activity can swap in + /// an implementation that takes advantage of native platform libraries. + /// + /// + /// The platform-specific object to install. + /// + public static void setGridSampler(GridSampler newGridSampler) + { + if (newGridSampler == null) + { + throw new System.ArgumentException(); + } + gridSampler = newGridSampler; + } + + ///

Samples an image for a square matrix of bits of the given dimension. This is used to extract + /// the black/white modules of a 2D barcode like a QR Code found in an image. Because this barcode + /// may be rotated or perspective-distorted, the caller supplies four points in the source image + /// that define known points in the barcode, so that the image may be sampled appropriately.

+ /// + ///

The last eight "from" parameters are four X/Y coordinate pairs of locations of points in + /// the image that define some significant points in the image to be sample. For example, + /// these may be the location of finder pattern in a QR Code.

+ /// + ///

The first eight "to" parameters are four X/Y coordinate pairs measured in the destination + /// {@link BitMatrix}, from the top left, where the known points in the image given by the "from" + /// parameters map to.

+ /// + ///

These 16 parameters define the transformation needed to sample the image.

+ /// + ///
+ /// image to sample + /// + /// width/height of {@link BitMatrix} to sample from image + /// + /// {@link BitMatrix} representing a grid of points sampled from the image within a region + /// defined by the "from" parameters + /// + /// ReaderException if image can't be sampled, for example, if the transformation defined + /// by the given points is invalid or results in sampling outside the image boundaries + /// + public abstract BitMatrix sampleGrid(BitMatrix image, int dimension, float p1ToX, float p1ToY, float p2ToX, float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX, float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY); + + public virtual BitMatrix sampleGrid(BitMatrix image, int dimension, PerspectiveTransform transform) + { + throw new System.NotSupportedException(); + } + + + ///

Checks a set of points that have been transformed to sample points on an image against + /// the image's dimensions to see if the point are even within the image.

+ /// + ///

This method will actually "nudge" the endpoints back onto the image if they are found to be + /// barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder + /// patterns in an image where the QR Code runs all the way to the image border.

+ /// + ///

For efficiency, the method will check points from either end of the line until one is found + /// to be within the image. Because the set of points are assumed to be linear, this is valid.

+ /// + ///
+ /// image into which the points should map + /// + /// actual points in x1,y1,...,xn,yn form + /// + /// ReaderException if an endpoint is lies outside the image boundaries + protected internal static void checkAndNudgePoints(BitMatrix image, float[] points) + { + int width = image.Width; + int height = image.Height; + // Check and nudge points from start until we see some that are OK: + bool nudged = true; + for (int offset = 0; offset < points.Length && nudged; offset += 2) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int x = (int) points[offset]; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int y = (int) points[offset + 1]; + if (x < - 1 || x > width || y < - 1 || y > height) + { + throw ReaderException.Instance; + } + nudged = false; + if (x == - 1) + { + points[offset] = 0.0f; + nudged = true; + } + else if (x == width) + { + points[offset] = width - 1; + nudged = true; + } + if (y == - 1) + { + points[offset + 1] = 0.0f; + nudged = true; + } + else if (y == height) + { + points[offset + 1] = height - 1; + nudged = true; + } + } + // Check and nudge points from end: + nudged = true; + for (int offset = points.Length - 2; offset >= 0 && nudged; offset -= 2) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int x = (int) points[offset]; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int y = (int) points[offset + 1]; + if (x < - 1 || x > width || y < - 1 || y > height) + { + throw ReaderException.Instance; + } + nudged = false; + if (x == - 1) + { + points[offset] = 0.0f; + nudged = true; + } + else if (x == width) + { + points[offset] = width - 1; + nudged = true; + } + if (y == - 1) + { + points[offset + 1] = 0.0f; + nudged = true; + } + else if (y == height) + { + points[offset + 1] = height - 1; + nudged = true; + } + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/HybridBinarizer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/HybridBinarizer.cs new file mode 100644 index 0000000..19d8fa3 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/HybridBinarizer.cs @@ -0,0 +1,187 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using Binarizer = com.google.zxing.Binarizer; +using LuminanceSource = com.google.zxing.LuminanceSource; +using ReaderException = com.google.zxing.ReaderException; +namespace com.google.zxing.common +{ + + /// This class implements a local thresholding algorithm, which while slower than the + /// GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for + /// high frequency images of barcodes with black data on white backgrounds. For this application, + /// it does a much better job than a global blackpoint with severe shadows and gradients. + /// However it tends to produce artifacts on lower frequency images and is therefore not + /// a good general purpose binarizer for uses outside ZXing. + /// + /// This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers, + /// and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already + /// inherently local, and only fails for horizontal gradients. We can revisit that problem later, + /// but for now it was not a win to use local blocks for 1D. + /// + /// This Binarizer is the default for the unit tests and the recommended class for library users. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class HybridBinarizer:GlobalHistogramBinarizer + { + override public BitMatrix BlackMatrix + { + get + { + binarizeEntireImage(); + return matrix; + } + + } + + // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels. + // So this is the smallest dimension in each axis we can accept. + private const int MINIMUM_DIMENSION = 40; + + private BitMatrix matrix = null; + + public HybridBinarizer(LuminanceSource source):base(source) + { + } + + public override Binarizer createBinarizer(LuminanceSource source) + { + return new HybridBinarizer(source); + } + + // Calculates the final BitMatrix once for all requests. This could be called once from the + // constructor instead, but there are some advantages to doing it lazily, such as making + // profiling easier, and not doing heavy lifting when callers don't expect it. + private void binarizeEntireImage() + { + if (matrix == null) + { + LuminanceSource source = LuminanceSource; + if (source.Width >= MINIMUM_DIMENSION && source.Height >= MINIMUM_DIMENSION) + { + sbyte[] luminances = source.Matrix; + int width = source.Width; + int height = source.Height; + int subWidth = width >> 3; + int subHeight = height >> 3; + int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width); + + matrix = new BitMatrix(width, height); + calculateThresholdForBlock(luminances, subWidth, subHeight, width, blackPoints, matrix); + } + else + { + // If the image is too small, fall back to the global histogram approach. + matrix = base.BlackMatrix; + } + } + } + + // For each 8x8 block in the image, calculate the average black point using a 5x5 grid + // of the blocks around it. Also handles the corner cases, but will ignore up to 7 pixels + // on the right edge and 7 pixels at the bottom of the image if the overall dimensions are not + // multiples of eight. In practice, leaving those pixels white does not seem to be a problem. + private static void calculateThresholdForBlock(sbyte[] luminances, int subWidth, int subHeight, int stride, int[][] blackPoints, BitMatrix matrix) + { + for (int y = 0; y < subHeight; y++) + { + for (int x = 0; x < subWidth; x++) + { + int left = (x > 1)?x:2; + left = (left < subWidth - 2)?left:subWidth - 3; + int top = (y > 1)?y:2; + top = (top < subHeight - 2)?top:subHeight - 3; + int sum = 0; + for (int z = - 2; z <= 2; z++) + { + int[] blackRow = blackPoints[top + z]; + sum += blackRow[left - 2]; + sum += blackRow[left - 1]; + sum += blackRow[left]; + sum += blackRow[left + 1]; + sum += blackRow[left + 2]; + } + int average = sum / 25; + threshold8x8Block(luminances, x << 3, y << 3, average, stride, matrix); + } + } + } + + // Applies a single threshold to an 8x8 block of pixels. + private static void threshold8x8Block(sbyte[] luminances, int xoffset, int yoffset, int threshold, int stride, BitMatrix matrix) + { + for (int y = 0; y < 8; y++) + { + int offset = (yoffset + y) * stride + xoffset; + for (int x = 0; x < 8; x++) + { + int pixel = luminances[offset + x] & 0xff; + if (pixel < threshold) + { + matrix.set_Renamed(xoffset + x, yoffset + y); + } + } + } + } + + // Calculates a single black point for each 8x8 block of pixels and saves it away. + private static int[][] calculateBlackPoints(sbyte[] luminances, int subWidth, int subHeight, int stride) + { + int[][] blackPoints = new int[subHeight][]; + for (int i = 0; i < subHeight; i++) + { + blackPoints[i] = new int[subWidth]; + } + for (int y = 0; y < subHeight; y++) + { + for (int x = 0; x < subWidth; x++) + { + int sum = 0; + int min = 255; + int max = 0; + for (int yy = 0; yy < 8; yy++) + { + int offset = ((y << 3) + yy) * stride + (x << 3); + for (int xx = 0; xx < 8; xx++) + { + int pixel = luminances[offset + xx] & 0xff; + sum += pixel; + if (pixel < min) + { + min = pixel; + } + if (pixel > max) + { + max = pixel; + } + } + } + + // If the contrast is inadequate, use half the minimum, so that this block will be + // treated as part of the white background, but won't drag down neighboring blocks + // too much. + int average = (max - min > 24)?(sum >> 6):(min >> 1); + blackPoints[y][x] = average; + } + } + return blackPoints; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/PerspectiveTransform.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/PerspectiveTransform.cs new file mode 100644 index 0000000..b516ef7 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/PerspectiveTransform.cs @@ -0,0 +1,146 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common +{ + + ///

This class implements a perspective transform in two dimensions. Given four source and four + /// destination points, it will compute the transformation implied between them. The code is based + /// directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class PerspectiveTransform + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'a11 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a12 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a13 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a21 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a22 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a23 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a31 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a32 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: Final was removed from the declaration of 'a33 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private float a11; + private float a12; + private float a13; + private float a21; + private float a22; + private float a23; + private float a31; + private float a32; + private float a33; + + private PerspectiveTransform(float a11, float a21, float a31, float a12, float a22, float a32, float a13, float a23, float a33) + { + this.a11 = a11; + this.a12 = a12; + this.a13 = a13; + this.a21 = a21; + this.a22 = a22; + this.a23 = a23; + this.a31 = a31; + this.a32 = a32; + this.a33 = a33; + } + + public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p) + { + + PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); + PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); + return sToQ.times(qToS); + } + + public void transformPoints(float[] points) + { + int max = points.Length; + float a11 = this.a11; + float a12 = this.a12; + float a13 = this.a13; + float a21 = this.a21; + float a22 = this.a22; + float a23 = this.a23; + float a31 = this.a31; + float a32 = this.a32; + float a33 = this.a33; + for (int i = 0; i < max; i += 2) + { + float x = points[i]; + float y = points[i + 1]; + float denominator = a13 * x + a23 * y + a33; + points[i] = (a11 * x + a21 * y + a31) / denominator; + points[i + 1] = (a12 * x + a22 * y + a32) / denominator; + } + } + + /// Convenience method, not optimized for performance. + public void transformPoints(float[] xValues, float[] yValues) + { + int n = xValues.Length; + for (int i = 0; i < n; i++) + { + float x = xValues[i]; + float y = yValues[i]; + float denominator = a13 * x + a23 * y + a33; + xValues[i] = (a11 * x + a21 * y + a31) / denominator; + yValues[i] = (a12 * x + a22 * y + a32) / denominator; + } + } + + public static PerspectiveTransform squareToQuadrilateral(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) + { + float dy2 = y3 - y2; + float dy3 = y0 - y1 + y2 - y3; + if (dy2 == 0.0f && dy3 == 0.0f) + { + return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f, 0.0f, 1.0f); + } + else + { + float dx1 = x1 - x2; + float dx2 = x3 - x2; + float dx3 = x0 - x1 + x2 - x3; + float dy1 = y1 - y2; + float denominator = dx1 * dy2 - dx2 * dy1; + float a13 = (dx3 * dy2 - dx2 * dy3) / denominator; + float a23 = (dx1 * dy3 - dx3 * dy1) / denominator; + return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0f); + } + } + + public static PerspectiveTransform quadrilateralToSquare(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) + { + // Here, the adjoint serves as the inverse: + return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint(); + } + + internal PerspectiveTransform buildAdjoint() + { + // Adjoint is the transpose of the cofactor matrix: + return new PerspectiveTransform(a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32 - a22 * a31, a13 * a32 - a12 * a33, a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22, a13 * a21 - a11 * a23, a11 * a22 - a12 * a21); + } + + internal PerspectiveTransform times(PerspectiveTransform other) + { + return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13, a11 * other.a21 + a21 * other.a22 + a31 * other.a23, a11 * other.a31 + a21 * other.a32 + a31 * other.a33, a12 * other.a11 + a22 * other.a12 + a32 * other.a13, a12 * other.a21 + a22 * other.a22 + a32 * other.a23, a12 * other.a31 + a22 * other.a32 + a32 * other.a33, a13 * other.a11 + a23 * other.a12 + a33 * other.a13, a13 * other.a21 + a23 * other.a22 + a33 * other.a23, a13 * other.a31 + a23 * other.a32 + a33 * other.a33); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/detector/MonochromeRectangleDetector.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/detector/MonochromeRectangleDetector.cs new file mode 100644 index 0000000..eea5cb1 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/detector/MonochromeRectangleDetector.cs @@ -0,0 +1,255 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.common.detector +{ + + ///

A somewhat generic detector that looks for a barcode-like rectangular region within an image. + /// It looks within a mostly white region of an image for a region of black and white, but mostly + /// black. It returns the four corners of the region, as best it can determine.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class MonochromeRectangleDetector + { + + private const int MAX_MODULES = 32; + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix image; + + public MonochromeRectangleDetector(BitMatrix image) + { + this.image = image; + } + + ///

Detects a rectangular region of black and white -- mostly black -- with a region of mostly + /// white, in an image.

+ /// + ///
+ /// {@link ResultPoint}[] describing the corners of the rectangular region. The first and + /// last points are opposed on the diagonal, as are the second and third. The first point will be + /// the topmost point and the last, the bottommost. The second point will be leftmost and the + /// third, the rightmost + /// + /// ReaderException if no Data Matrix Code can be found + public ResultPoint[] detect() + { + int height = image.Height; + int width = image.Width; + int halfHeight = height >> 1; + int halfWidth = width >> 1; + int deltaY = System.Math.Max(1, height / (MAX_MODULES << 3)); + int deltaX = System.Math.Max(1, width / (MAX_MODULES << 3)); + + int top = 0; + int bottom = height; + int left = 0; + int right = width; + ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 1); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + top = (int) pointA.Y - 1; + ResultPoint pointB = findCornerFromCenter(halfWidth, - deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + left = (int) pointB.X - 1; + ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, halfHeight, 0, top, bottom, halfHeight >> 1); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + right = (int) pointC.X + 1; + ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, deltaY, top, bottom, halfWidth >> 1); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + bottom = (int) pointD.Y + 1; + + // Go try to find point A again with better information -- might have been off at first. + pointA = findCornerFromCenter(halfWidth, 0, left, right, halfHeight, - deltaY, top, bottom, halfWidth >> 2); + + return new ResultPoint[]{pointA, pointB, pointC, pointD}; + } + + /// Attempts to locate a corner of the barcode by scanning up, down, left or right from a center + /// point which should be within the barcode. + /// + /// + /// center's x component (horizontal) + /// + /// same as deltaY but change in x per step instead + /// + /// minimum value of x + /// + /// maximum value of x + /// + /// center's y component (vertical) + /// + /// change in y per step. If scanning up this is negative; down, positive; + /// left or right, 0 + /// + /// minimum value of y to search through (meaningless when di == 0) + /// + /// maximum value of y + /// + /// maximum run of white pixels that can still be considered to be within + /// the barcode + /// + /// a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found + /// + /// com.google.zxing.ReaderException if such a point cannot be found + private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right, int centerY, int deltaY, int top, int bottom, int maxWhiteRun) + { + int[] lastRange = null; + for (int y = centerY, x = centerX; y < bottom && y >= top && x < right && x >= left; y += deltaY, x += deltaX) + { + int[] range; + if (deltaX == 0) + { + // horizontal slices, up and down + range = blackWhiteRange(y, maxWhiteRun, left, right, true); + } + else + { + // vertical slices, left and right + range = blackWhiteRange(x, maxWhiteRun, top, bottom, false); + } + if (range == null) + { + if (lastRange == null) + { + throw ReaderException.Instance; + } + // lastRange was found + if (deltaX == 0) + { + int lastY = y - deltaY; + if (lastRange[0] < centerX) + { + if (lastRange[1] > centerX) + { + // straddle, choose one or the other based on direction + return new ResultPoint(deltaY > 0?lastRange[0]:lastRange[1], lastY); + } + return new ResultPoint(lastRange[0], lastY); + } + else + { + return new ResultPoint(lastRange[1], lastY); + } + } + else + { + int lastX = x - deltaX; + if (lastRange[0] < centerY) + { + if (lastRange[1] > centerY) + { + return new ResultPoint(lastX, deltaX < 0?lastRange[0]:lastRange[1]); + } + return new ResultPoint(lastX, lastRange[0]); + } + else + { + return new ResultPoint(lastX, lastRange[1]); + } + } + } + lastRange = range; + } + throw ReaderException.Instance; + } + + /// Computes the start and end of a region of pixels, either horizontally or vertically, that could + /// be part of a Data Matrix barcode. + /// + /// + /// if scanning horizontally, this is the row (the fixed vertical location) + /// where we are scanning. If scanning vertically it's the column, the fixed horizontal location + /// + /// largest run of white pixels that can still be considered part of the + /// barcode region + /// + /// minimum pixel location, horizontally or vertically, to consider + /// + /// maximum pixel location, horizontally or vertically, to consider + /// + /// if true, we're scanning left-right, instead of up-down + /// + /// int[] with start and end of found range, or null if no such range is found + /// (e.g. only white was found) + /// + private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, bool horizontal) + { + + int center = (minDim + maxDim) >> 1; + + // Scan left/up first + int start = center; + while (start >= minDim) + { + if (horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start)) + { + start--; + } + else + { + int whiteRunStart = start; + do + { + start--; + } + while (start >= minDim && !(horizontal?image.get_Renamed(start, fixedDimension):image.get_Renamed(fixedDimension, start))); + int whiteRunSize = whiteRunStart - start; + if (start < minDim || whiteRunSize > maxWhiteRun) + { + start = whiteRunStart; + break; + } + } + } + start++; + + // Then try right/down + int end = center; + while (end < maxDim) + { + if (horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end)) + { + end++; + } + else + { + int whiteRunStart = end; + do + { + end++; + } + while (end < maxDim && !(horizontal?image.get_Renamed(end, fixedDimension):image.get_Renamed(fixedDimension, end))); + int whiteRunSize = end - whiteRunStart; + if (end >= maxDim || whiteRunSize > maxWhiteRun) + { + end = whiteRunStart; + break; + } + } + } + end--; + + return end > start?new int[]{start, end}:null; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/GF256.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/GF256.cs new file mode 100644 index 0000000..d006633 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/GF256.cs @@ -0,0 +1,174 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common.reedsolomon +{ + + ///

This class contains utility methods for performing mathematical operations over + /// the Galois Field GF(256). Operations use a given primitive polynomial in calculations.

+ /// + ///

Throughout this package, elements of GF(256) are represented as an int + /// for convenience and speed (but at the cost of memory). + /// Only the bottom 8 bits are really used.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class GF256 + { + internal GF256Poly Zero + { + get + { + return zero; + } + + } + internal GF256Poly One + { + get + { + return one; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'QR_CODE_FIELD '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly GF256 QR_CODE_FIELD = new GF256(0x011D); // x^8 + x^4 + x^3 + x^2 + 1 + //UPGRADE_NOTE: Final was removed from the declaration of 'DATA_MATRIX_FIELD '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly GF256 DATA_MATRIX_FIELD = new GF256(0x012D); // x^8 + x^5 + x^3 + x^2 + 1 + + //UPGRADE_NOTE: Final was removed from the declaration of 'expTable '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] expTable; + //UPGRADE_NOTE: Final was removed from the declaration of 'logTable '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] logTable; + //UPGRADE_NOTE: Final was removed from the declaration of 'zero '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private GF256Poly zero; + //UPGRADE_NOTE: Final was removed from the declaration of 'one '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private GF256Poly one; + + /// Create a representation of GF(256) using the given primitive polynomial. + /// + /// + /// irreducible polynomial whose coefficients are represented by + /// the bits of an int, where the least-significant bit represents the constant + /// coefficient + /// + private GF256(int primitive) + { + expTable = new int[256]; + logTable = new int[256]; + int x = 1; + for (int i = 0; i < 256; i++) + { + expTable[i] = x; + x <<= 1; // x = x * 2; we're assuming the generator alpha is 2 + if (x >= 0x100) + { + x ^= primitive; + } + } + for (int i = 0; i < 255; i++) + { + logTable[expTable[i]] = i; + } + // logTable[0] == 0 but this should never be used + zero = new GF256Poly(this, new int[]{0}); + one = new GF256Poly(this, new int[]{1}); + } + + /// the monomial representing coefficient * x^degree + /// + internal GF256Poly buildMonomial(int degree, int coefficient) + { + if (degree < 0) + { + throw new System.ArgumentException(); + } + if (coefficient == 0) + { + return zero; + } + int[] coefficients = new int[degree + 1]; + coefficients[0] = coefficient; + return new GF256Poly(this, coefficients); + } + + /// Implements both addition and subtraction -- they are the same in GF(256). + /// + /// + /// sum/difference of a and b + /// + internal static int addOrSubtract(int a, int b) + { + return a ^ b; + } + + /// 2 to the power of a in GF(256) + /// + internal int exp(int a) + { + return expTable[a]; + } + + /// base 2 log of a in GF(256) + /// + internal int log(int a) + { + if (a == 0) + { + throw new System.ArgumentException(); + } + return logTable[a]; + } + + /// multiplicative inverse of a + /// + internal int inverse(int a) + { + if (a == 0) + { + throw new System.ArithmeticException(); + } + return expTable[255 - logTable[a]]; + } + + /// + /// + /// + /// + /// product of a and b in GF(256) + /// + internal int multiply(int a, int b) + { + if (a == 0 || b == 0) + { + return 0; + } + if (a == 1) + { + return b; + } + if (b == 1) + { + return a; + } + return expTable[(logTable[a] + logTable[b]) % 255]; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/GF256Poly.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/GF256Poly.cs new file mode 100644 index 0000000..df5abb3 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/GF256Poly.cs @@ -0,0 +1,328 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common.reedsolomon +{ + + ///

Represents a polynomial whose coefficients are elements of GF(256). + /// Instances of this class are immutable.

+ /// + ///

Much credit is due to William Rucklidge since portions of this code are an indirect + /// port of his C++ Reed-Solomon implementation.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class GF256Poly + { + internal int[] Coefficients + { + get + { + return coefficients; + } + + } + /// degree of this polynomial + /// + internal int Degree + { + get + { + return coefficients.Length - 1; + } + + } + /// true iff this polynomial is the monomial "0" + /// + internal bool Zero + { + get + { + return coefficients[0] == 0; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'field '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private GF256 field; + //UPGRADE_NOTE: Final was removed from the declaration of 'coefficients '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] coefficients; + + /// the {@link GF256} instance representing the field to use + /// to perform computations + /// + /// coefficients as ints representing elements of GF(256), arranged + /// from most significant (highest-power term) coefficient to least significant + /// + /// IllegalArgumentException if argument is null or empty, + /// or if leading coefficient is 0 and this is not a + /// constant polynomial (that is, it is not the monomial "0") + /// + internal GF256Poly(GF256 field, int[] coefficients) + { + if (coefficients == null || coefficients.Length == 0) + { + throw new System.ArgumentException(); + } + this.field = field; + int coefficientsLength = coefficients.Length; + if (coefficientsLength > 1 && coefficients[0] == 0) + { + // Leading term must be non-zero for anything except the constant polynomial "0" + int firstNonZero = 1; + while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) + { + firstNonZero++; + } + if (firstNonZero == coefficientsLength) + { + this.coefficients = field.Zero.coefficients; + } + else + { + this.coefficients = new int[coefficientsLength - firstNonZero]; + Array.Copy(coefficients, firstNonZero, this.coefficients, 0, this.coefficients.Length); + } + } + else + { + this.coefficients = coefficients; + } + } + + /// coefficient of x^degree term in this polynomial + /// + internal int getCoefficient(int degree) + { + return coefficients[coefficients.Length - 1 - degree]; + } + + /// evaluation of this polynomial at a given point + /// + internal int evaluateAt(int a) + { + if (a == 0) + { + // Just return the x^0 coefficient + return getCoefficient(0); + } + int size = coefficients.Length; + if (a == 1) + { + // Just the sum of the coefficients + int result = 0; + for (int i = 0; i < size; i++) + { + result = GF256.addOrSubtract(result, coefficients[i]); + } + return result; + } + int result2 = coefficients[0]; + for (int i = 1; i < size; i++) + { + result2 = GF256.addOrSubtract(field.multiply(a, result2), coefficients[i]); + } + return result2; + } + + internal GF256Poly addOrSubtract(GF256Poly other) + { + if (!field.Equals(other.field)) + { + throw new System.ArgumentException("GF256Polys do not have same GF256 field"); + } + if (Zero) + { + return other; + } + if (other.Zero) + { + return this; + } + + int[] smallerCoefficients = this.coefficients; + int[] largerCoefficients = other.coefficients; + if (smallerCoefficients.Length > largerCoefficients.Length) + { + int[] temp = smallerCoefficients; + smallerCoefficients = largerCoefficients; + largerCoefficients = temp; + } + int[] sumDiff = new int[largerCoefficients.Length]; + int lengthDiff = largerCoefficients.Length - smallerCoefficients.Length; + // Copy high-order terms only found in higher-degree polynomial's coefficients + Array.Copy(largerCoefficients, 0, sumDiff, 0, lengthDiff); + + for (int i = lengthDiff; i < largerCoefficients.Length; i++) + { + sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); + } + + return new GF256Poly(field, sumDiff); + } + + internal GF256Poly multiply(GF256Poly other) + { + if (!field.Equals(other.field)) + { + throw new System.ArgumentException("GF256Polys do not have same GF256 field"); + } + if (Zero || other.Zero) + { + return field.Zero; + } + int[] aCoefficients = this.coefficients; + int aLength = aCoefficients.Length; + int[] bCoefficients = other.coefficients; + int bLength = bCoefficients.Length; + int[] product = new int[aLength + bLength - 1]; + for (int i = 0; i < aLength; i++) + { + int aCoeff = aCoefficients[i]; + for (int j = 0; j < bLength; j++) + { + product[i + j] = GF256.addOrSubtract(product[i + j], field.multiply(aCoeff, bCoefficients[j])); + } + } + return new GF256Poly(field, product); + } + + internal GF256Poly multiply(int scalar) + { + if (scalar == 0) + { + return field.Zero; + } + if (scalar == 1) + { + return this; + } + int size = coefficients.Length; + int[] product = new int[size]; + for (int i = 0; i < size; i++) + { + product[i] = field.multiply(coefficients[i], scalar); + } + return new GF256Poly(field, product); + } + + internal GF256Poly multiplyByMonomial(int degree, int coefficient) + { + if (degree < 0) + { + throw new System.ArgumentException(); + } + if (coefficient == 0) + { + return field.Zero; + } + int size = coefficients.Length; + int[] product = new int[size + degree]; + for (int i = 0; i < size; i++) + { + product[i] = field.multiply(coefficients[i], coefficient); + } + return new GF256Poly(field, product); + } + + internal GF256Poly[] divide(GF256Poly other) + { + if (!field.Equals(other.field)) + { + throw new System.ArgumentException("GF256Polys do not have same GF256 field"); + } + if (other.Zero) + { + throw new System.ArgumentException("Divide by 0"); + } + + GF256Poly quotient = field.Zero; + GF256Poly remainder = this; + + int denominatorLeadingTerm = other.getCoefficient(other.Degree); + int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); + + while (remainder.Degree >= other.Degree && !remainder.Zero) + { + int degreeDifference = remainder.Degree - other.Degree; + int scale = field.multiply(remainder.getCoefficient(remainder.Degree), inverseDenominatorLeadingTerm); + GF256Poly term = other.multiplyByMonomial(degreeDifference, scale); + GF256Poly iterationQuotient = field.buildMonomial(degreeDifference, scale); + quotient = quotient.addOrSubtract(iterationQuotient); + remainder = remainder.addOrSubtract(term); + } + + return new GF256Poly[]{quotient, remainder}; + } + + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(8 * Degree); + for (int degree = Degree; degree >= 0; degree--) + { + int coefficient = getCoefficient(degree); + if (coefficient != 0) + { + if (coefficient < 0) + { + result.Append(" - "); + coefficient = - coefficient; + } + else + { + if (result.Length > 0) + { + result.Append(" + "); + } + } + if (degree == 0 || coefficient != 1) + { + int alphaPower = field.log(coefficient); + if (alphaPower == 0) + { + result.Append('1'); + } + else if (alphaPower == 1) + { + result.Append('a'); + } + else + { + result.Append("a^"); + result.Append(alphaPower); + } + } + if (degree != 0) + { + if (degree == 1) + { + result.Append('x'); + } + else + { + result.Append("x^"); + result.Append(degree); + } + } + } + } + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonDecoder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonDecoder.cs new file mode 100644 index 0000000..1812311 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonDecoder.cs @@ -0,0 +1,217 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common.reedsolomon +{ + + ///

Implements Reed-Solomon decoding, as the name implies.

+ /// + ///

The algorithm will not be explained here, but the following references were helpful + /// in creating this implementation:

+ /// + /// + /// + ///

Much credit is due to William Rucklidge since portions of this code are an indirect + /// port of his C++ Reed-Solomon implementation.

+ /// + ///
+ /// Sean Owen + /// + /// William Rucklidge + /// + /// sanfordsquires + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ReedSolomonDecoder + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'field '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private GF256 field; + + public ReedSolomonDecoder(GF256 field) + { + this.field = field; + } + + ///

Decodes given set of received codewords, which include both data and error-correction + /// codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, + /// in the input.

+ /// + ///
+ /// data and error-correction codewords + /// + /// number of error-correction codewords available + /// + /// ReedSolomonException if decoding fails for any reason + public void decode(int[] received, int twoS) + { + GF256Poly poly = new GF256Poly(field, received); + int[] syndromeCoefficients = new int[twoS]; + bool dataMatrix = field.Equals(GF256.DATA_MATRIX_FIELD); + bool noError = true; + for (int i = 0; i < twoS; i++) + { + // Thanks to sanfordsquires for this fix: + int eval = poly.evaluateAt(field.exp(dataMatrix?i + 1:i)); + syndromeCoefficients[syndromeCoefficients.Length - 1 - i] = eval; + if (eval != 0) + { + noError = false; + } + } + if (noError) + { + return ; + } + GF256Poly syndrome = new GF256Poly(field, syndromeCoefficients); + GF256Poly[] sigmaOmega = runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); + GF256Poly sigma = sigmaOmega[0]; + GF256Poly omega = sigmaOmega[1]; + int[] errorLocations = findErrorLocations(sigma); + int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix); + for (int i = 0; i < errorLocations.Length; i++) + { + int position = received.Length - 1 - field.log(errorLocations[i]); + if (position < 0) + { + throw new ReedSolomonException("Bad error location"); + } + received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]); + } + } + + private GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R) + { + // Assume a's degree is >= b's + if (a.Degree < b.Degree) + { + GF256Poly temp = a; + a = b; + b = temp; + } + + GF256Poly rLast = a; + GF256Poly r = b; + GF256Poly sLast = field.One; + GF256Poly s = field.Zero; + GF256Poly tLast = field.Zero; + GF256Poly t = field.One; + + // Run Euclidean algorithm until r's degree is less than R/2 + while (r.Degree >= R / 2) + { + GF256Poly rLastLast = rLast; + GF256Poly sLastLast = sLast; + GF256Poly tLastLast = tLast; + rLast = r; + sLast = s; + tLast = t; + + // Divide rLastLast by rLast, with quotient in q and remainder in r + if (rLast.Zero) + { + // Oops, Euclidean algorithm already terminated? + throw new ReedSolomonException("r_{i-1} was zero"); + } + r = rLastLast; + GF256Poly q = field.Zero; + int denominatorLeadingTerm = rLast.getCoefficient(rLast.Degree); + int dltInverse = field.inverse(denominatorLeadingTerm); + while (r.Degree >= rLast.Degree && !r.Zero) + { + int degreeDiff = r.Degree - rLast.Degree; + int scale = field.multiply(r.getCoefficient(r.Degree), dltInverse); + q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); + r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); + } + + s = q.multiply(sLast).addOrSubtract(sLastLast); + t = q.multiply(tLast).addOrSubtract(tLastLast); + } + + int sigmaTildeAtZero = t.getCoefficient(0); + if (sigmaTildeAtZero == 0) + { + throw new ReedSolomonException("sigmaTilde(0) was zero"); + } + + int inverse = field.inverse(sigmaTildeAtZero); + GF256Poly sigma = t.multiply(inverse); + GF256Poly omega = r.multiply(inverse); + return new GF256Poly[]{sigma, omega}; + } + + private int[] findErrorLocations(GF256Poly errorLocator) + { + // This is a direct application of Chien's search + int numErrors = errorLocator.Degree; + if (numErrors == 1) + { + // shortcut + return new int[]{errorLocator.getCoefficient(1)}; + } + int[] result = new int[numErrors]; + int e = 0; + for (int i = 1; i < 256 && e < numErrors; i++) + { + if (errorLocator.evaluateAt(i) == 0) + { + result[e] = field.inverse(i); + e++; + } + } + if (e != numErrors) + { + throw new ReedSolomonException("Error locator degree does not match number of roots"); + } + return result; + } + + private int[] findErrorMagnitudes(GF256Poly errorEvaluator, int[] errorLocations, bool dataMatrix) + { + // This is directly applying Forney's Formula + int s = errorLocations.Length; + int[] result = new int[s]; + for (int i = 0; i < s; i++) + { + int xiInverse = field.inverse(errorLocations[i]); + int denominator = 1; + for (int j = 0; j < s; j++) + { + if (i != j) + { + denominator = field.multiply(denominator, GF256.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))); + } + } + result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), field.inverse(denominator)); + // Thanks to sanfordsquires for this fix: + if (dataMatrix) + { + result[i] = field.multiply(result[i], xiInverse); + } + } + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonEncoder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonEncoder.cs new file mode 100644 index 0000000..890328e --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonEncoder.cs @@ -0,0 +1,91 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common.reedsolomon +{ + + ///

Implements Reed-Solomon enbcoding, as the name implies.

+ /// + ///
+ /// Sean Owen + /// + /// William Rucklidge + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ReedSolomonEncoder + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'field '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private GF256 field; + //UPGRADE_NOTE: Final was removed from the declaration of 'cachedGenerators '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + // private System.Collections.ArrayList cachedGenerators; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.List cachedGenerators; // added by .net follower (http://dotnetfollower.com) + + public ReedSolomonEncoder(GF256 field) + { + if (!GF256.QR_CODE_FIELD.Equals(field)) + { + throw new System.ArgumentException("Only QR Code is supported at this time"); + } + this.field = field; + // this.cachedGenerators = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + this.cachedGenerators = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + cachedGenerators.Add(new GF256Poly(field, new int[]{1})); + } + + private GF256Poly buildGenerator(int degree) + { + if (degree >= cachedGenerators.Count) + { + GF256Poly lastGenerator = (GF256Poly) cachedGenerators[cachedGenerators.Count - 1]; + for (int d = cachedGenerators.Count; d <= degree; d++) + { + GF256Poly nextGenerator = lastGenerator.multiply(new GF256Poly(field, new int[]{1, field.exp(d - 1)})); + cachedGenerators.Add(nextGenerator); + lastGenerator = nextGenerator; + } + } + return (GF256Poly) cachedGenerators[degree]; + } + + public void encode(int[] toEncode, int ecBytes) + { + if (ecBytes == 0) + { + throw new System.ArgumentException("No error correction bytes"); + } + int dataBytes = toEncode.Length - ecBytes; + if (dataBytes <= 0) + { + throw new System.ArgumentException("No data bytes provided"); + } + GF256Poly generator = buildGenerator(ecBytes); + int[] infoCoefficients = new int[dataBytes]; + Array.Copy(toEncode, 0, infoCoefficients, 0, dataBytes); + GF256Poly info = new GF256Poly(field, infoCoefficients); + info = info.multiplyByMonomial(ecBytes, 1); + GF256Poly remainder = info.divide(generator)[1]; + int[] coefficients = remainder.Coefficients; + int numZeroCoefficients = ecBytes - coefficients.Length; + for (int i = 0; i < numZeroCoefficients; i++) + { + toEncode[dataBytes + i] = 0; + } + Array.Copy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.Length); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonException.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonException.cs new file mode 100644 index 0000000..ed7068b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/common/reedsolomon/ReedSolomonException.cs @@ -0,0 +1,36 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.common.reedsolomon +{ + + ///

Thrown when an exception occurs during Reed-Solomon decoding, such as when + /// there are too many errors to correct.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + // [Serializable] // commented by .net follower (http://dotnetfollower.com) + public sealed class ReedSolomonException:System.Exception + { + + public ReedSolomonException(System.String message):base(message) + { + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/DataMatrixReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/DataMatrixReader.cs new file mode 100644 index 0000000..dafece1 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/DataMatrixReader.cs @@ -0,0 +1,172 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using DecodeHintType = com.google.zxing.DecodeHintType; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using Reader = com.google.zxing.Reader; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using ResultMetadataType = com.google.zxing.ResultMetadataType; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DecoderResult = com.google.zxing.common.DecoderResult; +using DetectorResult = com.google.zxing.common.DetectorResult; +using Decoder = com.google.zxing.datamatrix.decoder.Decoder; +using Detector = com.google.zxing.datamatrix.detector.Detector; +namespace com.google.zxing.datamatrix +{ + + /// This implementation can detect and decode Data Matrix codes in an image. + /// + /// + /// bbrown@google.com (Brian Brown) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class DataMatrixReader : Reader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'NO_POINTS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly ResultPoint[] NO_POINTS = new ResultPoint[0]; + + //UPGRADE_NOTE: Final was removed from the declaration of 'decoder '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private Decoder decoder = new Decoder(); + + /// Locates and decodes a Data Matrix code in an image. + /// + /// + /// a String representing the content encoded by the Data Matrix code + /// + /// ReaderException if a Data Matrix code cannot be found, or cannot be decoded + public Result decode(BinaryBitmap image) + { + return decode(image, null); + } + + // public Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + DecoderResult decoderResult; + ResultPoint[] points; + if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE)) + { + BitMatrix bits = extractPureBits(image.BlackMatrix); + decoderResult = decoder.decode(bits); + points = NO_POINTS; + } + else + { + DetectorResult detectorResult = new Detector(image.BlackMatrix).detect(); + decoderResult = decoder.decode(detectorResult.Bits); + points = detectorResult.Points; + } + Result result = new Result(decoderResult.Text, decoderResult.RawBytes, points, BarcodeFormat.DATAMATRIX); + if (decoderResult.ByteSegments != null) + { + result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.ByteSegments); + } + if (decoderResult.ECLevel != null) + { + result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.ECLevel.ToString()); + } + return result; + } + + /// This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image + /// which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border + /// around it. This is a specialized method that works exceptionally fast in this special + /// case. + /// + private static BitMatrix extractPureBits(BitMatrix image) + { + // Now need to determine module size in pixels + + int height = image.Height; + int width = image.Width; + int minDimension = System.Math.Min(height, width); + + // First, skip white border by tracking diagonally from the top left down and to the right: + int borderWidth = 0; + while (borderWidth < minDimension && !image.get_Renamed(borderWidth, borderWidth)) + { + borderWidth++; + } + if (borderWidth == minDimension) + { + throw ReaderException.Instance; + } + + // And then keep tracking across the top-left black module to determine module size + int moduleEnd = borderWidth + 1; + while (moduleEnd < width && image.get_Renamed(moduleEnd, borderWidth)) + { + moduleEnd++; + } + if (moduleEnd == width) + { + throw ReaderException.Instance; + } + + int moduleSize = moduleEnd - borderWidth; + + // And now find where the bottommost black module on the first column ends + int columnEndOfSymbol = height - 1; + while (columnEndOfSymbol >= 0 && !image.get_Renamed(borderWidth, columnEndOfSymbol)) + { + columnEndOfSymbol--; + } + if (columnEndOfSymbol < 0) + { + throw ReaderException.Instance; + } + columnEndOfSymbol++; + + // Make sure width of barcode is a multiple of module size + if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) + { + throw ReaderException.Instance; + } + int dimension = (columnEndOfSymbol - borderWidth) / moduleSize; + + // Push in the "border" by half the module width so that we start + // sampling in the middle of the module. Just in case the image is a + // little off, this will help recover. + borderWidth += (moduleSize >> 1); + + int sampleDimension = borderWidth + (dimension - 1) * moduleSize; + if (sampleDimension >= width || sampleDimension >= height) + { + throw ReaderException.Instance; + } + + // Now just read off the bits + BitMatrix bits = new BitMatrix(dimension); + for (int i = 0; i < dimension; i++) + { + int iOffset = borderWidth + i * moduleSize; + for (int j = 0; j < dimension; j++) + { + if (image.get_Renamed(borderWidth + j * moduleSize, iOffset)) + { + bits.set_Renamed(j, i); + } + } + } + return bits; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/BitMatrixParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/BitMatrixParser.cs new file mode 100644 index 0000000..b94a682 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/BitMatrixParser.cs @@ -0,0 +1,547 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.datamatrix.decoder +{ + + /// bbrown@google.com (Brian Brown) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class BitMatrixParser + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'mappingBitMatrix '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix mappingBitMatrix; + //UPGRADE_NOTE: Final was removed from the declaration of 'readMappingMatrix '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix readMappingMatrix; + //UPGRADE_NOTE: Final was removed from the declaration of 'version '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private Version version; + + /// {@link BitMatrix} to parse + /// + /// ReaderException if dimension is < 10 or > 144 or not 0 mod 2 + internal BitMatrixParser(BitMatrix bitMatrix) + { + int dimension = bitMatrix.Dimension; + if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) + { + throw ReaderException.Instance; + } + + version = readVersion(bitMatrix); + this.mappingBitMatrix = extractDataRegion(bitMatrix); + // TODO(bbrown): Make this work for rectangular symbols + this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.Dimension); + } + + ///

Creates the version object based on the dimension of the original bit matrix from + /// the datamatrix code.

+ /// + ///

See ISO 16022:2006 Table 7 - ECC 200 symbol attributes

+ /// + ///
+ /// Original {@link BitMatrix} including alignment patterns + /// + /// {@link Version} encapsulating the Data Matrix Code's "version" + /// + /// ReaderException if the dimensions of the mapping matrix are not valid + /// Data Matrix dimensions. + /// + internal Version readVersion(BitMatrix bitMatrix) + { + + if (version != null) + { + return version; + } + + // TODO(bbrown): make this work for rectangular dimensions as well. + int numRows = bitMatrix.Dimension; + int numColumns = numRows; + + return Version.getVersionForDimensions(numRows, numColumns); + } + + ///

Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns) + /// in the correct order in order to reconstitute the codewords bytes contained within the + /// Data Matrix Code.

+ /// + ///
+ /// bytes encoded within the Data Matrix Code + /// + /// ReaderException if the exact number of bytes expected is not read + internal sbyte[] readCodewords() + { + + sbyte[] result = new sbyte[version.TotalCodewords]; + int resultOffset = 0; + + int row = 4; + int column = 0; + // TODO(bbrown): Data Matrix can be rectangular, assuming square for now + int numRows = mappingBitMatrix.Dimension; + int numColumns = numRows; + + bool corner1Read = false; + bool corner2Read = false; + bool corner3Read = false; + bool corner4Read = false; + + // Read all of the codewords + do + { + // Check the four corner cases + if ((row == numRows) && (column == 0) && !corner1Read) + { + result[resultOffset++] = (sbyte) readCorner1(numRows, numColumns); + row -= 2; + column += 2; + corner1Read = true; + } + else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) + { + result[resultOffset++] = (sbyte) readCorner2(numRows, numColumns); + row -= 2; + column += 2; + corner2Read = true; + } + else if ((row == numRows + 4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) + { + result[resultOffset++] = (sbyte) readCorner3(numRows, numColumns); + row -= 2; + column += 2; + corner3Read = true; + } + else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) + { + result[resultOffset++] = (sbyte) readCorner4(numRows, numColumns); + row -= 2; + column += 2; + corner4Read = true; + } + else + { + // Sweep upward diagonally to the right + do + { + if ((row < numRows) && (column >= 0) && !readMappingMatrix.get_Renamed(column, row)) + { + result[resultOffset++] = (sbyte) readUtah(row, column, numRows, numColumns); + } + row -= 2; + column += 2; + } + while ((row >= 0) && (column < numColumns)); + row += 1; + column += 3; + + // Sweep downward diagonally to the left + do + { + if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get_Renamed(column, row)) + { + result[resultOffset++] = (sbyte) readUtah(row, column, numRows, numColumns); + } + row += 2; + column -= 2; + } + while ((row < numRows) && (column >= 0)); + row += 3; + column += 1; + } + } + while ((row < numRows) || (column < numColumns)); + + if (resultOffset != version.TotalCodewords) + { + throw ReaderException.Instance; + } + return result; + } + + ///

Reads a bit of the mapping matrix accounting for boundary wrapping.

+ /// + ///
+ /// Row to read in the mapping matrix + /// + /// Column to read in the mapping matrix + /// + /// Number of rows in the mapping matrix + /// + /// Number of columns in the mapping matrix + /// + /// value of the given bit in the mapping matrix + /// + internal bool readModule(int row, int column, int numRows, int numColumns) + { + // Adjust the row and column indices based on boundary wrapping + if (row < 0) + { + row += numRows; + column += 4 - ((numRows + 4) & 0x07); + } + if (column < 0) + { + column += numColumns; + row += 4 - ((numColumns + 4) & 0x07); + } + readMappingMatrix.set_Renamed(column, row); + return mappingBitMatrix.get_Renamed(column, row); + } + + ///

Reads the 8 bits of the standard Utah-shaped pattern.

+ /// + ///

See ISO 16022:2006, 5.8.1 Figure 6

+ /// + ///
+ /// Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern + /// + /// Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern + /// + /// Number of rows in the mapping matrix + /// + /// Number of columns in the mapping matrix + /// + /// byte from the utah shape + /// + internal int readUtah(int row, int column, int numRows, int numColumns) + { + int currentByte = 0; + if (readModule(row - 2, column - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 2, column - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 1, column - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 1, column - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 1, column, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row, column - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row, column - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row, column, numRows, numColumns)) + { + currentByte |= 1; + } + return currentByte; + } + + ///

Reads the 8 bits of the special corner condition 1.

+ /// + ///

See ISO 16022:2006, Figure F.3

+ /// + ///
+ /// Number of rows in the mapping matrix + /// + /// Number of columns in the mapping matrix + /// + /// byte from the Corner condition 1 + /// + internal int readCorner1(int numRows, int numColumns) + { + int currentByte = 0; + if (readModule(numRows - 1, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(2, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(3, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + return currentByte; + } + + ///

Reads the 8 bits of the special corner condition 2.

+ /// + ///

See ISO 16022:2006, Figure F.4

+ /// + ///
+ /// Number of rows in the mapping matrix + /// + /// Number of columns in the mapping matrix + /// + /// byte from the Corner condition 2 + /// + internal int readCorner2(int numRows, int numColumns) + { + int currentByte = 0; + if (readModule(numRows - 3, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 2, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 4, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 3, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + return currentByte; + } + + ///

Reads the 8 bits of the special corner condition 3.

+ /// + ///

See ISO 16022:2006, Figure F.5

+ /// + ///
+ /// Number of rows in the mapping matrix + /// + /// Number of columns in the mapping matrix + /// + /// byte from the Corner condition 3 + /// + internal int readCorner3(int numRows, int numColumns) + { + int currentByte = 0; + if (readModule(numRows - 1, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 3, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 3, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + return currentByte; + } + + ///

Reads the 8 bits of the special corner condition 4.

+ /// + ///

See ISO 16022:2006, Figure F.6

+ /// + ///
+ /// Number of rows in the mapping matrix + /// + /// Number of columns in the mapping matrix + /// + /// byte from the Corner condition 4 + /// + internal int readCorner4(int numRows, int numColumns) + { + int currentByte = 0; + if (readModule(numRows - 3, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 2, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 0, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(2, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(3, numColumns - 1, numRows, numColumns)) + { + currentByte |= 1; + } + return currentByte; + } + + ///

Extracts the data region from a {@link BitMatrix} that contains + /// alignment patterns.

+ /// + ///
+ /// Original {@link BitMatrix} with alignment patterns + /// + /// BitMatrix that has the alignment patterns removed + /// + internal BitMatrix extractDataRegion(BitMatrix bitMatrix) + { + int symbolSizeRows = version.SymbolSizeRows; + int symbolSizeColumns = version.SymbolSizeColumns; + + // TODO(bbrown): Make this work with rectangular codes + if (bitMatrix.Dimension != symbolSizeRows) + { + throw new System.ArgumentException("Dimension of bitMarix must match the version size"); + } + + int dataRegionSizeRows = version.DataRegionSizeRows; + int dataRegionSizeColumns = version.DataRegionSizeColumns; + + int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows; + int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; + + int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; + //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; + + // TODO(bbrown): Make this work with rectangular codes + BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow); + for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) + { + int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; + for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) + { + int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; + for (int i = 0; i < dataRegionSizeRows; ++i) + { + int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; + int writeRowOffset = dataRegionRowOffset + i; + for (int j = 0; j < dataRegionSizeColumns; ++j) + { + int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; + if (bitMatrix.get_Renamed(readColumnOffset, readRowOffset)) + { + int writeColumnOffset = dataRegionColumnOffset + j; + bitMatrixWithoutAlignment.set_Renamed(writeColumnOffset, writeRowOffset); + } + } + } + } + } + return bitMatrixWithoutAlignment; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/DataBlock.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/DataBlock.cs new file mode 100644 index 0000000..28d48a1 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/DataBlock.cs @@ -0,0 +1,144 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.datamatrix.decoder +{ + + ///

Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into + /// multiple blocks, each of which is a unit of data and error-correction codewords. Each + /// is represented by an instance of this class.

+ /// + ///
+ /// bbrown@google.com (Brian Brown) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class DataBlock + { + internal int NumDataCodewords + { + get + { + return numDataCodewords; + } + + } + internal sbyte[] Codewords + { + get + { + return codewords; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'numDataCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int numDataCodewords; + //UPGRADE_NOTE: Final was removed from the declaration of 'codewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[] codewords; + + private DataBlock(int numDataCodewords, sbyte[] codewords) + { + this.numDataCodewords = numDataCodewords; + this.codewords = codewords; + } + + ///

When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them. + /// That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This + /// method will separate the data into original blocks.

+ /// + ///
+ /// bytes as read directly from the Data Matrix Code + /// + /// version of the Data Matrix Code + /// + /// {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the + /// Data Matrix Code + /// + internal static DataBlock[] getDataBlocks(sbyte[] rawCodewords, Version version) + { + // Figure out the number and size of data blocks used by this version + Version.ECBlocks ecBlocks = version.getECBlocks(); + + // First count the total number of data blocks + int totalBlocks = 0; + Version.ECB[] ecBlockArray = ecBlocks.getECBlocks(); + for (int i = 0; i < ecBlockArray.Length; i++) + { + totalBlocks += ecBlockArray[i].Count; + } + + // Now establish DataBlocks of the appropriate size and number of data codewords + DataBlock[] result = new DataBlock[totalBlocks]; + int numResultBlocks = 0; + for (int j = 0; j < ecBlockArray.Length; j++) + { + Version.ECB ecBlock = ecBlockArray[j]; + for (int i = 0; i < ecBlock.Count; i++) + { + int numDataCodewords = ecBlock.DataCodewords; + int numBlockCodewords = ecBlocks.ECCodewords + numDataCodewords; + result[numResultBlocks++] = new DataBlock(numDataCodewords, new sbyte[numBlockCodewords]); + } + } + + // All blocks have the same amount of data, except that the last n + // (where n may be 0) have 1 less byte. Figure out where these start. + // TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144 + int longerBlocksTotalCodewords = result[0].codewords.Length; + //int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1; + + int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.ECCodewords; + int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1; + // The last elements of result may be 1 element shorter for 144 matrix + // first fill out as many elements as all of them have minus 1 + int rawCodewordsOffset = 0; + for (int i = 0; i < shorterBlocksNumDataCodewords; i++) + { + for (int j = 0; j < numResultBlocks; j++) + { + result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; + } + } + + // Fill out the last data block in the longer ones + bool specialVersion = version.VersionNumber == 24; + int numLongerBlocks = specialVersion?8:numResultBlocks; + for (int j = 0; j < numLongerBlocks; j++) + { + result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++]; + } + + // Now add in error correction blocks + int max = result[0].codewords.Length; + for (int i = longerBlocksNumDataCodewords; i < max; i++) + { + for (int j = 0; j < numResultBlocks; j++) + { + int iOffset = (specialVersion && j > 7)?i - 1:i; + result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; + } + } + + if (rawCodewordsOffset != rawCodewords.Length) + { + throw new System.ArgumentException(); + } + + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/DecodedBitStreamParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/DecodedBitStreamParser.cs new file mode 100644 index 0000000..41d8653 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/DecodedBitStreamParser.cs @@ -0,0 +1,629 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitSource = com.google.zxing.common.BitSource; +using DecoderResult = com.google.zxing.common.DecoderResult; +namespace com.google.zxing.datamatrix.decoder +{ + + ///

Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes + /// in one Data Matrix Code. This class decodes the bits back into text.

+ /// + ///

See ISO 16022:2006, 5.2.1 - 5.2.9.2

+ /// + ///
+ /// bbrown@google.com (Brian Brown) + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class DecodedBitStreamParser + { + + /// See ISO 16022:2006, Annex C Table C.1 + /// The C40 Basic Character Set (*'s used for placeholders for the shift values) + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'C40_BASIC_SET_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] C40_BASIC_SET_CHARS = new char[]{'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'C40_SHIFT2_SET_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] C40_SHIFT2_SET_CHARS = new char[]{'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'}; + + /// See ISO 16022:2006, Annex C Table C.2 + /// The Text Basic Character Set (*'s used for placeholders for the shift values) + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'TEXT_BASIC_SET_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] TEXT_BASIC_SET_CHARS = new char[]{'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + private static char[] TEXT_SHIFT3_SET_CHARS = new char[]{'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127}; + + private const int PAD_ENCODE = 0; // Not really an encoding + private const int ASCII_ENCODE = 1; + private const int C40_ENCODE = 2; + private const int TEXT_ENCODE = 3; + private const int ANSIX12_ENCODE = 4; + private const int EDIFACT_ENCODE = 5; + private const int BASE256_ENCODE = 6; + + private DecodedBitStreamParser() + { + } + + internal static DecoderResult decode(sbyte[] bytes) + { + BitSource bits = new BitSource(bytes); + System.Text.StringBuilder result = new System.Text.StringBuilder(100); + System.Text.StringBuilder resultTrailer = new System.Text.StringBuilder(0); + // System.Collections.ArrayList byteSegments = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(1)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List byteSegments = new System.Collections.Generic.List(1); // added by .net follower (http://dotnetfollower.com) + int mode = ASCII_ENCODE; + do + { + if (mode == ASCII_ENCODE) + { + mode = decodeAsciiSegment(bits, result, resultTrailer); + } + else + { + switch (mode) + { + + case C40_ENCODE: + decodeC40Segment(bits, result); + break; + + case TEXT_ENCODE: + decodeTextSegment(bits, result); + break; + + case ANSIX12_ENCODE: + decodeAnsiX12Segment(bits, result); + break; + + case EDIFACT_ENCODE: + decodeEdifactSegment(bits, result); + break; + + case BASE256_ENCODE: + decodeBase256Segment(bits, result, byteSegments); + break; + + default: + throw ReaderException.Instance; + + } + mode = ASCII_ENCODE; + } + } + while (mode != PAD_ENCODE && bits.available() > 0); + if (resultTrailer.Length > 0) + { + result.Append(resultTrailer.ToString()); + } + return new DecoderResult(bytes, result.ToString(), (byteSegments.Count == 0)?null:byteSegments, null); + } + + /// See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 + private static int decodeAsciiSegment(BitSource bits, System.Text.StringBuilder result, System.Text.StringBuilder resultTrailer) + { + bool upperShift = false; + do + { + int oneByte = bits.readBits(8); + if (oneByte == 0) + { + throw ReaderException.Instance; + } + else if (oneByte <= 128) + { + // ASCII data (ASCII value + 1) + oneByte = upperShift?(oneByte + 128):oneByte; + upperShift = false; + result.Append((char) (oneByte - 1)); + return ASCII_ENCODE; + } + else if (oneByte == 129) + { + // Pad + return PAD_ENCODE; + } + else if (oneByte <= 229) + { + // 2-digit data 00-99 (Numeric Value + 130) + int value_Renamed = oneByte - 130; + if (value_Renamed < 10) + { + // padd with '0' for single digit values + result.Append('0'); + } + result.Append(value_Renamed); + } + else if (oneByte == 230) + { + // Latch to C40 encodation + return C40_ENCODE; + } + else if (oneByte == 231) + { + // Latch to Base 256 encodation + return BASE256_ENCODE; + } + else if (oneByte == 232) + { + // FNC1 + //throw ReaderException.getInstance(); + // Ignore this symbol for now + } + else if (oneByte == 233) + { + // Structured Append + //throw ReaderException.getInstance(); + // Ignore this symbol for now + } + else if (oneByte == 234) + { + // Reader Programming + //throw ReaderException.getInstance(); + // Ignore this symbol for now + } + else if (oneByte == 235) + { + // Upper Shift (shift to Extended ASCII) + upperShift = true; + } + else if (oneByte == 236) + { + // 05 Macro + result.Append("[)>\u001E05\u001D"); + resultTrailer.Insert(0, "\u001E\u0004"); + } + else if (oneByte == 237) + { + // 06 Macro + result.Append("[)>\u001E06\u001D"); + resultTrailer.Insert(0, "\u001E\u0004"); + } + else if (oneByte == 238) + { + // Latch to ANSI X12 encodation + return ANSIX12_ENCODE; + } + else if (oneByte == 239) + { + // Latch to Text encodation + return TEXT_ENCODE; + } + else if (oneByte == 240) + { + // Latch to EDIFACT encodation + return EDIFACT_ENCODE; + } + else if (oneByte == 241) + { + // ECI Character + // TODO(bbrown): I think we need to support ECI + //throw ReaderException.getInstance(); + // Ignore this symbol for now + } + else if (oneByte >= 242) + { + // Not to be used in ASCII encodation + throw ReaderException.Instance; + } + } + while (bits.available() > 0); + return ASCII_ENCODE; + } + + /// See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 + private static void decodeC40Segment(BitSource bits, System.Text.StringBuilder result) + { + // Three C40 values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time + bool upperShift = false; + + int[] cValues = new int[3]; + do + { + // If there is only one byte left then it will be encoded as ASCII + if (bits.available() == 8) + { + return ; + } + int firstByte = bits.readBits(8); + if (firstByte == 254) + { + // Unlatch codeword + return ; + } + + parseTwoBytes(firstByte, bits.readBits(8), cValues); + + int shift = 0; + for (int i = 0; i < 3; i++) + { + int cValue = cValues[i]; + switch (shift) + { + + case 0: + if (cValue < 3) + { + shift = cValue + 1; + } + else + { + if (upperShift) + { + result.Append((char) (C40_BASIC_SET_CHARS[cValue] + 128)); + upperShift = false; + } + else + { + result.Append(C40_BASIC_SET_CHARS[cValue]); + } + } + break; + + case 1: + if (upperShift) + { + result.Append((char) (cValue + 128)); + upperShift = false; + } + else + { + result.Append(cValue); + } + shift = 0; + break; + + case 2: + if (cValue < 27) + { + if (upperShift) + { + result.Append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128)); + upperShift = false; + } + else + { + result.Append(C40_SHIFT2_SET_CHARS[cValue]); + } + } + else if (cValue == 27) + { + // FNC1 + throw ReaderException.Instance; + } + else if (cValue == 30) + { + // Upper Shift + upperShift = true; + } + else + { + throw ReaderException.Instance; + } + shift = 0; + break; + + case 3: + if (upperShift) + { + result.Append((char) (cValue + 224)); + upperShift = false; + } + else + { + result.Append((char) (cValue + 96)); + } + shift = 0; + break; + + default: + throw ReaderException.Instance; + + } + } + } + while (bits.available() > 0); + } + + /// See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 + private static void decodeTextSegment(BitSource bits, System.Text.StringBuilder result) + { + // Three Text values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time + bool upperShift = false; + + int[] cValues = new int[3]; + do + { + // If there is only one byte left then it will be encoded as ASCII + if (bits.available() == 8) + { + return ; + } + int firstByte = bits.readBits(8); + if (firstByte == 254) + { + // Unlatch codeword + return ; + } + + parseTwoBytes(firstByte, bits.readBits(8), cValues); + + int shift = 0; + for (int i = 0; i < 3; i++) + { + int cValue = cValues[i]; + switch (shift) + { + + case 0: + if (cValue < 3) + { + shift = cValue + 1; + } + else + { + if (upperShift) + { + result.Append((char) (TEXT_BASIC_SET_CHARS[cValue] + 128)); + upperShift = false; + } + else + { + result.Append(TEXT_BASIC_SET_CHARS[cValue]); + } + } + break; + + case 1: + if (upperShift) + { + result.Append((char) (cValue + 128)); + upperShift = false; + } + else + { + result.Append(cValue); + } + shift = 0; + break; + + case 2: + // Shift 2 for Text is the same encoding as C40 + if (cValue < 27) + { + if (upperShift) + { + result.Append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128)); + upperShift = false; + } + else + { + result.Append(C40_SHIFT2_SET_CHARS[cValue]); + } + } + else if (cValue == 27) + { + // FNC1 + throw ReaderException.Instance; + } + else if (cValue == 30) + { + // Upper Shift + upperShift = true; + } + else + { + throw ReaderException.Instance; + } + shift = 0; + break; + + case 3: + if (upperShift) + { + result.Append((char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128)); + upperShift = false; + } + else + { + result.Append(TEXT_SHIFT3_SET_CHARS[cValue]); + } + shift = 0; + break; + + default: + throw ReaderException.Instance; + + } + } + } + while (bits.available() > 0); + } + + /// See ISO 16022:2006, 5.2.7 + private static void decodeAnsiX12Segment(BitSource bits, System.Text.StringBuilder result) + { + // Three ANSI X12 values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + + int[] cValues = new int[3]; + do + { + // If there is only one byte left then it will be encoded as ASCII + if (bits.available() == 8) + { + return ; + } + int firstByte = bits.readBits(8); + if (firstByte == 254) + { + // Unlatch codeword + return ; + } + + parseTwoBytes(firstByte, bits.readBits(8), cValues); + + for (int i = 0; i < 3; i++) + { + int cValue = cValues[i]; + if (cValue == 0) + { + // X12 segment terminator + result.Append('\r'); + } + else if (cValue == 1) + { + // X12 segment separator * + result.Append('*'); + } + else if (cValue == 2) + { + // X12 sub-element separator > + result.Append('>'); + } + else if (cValue == 3) + { + // space + result.Append(' '); + } + else if (cValue < 14) + { + // 0 - 9 + result.Append((char) (cValue + 44)); + } + else if (cValue < 40) + { + // A - Z + result.Append((char) (cValue + 51)); + } + else + { + throw ReaderException.Instance; + } + } + } + while (bits.available() > 0); + } + + private static void parseTwoBytes(int firstByte, int secondByte, int[] result) + { + int fullBitValue = (firstByte << 8) + secondByte - 1; + int temp = fullBitValue / 1600; + result[0] = temp; + fullBitValue -= temp * 1600; + temp = fullBitValue / 40; + result[1] = temp; + result[2] = fullBitValue - temp * 40; + } + + /// See ISO 16022:2006, 5.2.8 and Annex C Table C.3 + private static void decodeEdifactSegment(BitSource bits, System.Text.StringBuilder result) + { + bool unlatch = false; + do + { + // If there is only two or less bytes left then it will be encoded as ASCII + if (bits.available() <= 16) + { + return ; + } + + for (int i = 0; i < 4; i++) + { + int edifactValue = bits.readBits(6); + + // Check for the unlatch character + if (edifactValue == 0x2B67) + { + // 011111 + unlatch = true; + // If we encounter the unlatch code then continue reading because the Codeword triple + // is padded with 0's + } + + if (!unlatch) + { + if ((edifactValue & 32) == 0) + { + // no 1 in the leading (6th) bit + edifactValue |= 64; // Add a leading 01 to the 6 bit binary value + } + result.Append(edifactValue); + } + } + } + while (!unlatch && bits.available() > 0); + } + + /// See ISO 16022:2006, 5.2.9 and Annex B, B.2 + //private static void decodeBase256Segment(BitSource bits, System.Text.StringBuilder result, System.Collections.ArrayList byteSegments) // commented by .net follower (http://dotnetfollower.com) + private static void decodeBase256Segment(BitSource bits, System.Text.StringBuilder result, System.Collections.Generic.List byteSegments) // added by .net follower (http://dotnetfollower.com) + { + // Figure out how long the Base 256 Segment is. + int d1 = bits.readBits(8); + int count; + if (d1 == 0) + { + // Read the remainder of the symbol + count = bits.available() / 8; + } + else if (d1 < 250) + { + count = d1; + } + else + { + count = 250 * (d1 - 249) + bits.readBits(8); + } + sbyte[] bytes = new sbyte[count]; + for (int i = 0; i < count; i++) + { + bytes[i] = unrandomize255State(bits.readBits(8), i); + } + byteSegments.Add(SupportClass.ToByteArray(bytes)); + try + { + //UPGRADE_TODO: The differences in the Format of parameters for constructor 'java.lang.String.String' may cause compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'" + // result.Append(System.Text.Encoding.GetEncoding("ISO8859_1").GetString(SupportClass.ToByteArray(bytes))); // commented by .net follower (http://dotnetfollower.com) + byte[] inputBytes = SupportClass.ToByteArray(bytes); // added by .net follower (http://dotnetfollower.com) + result.Append(System.Text.Encoding.GetEncoding("ISO8859_1").GetString(inputBytes, 0, inputBytes.Length)); // added by .net follower (http://dotnetfollower.com) + } + catch (System.IO.IOException uee) + { + //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" + throw new System.SystemException("Platform does not support required encoding: " + uee); + } + } + + /// See ISO 16022:2006, Annex B, B.2 + private static sbyte unrandomize255State(int randomizedBase256Codeword, int base256CodewordPosition) + { + int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1; + int tempVariable = randomizedBase256Codeword - pseudoRandomNumber; + return (sbyte) (tempVariable >= 0?tempVariable:(tempVariable + 256)); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/Decoder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/Decoder.cs new file mode 100644 index 0000000..4901ceb --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/Decoder.cs @@ -0,0 +1,153 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DecoderResult = com.google.zxing.common.DecoderResult; +using GF256 = com.google.zxing.common.reedsolomon.GF256; +using ReedSolomonDecoder = com.google.zxing.common.reedsolomon.ReedSolomonDecoder; +using ReedSolomonException = com.google.zxing.common.reedsolomon.ReedSolomonException; +namespace com.google.zxing.datamatrix.decoder +{ + + ///

The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting + /// the Data Matrix Code from an image.

+ /// + ///
+ /// bbrown@google.com (Brian Brown) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Decoder + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'rsDecoder '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ReedSolomonDecoder rsDecoder; + + public Decoder() + { + rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD); + } + + ///

Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans. + /// "true" is taken to mean a black module.

+ /// + ///
+ /// booleans representing white/black Data Matrix Code modules + /// + /// text and bytes encoded within the Data Matrix Code + /// + /// ReaderException if the Data Matrix Code cannot be decoded + public DecoderResult decode(bool[][] image) + { + int dimension = image.Length; + BitMatrix bits = new BitMatrix(dimension); + for (int i = 0; i < dimension; i++) + { + for (int j = 0; j < dimension; j++) + { + if (image[i][j]) + { + bits.set_Renamed(j, i); + } + } + } + return decode(bits); + } + + ///

Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken + /// to mean a black module.

+ /// + ///
+ /// booleans representing white/black Data Matrix Code modules + /// + /// text and bytes encoded within the Data Matrix Code + /// + /// ReaderException if the Data Matrix Code cannot be decoded + public DecoderResult decode(BitMatrix bits) + { + + // Construct a parser and read version, error-correction level + BitMatrixParser parser = new BitMatrixParser(bits); + Version version = parser.readVersion(bits); + + // Read codewords + sbyte[] codewords = parser.readCodewords(); + // Separate into data blocks + DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version); + + // Count total number of data bytes + int totalBytes = 0; + for (int i = 0; i < dataBlocks.Length; i++) + { + totalBytes += dataBlocks[i].NumDataCodewords; + } + sbyte[] resultBytes = new sbyte[totalBytes]; + int resultOffset = 0; + + // Error-correct and copy data blocks together into a stream of bytes + for (int j = 0; j < dataBlocks.Length; j++) + { + DataBlock dataBlock = dataBlocks[j]; + sbyte[] codewordBytes = dataBlock.Codewords; + int numDataCodewords = dataBlock.NumDataCodewords; + correctErrors(codewordBytes, numDataCodewords); + for (int i = 0; i < numDataCodewords; i++) + { + resultBytes[resultOffset++] = codewordBytes[i]; + } + } + + // Decode the contents of that stream of bytes + return DecodedBitStreamParser.decode(resultBytes); + } + + ///

Given data and error-correction codewords received, possibly corrupted by errors, attempts to + /// correct the errors in-place using Reed-Solomon error correction.

+ /// + ///
+ /// data and error correction codewords + /// + /// number of codewords that are data bytes + /// + /// ReaderException if error correction fails + private void correctErrors(sbyte[] codewordBytes, int numDataCodewords) + { + int numCodewords = codewordBytes.Length; + // First read into an array of ints + int[] codewordsInts = new int[numCodewords]; + for (int i = 0; i < numCodewords; i++) + { + codewordsInts[i] = codewordBytes[i] & 0xFF; + } + int numECCodewords = codewordBytes.Length - numDataCodewords; + try + { + rsDecoder.decode(codewordsInts, numECCodewords); + } + catch (ReedSolomonException rse) + { + throw ReaderException.Instance; + } + // Copy back into array of bytes -- only need to worry about the bytes that were data + // We don't care about errors in the error-correction codewords + for (int i = 0; i < numDataCodewords; i++) + { + codewordBytes[i] = (sbyte) codewordsInts[i]; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/Version.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/Version.cs new file mode 100644 index 0000000..e91ef7d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/decoder/Version.cs @@ -0,0 +1,241 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +namespace com.google.zxing.datamatrix.decoder +{ + + /// The Version object encapsulates attributes about a particular + /// size Data Matrix Code. + /// + /// + /// bbrown@google.com (Brian Brown) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Version + { + public int VersionNumber + { + get + { + return versionNumber; + } + + } + public int SymbolSizeRows + { + get + { + return symbolSizeRows; + } + + } + public int SymbolSizeColumns + { + get + { + return symbolSizeColumns; + } + + } + public int DataRegionSizeRows + { + get + { + return dataRegionSizeRows; + } + + } + public int DataRegionSizeColumns + { + get + { + return dataRegionSizeColumns; + } + + } + public int TotalCodewords + { + get + { + return totalCodewords; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'VERSIONS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly Version[] VERSIONS = buildVersions(); + + //UPGRADE_NOTE: Final was removed from the declaration of 'versionNumber '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int versionNumber; + //UPGRADE_NOTE: Final was removed from the declaration of 'symbolSizeRows '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int symbolSizeRows; + //UPGRADE_NOTE: Final was removed from the declaration of 'symbolSizeColumns '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int symbolSizeColumns; + //UPGRADE_NOTE: Final was removed from the declaration of 'dataRegionSizeRows '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int dataRegionSizeRows; + //UPGRADE_NOTE: Final was removed from the declaration of 'dataRegionSizeColumns '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int dataRegionSizeColumns; + //UPGRADE_NOTE: Final was removed from the declaration of 'ecBlocks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ECBlocks ecBlocks; + //UPGRADE_NOTE: Final was removed from the declaration of 'totalCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int totalCodewords; + + private Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns, int dataRegionSizeRows, int dataRegionSizeColumns, ECBlocks ecBlocks) + { + this.versionNumber = versionNumber; + this.symbolSizeRows = symbolSizeRows; + this.symbolSizeColumns = symbolSizeColumns; + this.dataRegionSizeRows = dataRegionSizeRows; + this.dataRegionSizeColumns = dataRegionSizeColumns; + this.ecBlocks = ecBlocks; + + // Calculate the total number of codewords + int total = 0; + int ecCodewords = ecBlocks.ECCodewords; + ECB[] ecbArray = ecBlocks.getECBlocks(); + for (int i = 0; i < ecbArray.Length; i++) + { + ECB ecBlock = ecbArray[i]; + total += ecBlock.Count * (ecBlock.DataCodewords + ecCodewords); + } + this.totalCodewords = total; + } + + internal ECBlocks getECBlocks() + { + return ecBlocks; + } + + ///

Deduces version information from Data Matrix dimensions.

+ /// + ///
+ /// Number of rows in modules + /// + /// Number of columns in modules + /// + /// {@link Version} for a Data Matrix Code of those dimensions + /// + /// ReaderException if dimensions do correspond to a valid Data Matrix size + public static Version getVersionForDimensions(int numRows, int numColumns) + { + if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) + { + throw ReaderException.Instance; + } + + // TODO(bbrown): This is doing a linear search through the array of versions. + // If we interleave the rectangular versions with the square versions we could + // do a binary search. + int numVersions = VERSIONS.Length; + for (int i = 0; i < numVersions; ++i) + { + Version version = VERSIONS[i]; + if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) + { + return version; + } + } + + throw ReaderException.Instance; + } + + ///

Encapsulates a set of error-correction blocks in one symbol version. Most versions will + /// use blocks of differing sizes within one version, so, this encapsulates the parameters for + /// each set of blocks. It also holds the number of error-correction codewords per block since it + /// will be the same across all blocks within one version.

+ ///
+ internal sealed class ECBlocks + { + internal int ECCodewords + { + get + { + return ecCodewords; + } + + } + //UPGRADE_NOTE: Final was removed from the declaration of 'ecCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int ecCodewords; + //UPGRADE_NOTE: Final was removed from the declaration of 'ecBlocks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ECB[] ecBlocks; + + internal ECBlocks(int ecCodewords, ECB ecBlocks) + { + this.ecCodewords = ecCodewords; + this.ecBlocks = new ECB[]{ecBlocks}; + } + + internal ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) + { + this.ecCodewords = ecCodewords; + this.ecBlocks = new ECB[]{ecBlocks1, ecBlocks2}; + } + + internal ECB[] getECBlocks() + { + return ecBlocks; + } + } + + ///

Encapsualtes the parameters for one error-correction block in one symbol version. + /// This includes the number of data codewords, and the number of times a block with these + /// parameters is used consecutively in the Data Matrix code version's format.

+ ///
+ internal sealed class ECB + { + internal int Count + { + get + { + return count; + } + + } + internal int DataCodewords + { + get + { + return dataCodewords; + } + + } + //UPGRADE_NOTE: Final was removed from the declaration of 'count '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int count; + //UPGRADE_NOTE: Final was removed from the declaration of 'dataCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int dataCodewords; + + internal ECB(int count, int dataCodewords) + { + this.count = count; + this.dataCodewords = dataCodewords; + } + } + + public override System.String ToString() + { + return System.Convert.ToString(versionNumber); + } + + /// See ISO 16022:2006 5.5.1 Table 7 + private static Version[] buildVersions() + { + return new Version[]{new Version(1, 10, 10, 8, 8, new ECBlocks(5, new ECB(1, 3))), new Version(2, 12, 12, 10, 10, new ECBlocks(7, new ECB(1, 5))), new Version(3, 14, 14, 12, 12, new ECBlocks(10, new ECB(1, 8))), new Version(4, 16, 16, 14, 14, new ECBlocks(12, new ECB(1, 12))), new Version(5, 18, 18, 16, 16, new ECBlocks(14, new ECB(1, 18))), new Version(6, 20, 20, 18, 18, new ECBlocks(18, new ECB(1, 22))), new Version(7, 22, 22, 20, 20, new ECBlocks(20, new ECB(1, 30))), new Version(8, 24, 24, 22, 22, new ECBlocks(24, new ECB(1, 36))), new Version(9, 26, 26, 24, 24, new ECBlocks(28, new ECB(1, 44))), new Version(10, 32, 32, 14, 14, new ECBlocks(36, new ECB(1, 62))), new Version(11, 36, 36, 16, 16, new ECBlocks(42, new ECB(1, 86))), new Version(12, 40, 40, 18, 18, new ECBlocks(48, new ECB(1, 114))), new Version(13, 44, 44, 20, 20, new ECBlocks(56, new ECB(1, 144))), new Version(14, 48, 48, 22, 22, new ECBlocks(68, new ECB(1, 174))), new Version(15, 52, 52, 24, 24, new ECBlocks(42, new ECB(2, 102))), new Version(16, 64, 64, 14, 14, new ECBlocks(56, new ECB(2, 140))), new Version(17, 72, 72, 16, 16, new ECBlocks(36, new ECB(4, 92))), new Version(18, 80, 80, 18, 18, new ECBlocks(48, new ECB(4, 114))), new Version(19, 88, 88, 20, 20, new ECBlocks(56, new ECB(4, 144))), new Version(20, 96, 96, 22, 22, new ECBlocks(68, new ECB(4, 174))), new Version(21, 104, 104, 24, 24, new ECBlocks(56, new ECB(6, 136))), new Version(22, 120, 120, 18, 18, new ECBlocks(68, new ECB(6, 175))), new Version(23, 132, 132, 20, 20, new ECBlocks(62, new ECB(8, 163))), new Version(24, 144, 144, 22, 22, new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))), new Version(25, 8, 18, 6, 16, new ECBlocks(7, new ECB(1, 5))), new Version(26, 8, 32, 6, 14, new ECBlocks(11, new ECB(1, 10))), new Version(27, 12, 26, 10, 24, new ECBlocks(14, new ECB(1, 16))), new Version(28, 12, 36, 10, 16, new ECBlocks(18, new ECB(1, 22))), new Version(29, 16, 36, 10, 16, new ECBlocks(24, new ECB(1, 32))), new Version(30, 16, 48, 14, 22, new ECBlocks(28, + new ECB(1, 49)))}; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/detector/Detector.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/detector/Detector.cs new file mode 100644 index 0000000..d426e6a --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/datamatrix/detector/Detector.cs @@ -0,0 +1,328 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using BitMatrix = com.google.zxing.common.BitMatrix; +using Collections = com.google.zxing.common.Collections; +using Comparator = com.google.zxing.common.Comparator; +using DetectorResult = com.google.zxing.common.DetectorResult; +using GridSampler = com.google.zxing.common.GridSampler; +using MonochromeRectangleDetector = com.google.zxing.common.detector.MonochromeRectangleDetector; +namespace com.google.zxing.datamatrix.detector +{ + + ///

Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code + /// is rotated or skewed, or partially obscured.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Detector + { + + //private static final int MAX_MODULES = 32; + + // Trick to avoid creating new Integer objects below -- a sort of crude copy of + // the Integer.valueOf(int) optimization added in Java 5, not in J2ME + //UPGRADE_NOTE: Final was removed from the declaration of 'INTEGERS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly System.Int32[] INTEGERS = new System.Int32[]{0, 1, 2, 3, 4}; + // No, can't use valueOf() + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix image; + //UPGRADE_NOTE: Final was removed from the declaration of 'rectangleDetector '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private MonochromeRectangleDetector rectangleDetector; + + public Detector(BitMatrix image) + { + this.image = image; + rectangleDetector = new MonochromeRectangleDetector(image); + } + + ///

Detects a Data Matrix Code in an image.

+ /// + ///
+ /// {@link DetectorResult} encapsulating results of detecting a QR Code + /// + /// ReaderException if no Data Matrix Code can be found + public DetectorResult detect() + { + + ResultPoint[] cornerPoints = rectangleDetector.detect(); + ResultPoint pointA = cornerPoints[0]; + ResultPoint pointB = cornerPoints[1]; + ResultPoint pointC = cornerPoints[2]; + ResultPoint pointD = cornerPoints[3]; + + // Point A and D are across the diagonal from one another, + // as are B and C. Figure out which are the solid black lines + // by counting transitions + // System.Collections.ArrayList transitions = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(4)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List transitions = new System.Collections.Generic.List(4); // added by .net follower (http://dotnetfollower.com) + transitions.Add(transitionsBetween(pointA, pointB)); + transitions.Add(transitionsBetween(pointA, pointC)); + transitions.Add(transitionsBetween(pointB, pointD)); + transitions.Add(transitionsBetween(pointC, pointD)); + Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator()); + + // Sort by number of transitions. First two will be the two solid sides; last two + // will be the two alternating black/white sides + ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions) transitions[0]; + ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions) transitions[1]; + + // Figure out which point is their intersection by tallying up the number of times we see the + // endpoints in the four endpoints. One will show up twice. + // System.Collections.Hashtable pointCount = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.Dictionary pointCount = new System.Collections.Generic.Dictionary(); // added by .net follower (http://dotnetfollower.com) + increment(pointCount, lSideOne.From); + increment(pointCount, lSideOne.To); + increment(pointCount, lSideTwo.From); + increment(pointCount, lSideTwo.To); + + ResultPoint maybeTopLeft = null; + ResultPoint bottomLeft = null; + ResultPoint maybeBottomRight = null; + System.Collections.IEnumerator points = pointCount.Keys.GetEnumerator(); + //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'" + while (points.MoveNext()) + { + //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'" + ResultPoint point = (ResultPoint) points.Current; + System.Int32 value_Renamed = (System.Int32) pointCount[point]; + if (value_Renamed == 2) + { + bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides + } + else + { + // Otherwise it's either top left or bottom right -- just assign the two arbitrarily now + if (maybeTopLeft == null) + { + maybeTopLeft = point; + } + else + { + maybeBottomRight = point; + } + } + } + + if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) + { + throw ReaderException.Instance; + } + + // Bottom left is correct but top left and bottom right might be switched + ResultPoint[] corners = new ResultPoint[]{maybeTopLeft, bottomLeft, maybeBottomRight}; + // Use the dot product trick to sort them out + ResultPoint.orderBestPatterns(corners); + + // Now we know which is which: + ResultPoint bottomRight = corners[0]; + bottomLeft = corners[1]; + ResultPoint topLeft = corners[2]; + + // Which point didn't we find in relation to the "L" sides? that's the top right corner + ResultPoint topRight; + if (!pointCount.ContainsKey(pointA)) + { + topRight = pointA; + } + else if (!pointCount.ContainsKey(pointB)) + { + topRight = pointB; + } + else if (!pointCount.ContainsKey(pointC)) + { + topRight = pointC; + } + else + { + topRight = pointD; + } + + // Next determine the dimension by tracing along the top or right side and counting black/white + // transitions. Since we start inside a black module, we should see a number of transitions + // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to + // end on a black module: + + // The top right point is actually the corner of a module, which is one of the two black modules + // adjacent to the white module at the top right. Tracing to that corner from either the top left + // or bottom right should work here. The number of transitions could be higher than it should be + // due to noise. So we try both and take the min. + + int dimension = System.Math.Min(transitionsBetween(topLeft, topRight).Transitions, transitionsBetween(bottomRight, topRight).Transitions); + if ((dimension & 0x01) == 1) + { + // it can't be odd, so, round... up? + dimension++; + } + dimension += 2; + + BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension); + return new DetectorResult(bits, new ResultPoint[]{pointA, pointB, pointC, pointD}); + } + + /// Increments the Integer associated with a key by one. + // private static void increment(System.Collections.Hashtable table, ResultPoint key) // commented by .net follower (http://dotnetfollower.com) + private static void increment(System.Collections.Generic.Dictionary table, ResultPoint key) // added by .net follower (http://dotnetfollower.com) + { + //System.Int32 value_Renamed = (System.Int32) table[key]; + ////UPGRADE_TODO: The 'System.Int32' structure does not have an equivalent to NULL. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1291'" + //table[key] = value_Renamed == null?INTEGERS[1]:INTEGERS[value_Renamed + 1]; + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // Added + // START + System.Int32 value_Renamed = 0; + try + { + if (table.Count > 0) + value_Renamed = (System.Int32)table[key]; + } + catch + { + value_Renamed = 0; + } + table[key] = value_Renamed == 0 ? INTEGERS[1] : INTEGERS[value_Renamed + 1]; + //END + } + + private static BitMatrix sampleGrid(BitMatrix image, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint bottomRight, int dimension) + { + + // We make up the top right point for now, based on the others. + // TODO: we actually found a fourth corner above and figured out which of two modules + // it was the corner of. We could use that here and adjust for perspective distortion. + float topRightX = (bottomRight.X - bottomLeft.X) + topLeft.X; + float topRightY = (bottomRight.Y - bottomLeft.Y) + topLeft.Y; + + // Note that unlike in the QR Code sampler, we didn't find the center of modules, but the + // very corners. So there is no 0.5f here; 0.0f is right. + GridSampler sampler = GridSampler.Instance; + return sampler.sampleGrid(image, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.X, topLeft.Y, topRightX, topRightY, bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y); + } + + /// Counts the number of black/white transitions between two points, using something like Bresenham's algorithm. + private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to) + { + // See QR Code Detector, sizeOfBlackWhiteBlackRun() + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int fromX = (int) from.X; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int fromY = (int) from.Y; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int toX = (int) to.X; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int toY = (int) to.Y; + bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX); + if (steep) + { + int temp = fromX; + fromX = fromY; + fromY = temp; + temp = toX; + toX = toY; + toY = temp; + } + + int dx = System.Math.Abs(toX - fromX); + int dy = System.Math.Abs(toY - fromY); + int error = - dx >> 1; + int ystep = fromY < toY?1:- 1; + int xstep = fromX < toX?1:- 1; + int transitions = 0; + bool inBlack = image.get_Renamed(steep?fromY:fromX, steep?fromX:fromY); + for (int x = fromX, y = fromY; x != toX; x += xstep) + { + bool isBlack = image.get_Renamed(steep?y:x, steep?x:y); + if (isBlack != inBlack) + { + transitions++; + inBlack = isBlack; + } + error += dy; + if (error > 0) + { + if (y == toY) + { + break; + } + y += ystep; + error -= dx; + } + } + return new ResultPointsAndTransitions(from, to, transitions); + } + + /// Simply encapsulates two points and a number of transitions between them. + private class ResultPointsAndTransitions + { + public ResultPoint From + { + get + { + return from; + } + + } + public ResultPoint To + { + get + { + return to; + } + + } + public int Transitions + { + get + { + return transitions; + } + + } + //UPGRADE_NOTE: Final was removed from the declaration of 'from '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPoint from; + //UPGRADE_NOTE: Final was removed from the declaration of 'to '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPoint to; + //UPGRADE_NOTE: Final was removed from the declaration of 'transitions '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int transitions; + internal ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions) + { + this.from = from; + this.to = to; + this.transitions = transitions; + } + public override System.String ToString() + { + return from + "/" + to + '/' + transitions; + } + } + + /// Orders ResultPointsAndTransitions by number of transitions, ascending. + private class ResultPointsAndTransitionsComparator : Comparator + { + public int compare(System.Object o1, System.Object o2) + { + return ((ResultPointsAndTransitions) o1).Transitions - ((ResultPointsAndTransitions) o2).Transitions; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/ByQuadrantReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/ByQuadrantReader.cs new file mode 100644 index 0000000..c33d90f --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/ByQuadrantReader.cs @@ -0,0 +1,104 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using Reader = com.google.zxing.Reader; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +namespace com.google.zxing.multi +{ + + /// This class attempts to decode a barcode from an image, not by scanning the whole image, + /// but by scanning subsets of the image. This is important when there may be multiple barcodes in + /// an image, and detecting a barcode may find parts of multiple barcode and fail to decode + /// (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center + /// 'quadrant' to cover the case where a barcode is found in the center. + /// + /// + /// + /// + public sealed class ByQuadrantReader : Reader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'delegate '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private Reader delegate_Renamed; + + public ByQuadrantReader(Reader delegate_Renamed) + { + this.delegate_Renamed = delegate_Renamed; + } + + public Result decode(BinaryBitmap image) + { + return decode(image, null); + } + + // public Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + int width = image.Width; + int height = image.Height; + int halfWidth = width / 2; + int halfHeight = height / 2; + + BinaryBitmap topLeft = image.crop(0, 0, halfWidth, halfHeight); + try + { + return delegate_Renamed.decode(topLeft, hints); + } + catch (ReaderException re) + { + // continue + } + + BinaryBitmap topRight = image.crop(halfWidth, 0, halfWidth, halfHeight); + try + { + return delegate_Renamed.decode(topRight, hints); + } + catch (ReaderException re) + { + // continue + } + + BinaryBitmap bottomLeft = image.crop(0, halfHeight, halfWidth, halfHeight); + try + { + return delegate_Renamed.decode(bottomLeft, hints); + } + catch (ReaderException re) + { + // continue + } + + BinaryBitmap bottomRight = image.crop(halfWidth, halfHeight, halfWidth, halfHeight); + try + { + return delegate_Renamed.decode(bottomRight, hints); + } + catch (ReaderException re) + { + // continue + } + + int quarterWidth = halfWidth / 2; + int quarterHeight = halfHeight / 2; + BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight); + return delegate_Renamed.decode(center, hints); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/GenericMultipleBarcodeReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/GenericMultipleBarcodeReader.cs new file mode 100644 index 0000000..9c35f29 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/GenericMultipleBarcodeReader.cs @@ -0,0 +1,178 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using Reader = com.google.zxing.Reader; +using Result = com.google.zxing.Result; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +namespace com.google.zxing.multi +{ + + ///

Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image. + /// After one barcode is found, the areas left, above, right and below the barcode's + /// {@link com.google.zxing.ResultPoint}s are scanned, recursively.

+ /// + ///

A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple + /// 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent + /// detecting any one of them.

+ /// + ///

That is, instead of passing a {@link Reader} a caller might pass + /// new ByQuadrantReader(reader).

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public sealed class GenericMultipleBarcodeReader : MultipleBarcodeReader + { + + private const int MIN_DIMENSION_TO_RECUR = 30; + + //UPGRADE_NOTE: Final was removed from the declaration of 'delegate '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private Reader delegate_Renamed; + + public GenericMultipleBarcodeReader(Reader delegate_Renamed) + { + this.delegate_Renamed = delegate_Renamed; + } + + public Result[] decodeMultiple(BinaryBitmap image) + { + return decodeMultiple(image, null); + } + + // public Result[] decodeMultiple(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public Result[] decodeMultiple(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + // System.Collections.ArrayList results = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List results = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + doDecodeMultiple(image, hints, results, 0, 0); + if ((results.Count == 0)) + { + throw ReaderException.Instance; + } + int numResults = results.Count; + Result[] resultArray = new Result[numResults]; + for (int i = 0; i < numResults; i++) + { + resultArray[i] = (Result) results[i]; + } + return resultArray; + } + + // private void doDecodeMultiple(BinaryBitmap image, System.Collections.Hashtable hints, System.Collections.ArrayList results, int xOffset, int yOffset) // commented by .net follower (http://dotnetfollower.com) + private void doDecodeMultiple(BinaryBitmap image, System.Collections.Generic.Dictionary hints, System.Collections.Generic.List results, int xOffset, int yOffset) // added by .net follower (http://dotnetfollower.com) + { + Result result; + try + { + result = delegate_Renamed.decode(image, hints); + } + catch (ReaderException re) + { + return ; + } + bool alreadyFound = false; + for (int i = 0; i < results.Count; i++) + { + Result existingResult = (Result) results[i]; + if (existingResult.Text.Equals(result.Text)) + { + alreadyFound = true; + break; + } + } + if (alreadyFound) + { + return ; + } + results.Add(translateResultPoints(result, xOffset, yOffset)); + ResultPoint[] resultPoints = result.ResultPoints; + if (resultPoints == null || resultPoints.Length == 0) + { + return ; + } + int width = image.Width; + int height = image.Height; + float minX = width; + float minY = height; + float maxX = 0.0f; + float maxY = 0.0f; + for (int i = 0; i < resultPoints.Length; i++) + { + ResultPoint point = resultPoints[i]; + float x = point.X; + float y = point.Y; + if (x < minX) + { + minX = x; + } + if (y < minY) + { + minY = y; + } + if (x > maxX) + { + maxX = x; + } + if (y > maxY) + { + maxY = y; + } + } + + // Decode left of barcode + if (minX > MIN_DIMENSION_TO_RECUR) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + doDecodeMultiple(image.crop(0, 0, (int) minX, height), hints, results, xOffset, yOffset); + } + // Decode above barcode + if (minY > MIN_DIMENSION_TO_RECUR) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + doDecodeMultiple(image.crop(0, 0, width, (int) minY), hints, results, xOffset, yOffset); + } + // Decode right of barcode + if (maxX < width - MIN_DIMENSION_TO_RECUR) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height), hints, results, xOffset + (int) maxX, yOffset); + } + // Decode below barcode + if (maxY < height - MIN_DIMENSION_TO_RECUR) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY), hints, results, xOffset, yOffset + (int) maxY); + } + } + + private static Result translateResultPoints(Result result, int xOffset, int yOffset) + { + ResultPoint[] oldResultPoints = result.ResultPoints; + ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.Length]; + for (int i = 0; i < oldResultPoints.Length; i++) + { + ResultPoint oldPoint = oldResultPoints[i]; + newResultPoints[i] = new ResultPoint(oldPoint.X + xOffset, oldPoint.Y + yOffset); + } + return new Result(result.Text, result.RawBytes, newResultPoints, result.BarcodeFormat); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/MultipleBarcodeReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/MultipleBarcodeReader.cs new file mode 100644 index 0000000..3e89869 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/MultipleBarcodeReader.cs @@ -0,0 +1,41 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using Result = com.google.zxing.Result; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using ReaderException = com.google.zxing.ReaderException; +namespace com.google.zxing.multi +{ + + /// Implementation of this interface attempt to read several barcodes from one image. + /// + /// + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public interface MultipleBarcodeReader + { + + Result[] decodeMultiple(BinaryBitmap image); + + // Result[] decodeMultiple(BinaryBitmap image, System.Collections.Hashtable hints); // commented by .net follower (http://dotnetfollower.com) + Result[] decodeMultiple(BinaryBitmap image, System.Collections.Generic.Dictionary hints); // added by .net follower (http://dotnetfollower.com) + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/QRCodeMultiReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/QRCodeMultiReader.cs new file mode 100644 index 0000000..15f2e84 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/QRCodeMultiReader.cs @@ -0,0 +1,95 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultMetadataType = com.google.zxing.ResultMetadataType; +using ResultPoint = com.google.zxing.ResultPoint; +using DecoderResult = com.google.zxing.common.DecoderResult; +using DetectorResult = com.google.zxing.common.DetectorResult; +using MultipleBarcodeReader = com.google.zxing.multi.MultipleBarcodeReader; +using MultiDetector = com.google.zxing.multi.qrcode.detector.MultiDetector; +using QRCodeReader = com.google.zxing.qrcode.QRCodeReader; +namespace com.google.zxing.multi.qrcode +{ + + /// This implementation can detect and decode multiple QR Codes in an image. + /// + /// + /// Sean Owen + /// + /// Hannes Erven + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public sealed class QRCodeMultiReader:QRCodeReader, MultipleBarcodeReader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'EMPTY_RESULT_ARRAY '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly Result[] EMPTY_RESULT_ARRAY = new Result[0]; + + public Result[] decodeMultiple(BinaryBitmap image) + { + return decodeMultiple(image, null); + } + + // public Result[] decodeMultiple(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public Result[] decodeMultiple(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + // System.Collections.ArrayList results = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List results = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + DetectorResult[] detectorResult = new MultiDetector(image.BlackMatrix).detectMulti(hints); + for (int i = 0; i < detectorResult.Length; i++) + { + try + { + DecoderResult decoderResult = Decoder.decode(detectorResult[i].Bits); + ResultPoint[] points = detectorResult[i].Points; + Result result = new Result(decoderResult.Text, decoderResult.RawBytes, points, BarcodeFormat.QR_CODE); + if (decoderResult.ByteSegments != null) + { + result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.ByteSegments); + } + if (decoderResult.ECLevel != null) + { + result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.ECLevel.ToString()); + } + results.Add(result); + } + catch (ReaderException re) + { + // ignore and continue + } + } + if ((results.Count == 0)) + { + return EMPTY_RESULT_ARRAY; + } + else + { + Result[] resultArray = new Result[results.Count]; + for (int i = 0; i < results.Count; i++) + { + resultArray[i] = (Result) results[i]; + } + return resultArray; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/detector/MultiDetector.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/detector/MultiDetector.cs new file mode 100644 index 0000000..eec79ae --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/detector/MultiDetector.cs @@ -0,0 +1,86 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using DetectorResult = com.google.zxing.common.DetectorResult; +using BitMatrix = com.google.zxing.common.BitMatrix; +using Detector = com.google.zxing.qrcode.detector.Detector; +using FinderPatternInfo = com.google.zxing.qrcode.detector.FinderPatternInfo; +namespace com.google.zxing.multi.qrcode.detector +{ + + ///

Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code + /// is rotated or skewed, or partially obscured.

+ /// + ///
+ /// Sean Owen + /// + /// Hannes Erven + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + public sealed class MultiDetector:Detector + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'EMPTY_DETECTOR_RESULTS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly DetectorResult[] EMPTY_DETECTOR_RESULTS = new DetectorResult[0]; + + public MultiDetector(BitMatrix image):base(image) + { + } + + // public DetectorResult[] detectMulti(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public DetectorResult[] detectMulti(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + BitMatrix image = Image; + MultiFinderPatternFinder finder = new MultiFinderPatternFinder(image); + FinderPatternInfo[] info = finder.findMulti(hints); + + if (info == null || info.Length == 0) + { + throw ReaderException.Instance; + } + + // System.Collections.ArrayList result = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List result = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + for (int i = 0; i < info.Length; i++) + { + try + { + result.Add(processFinderPatternInfo(info[i])); + } + catch (ReaderException e) + { + // ignore + } + } + if ((result.Count == 0)) + { + return EMPTY_DETECTOR_RESULTS; + } + else + { + DetectorResult[] resultArray = new DetectorResult[result.Count]; + for (int i = 0; i < result.Count; i++) + { + resultArray[i] = (DetectorResult) result[i]; + } + return resultArray; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/detector/MultiFinderPatternFinder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/detector/MultiFinderPatternFinder.cs new file mode 100644 index 0000000..ebf7b6a --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/multi/qrcode/detector/MultiFinderPatternFinder.cs @@ -0,0 +1,371 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using ResultPointCallback = com.google.zxing.ResultPointCallback; +using Collections = com.google.zxing.common.Collections; +using Comparator = com.google.zxing.common.Comparator; +using BitMatrix = com.google.zxing.common.BitMatrix; +using FinderPattern = com.google.zxing.qrcode.detector.FinderPattern; +using FinderPatternFinder = com.google.zxing.qrcode.detector.FinderPatternFinder; +using FinderPatternInfo = com.google.zxing.qrcode.detector.FinderPatternInfo; +namespace com.google.zxing.multi.qrcode.detector +{ + + ///

This class attempts to find finder patterns in a QR Code. Finder patterns are the square + /// markers at three corners of a QR Code.

+ /// + ///

This class is thread-safe but not reentrant. Each thread must allocate its own object. + /// + ///

In contrast to {@link FinderPatternFinder}, this class will return an array of all possible + /// QR code locations in the image.

+ /// + ///

Use the TRY_HARDER hint to ask for a more thorough detection.

+ /// + ///
+ /// Sean Owen + /// + /// Hannes Erven + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + + + sealed class MultiFinderPatternFinder:FinderPatternFinder + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'EMPTY_RESULT_ARRAY '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0]; + + // TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for + // since it limits the number of regions to decode + + // max. legal count of modules per QR code edge (177) + private const float MAX_MODULE_COUNT_PER_EDGE = 180; + // min. legal count per modules per QR code edge (11) + private const float MIN_MODULE_COUNT_PER_EDGE = 9; + + /// More or less arbitrary cutoff point for determining if two finder patterns might belong + /// to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their + /// estimated modules sizes. + /// + private const float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f; + + /// More or less arbitrary cutoff point for determining if two finder patterns might belong + /// to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their + /// estimated modules sizes. + /// + private const float DIFF_MODSIZE_CUTOFF = 0.5f; + + + /// A comparator that orders FinderPatterns by their estimated module size. + private class ModuleSizeComparator : Comparator + { + public int compare(System.Object center1, System.Object center2) + { + float value_Renamed = ((FinderPattern) center2).EstimatedModuleSize - ((FinderPattern) center1).EstimatedModuleSize; + return value_Renamed < 0.0?- 1:(value_Renamed > 0.0?1:0); + } + } + + ///

Creates a finder that will search the image for three finder patterns.

+ /// + ///
+ /// image to search + /// + internal MultiFinderPatternFinder(BitMatrix image):base(image) + { + } + + internal MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback):base(image, resultPointCallback) + { + } + + /// the 3 best {@link FinderPattern}s from our list of candidates. The "best" are + /// those that have been detected at least {@link #CENTER_QUORUM} times, and whose module + /// size differs from the average among those patterns the least + /// + /// ReaderException if 3 such finder patterns do not exist + private FinderPattern[][] selectBestPatterns() + { + // System.Collections.ArrayList possibleCenters = PossibleCenters; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List possibleCenters = PossibleCenters; // added by .net follower (http://dotnetfollower.com) + int size = possibleCenters.Count; + + if (size < 3) + { + // Couldn't find enough finder patterns + throw ReaderException.Instance; + } + + /* + * Begin HE modifications to safely detect multiple codes of equal size + */ + if (size == 3) + { + return new FinderPattern[][]{new FinderPattern[]{(FinderPattern) possibleCenters[0], (FinderPattern) possibleCenters[1], (FinderPattern) possibleCenters[2]}}; + } + + // Sort by estimated module size to speed up the upcoming checks + Collections.insertionSort(possibleCenters, new ModuleSizeComparator()); + + /* + * Now lets start: build a list of tuples of three finder locations that + * - feature similar module sizes + * - are placed in a distance so the estimated module count is within the QR specification + * - have similar distance between upper left/right and left top/bottom finder patterns + * - form a triangle with 90° angle (checked by comparing top right/bottom left distance + * with pythagoras) + * + * Note: we allow each point to be used for more than one code region: this might seem + * counterintuitive at first, but the performance penalty is not that big. At this point, + * we cannot make a good quality decision whether the three finders actually represent + * a QR code, or are just by chance layouted so it looks like there might be a QR code there. + * So, if the layout seems right, lets have the decoder try to decode. + */ + + // System.Collections.ArrayList results = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // holder for the results // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List results = new System.Collections.Generic.List(10); // holder for the results // added by .net follower (http://dotnetfollower.com) + + for (int i1 = 0; i1 < (size - 2); i1++) + { + FinderPattern p1 = (FinderPattern) possibleCenters[i1]; + if (p1 == null) + { + continue; + } + + for (int i2 = i1 + 1; i2 < (size - 1); i2++) + { + FinderPattern p2 = (FinderPattern) possibleCenters[i2]; + if (p2 == null) + { + continue; + } + + // Compare the expected module sizes; if they are really off, skip + float vModSize12 = (p1.EstimatedModuleSize - p2.EstimatedModuleSize) / (System.Math.Min(p1.EstimatedModuleSize, p2.EstimatedModuleSize)); + float vModSize12A = System.Math.Abs(p1.EstimatedModuleSize - p2.EstimatedModuleSize); + if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) + { + // break, since elements are ordered by the module size deviation there cannot be + // any more interesting elements for the given p1. + break; + } + + for (int i3 = i2 + 1; i3 < size; i3++) + { + FinderPattern p3 = (FinderPattern) possibleCenters[i3]; + if (p3 == null) + { + continue; + } + + // Compare the expected module sizes; if they are really off, skip + float vModSize23 = (p2.EstimatedModuleSize - p3.EstimatedModuleSize) / (System.Math.Min(p2.EstimatedModuleSize, p3.EstimatedModuleSize)); + float vModSize23A = System.Math.Abs(p2.EstimatedModuleSize - p3.EstimatedModuleSize); + if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) + { + // break, since elements are ordered by the module size deviation there cannot be + // any more interesting elements for the given p1. + break; + } + + FinderPattern[] test = new FinderPattern[]{p1, p2, p3}; + ResultPoint.orderBestPatterns(test); + + // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal + FinderPatternInfo info = new FinderPatternInfo(test); + float dA = ResultPoint.distance(info.TopLeft, info.BottomLeft); + float dC = ResultPoint.distance(info.TopRight, info.BottomLeft); + float dB = ResultPoint.distance(info.TopLeft, info.TopRight); + + // Check the sizes + float estimatedModuleCount = ((dA + dB) / p1.EstimatedModuleSize) / 2; + if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) + { + continue; + } + + // Calculate the difference of the edge lengths in percent + float vABBC = System.Math.Abs(((dA - dB) / System.Math.Min(dA, dB))); + if (vABBC >= 0.1f) + { + continue; + } + + // Calculate the diagonal length by assuming a 90° angle at topleft + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float dCpy = (float) System.Math.Sqrt(dA * dA + dB * dB); + // Compare to the real distance in % + float vPyC = System.Math.Abs(((dC - dCpy) / System.Math.Min(dC, dCpy))); + + if (vPyC >= 0.1f) + { + continue; + } + + // All tests passed! + results.Add(test); + } // end iterate p3 + } // end iterate p2 + } // end iterate p1 + + if (!(results.Count == 0)) + { + FinderPattern[][] resultArray = new FinderPattern[results.Count][]; + for (int i = 0; i < results.Count; i++) + { + resultArray[i] = (FinderPattern[]) results[i]; + } + return resultArray; + } + + // Nothing found! + throw ReaderException.Instance; + } + + // public FinderPatternInfo[] findMulti(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public FinderPatternInfo[] findMulti(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); + BitMatrix image = Image; + int maxI = image.Height; + int maxJ = image.Width; + // We are looking for black/white/black/white/black modules in + // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far + + // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the + // image, and then account for the center being 3 modules in size. This gives the smallest + // number of pixels the center could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3); + if (iSkip < MIN_SKIP || tryHarder) + { + iSkip = MIN_SKIP; + } + + int[] stateCount = new int[5]; + for (int i = iSkip - 1; i < maxI; i += iSkip) + { + // Get a row of black/white values + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + int currentState = 0; + for (int j = 0; j < maxJ; j++) + { + if (image.get_Renamed(j, i)) + { + // Black pixel + if ((currentState & 1) == 1) + { + // Counting white pixels + currentState++; + } + stateCount[currentState]++; + } + else + { + // White pixel + if ((currentState & 1) == 0) + { + // Counting black pixels + if (currentState == 4) + { + // A winner? + if (foundPatternCross(stateCount)) + { + // Yes + bool confirmed = handlePossibleCenter(stateCount, i, j); + if (!confirmed) + { + do + { + // Advance to next black pixel + j++; + } + while (j < maxJ && !image.get_Renamed(j, i)); + j--; // back up to that last white pixel + } + // Clear state to start looking again + currentState = 0; + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + } + else + { + // No, shift counts back by two + stateCount[0] = stateCount[2]; + stateCount[1] = stateCount[3]; + stateCount[2] = stateCount[4]; + stateCount[3] = 1; + stateCount[4] = 0; + currentState = 3; + } + } + else + { + stateCount[++currentState]++; + } + } + else + { + // Counting white pixels + stateCount[currentState]++; + } + } + } // for j=... + + if (foundPatternCross(stateCount)) + { + handlePossibleCenter(stateCount, i, maxJ); + } // end if foundPatternCross + } // for i=iSkip-1 ... + FinderPattern[][] patternInfo = selectBestPatterns(); + // System.Collections.ArrayList result = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List result = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + for (int i = 0; i < patternInfo.Length; i++) + { + FinderPattern[] pattern = patternInfo[i]; + ResultPoint.orderBestPatterns(pattern); + result.Add(new FinderPatternInfo(pattern)); + } + + if ((result.Count == 0)) + { + return EMPTY_RESULT_ARRAY; + } + else + { + FinderPatternInfo[] resultArray = new FinderPatternInfo[result.Count]; + for (int i = 0; i < result.Count; i++) + { + resultArray[i] = (FinderPatternInfo) result[i]; + } + return resultArray; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code128Reader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code128Reader.cs new file mode 100644 index 0000000..a02282a --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code128Reader.cs @@ -0,0 +1,445 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Decodes Code 128 barcodes.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Code128Reader:OneDReader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'CODE_PATTERNS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] CODE_PATTERNS = new int[][]{new int[]{2, 1, 2, 2, 2, 2}, new int[]{2, 2, 2, 1, 2, 2}, new int[]{2, 2, 2, 2, 2, 1}, new int[]{1, 2, 1, 2, 2, 3}, new int[]{1, 2, 1, 3, 2, 2}, new int[]{1, 3, 1, 2, 2, 2}, new int[]{1, 2, 2, 2, 1, 3}, new int[]{1, 2, 2, 3, 1, 2}, new int[]{1, 3, 2, 2, 1, 2}, new int[]{2, 2, 1, 2, 1, 3}, new int[]{2, 2, 1, 3, 1, 2}, new int[]{2, 3, 1, 2, 1, 2}, new int[]{1, 1, 2, 2, 3, 2}, new int[]{1, 2, 2, 1, 3, 2}, new int[]{1, 2, 2, 2, 3, 1}, new int[]{1, 1, 3, 2, 2, 2}, new int[]{1, 2, 3, 1, 2, 2}, new int[]{1, 2, 3, 2, 2, 1}, new int[]{2, 2, 3, 2, 1, 1}, new int[]{2, 2, 1, 1, 3, 2}, new int[]{2, 2, 1, 2, 3, 1}, new int[]{2, 1, 3, 2, 1, 2}, new int[]{2, 2, 3, 1, 1, 2}, new int[]{3, 1, 2, 1, 3, 1}, new int[]{3, 1, 1, 2, 2, 2}, new int[]{3, 2, 1, 1, 2, 2}, new int[]{3, 2, 1, 2, 2, 1}, new int[]{3, 1, 2, 2, 1, 2}, new int[]{3, 2, 2, 1, 1, 2}, new int[]{3, 2, 2, 2, 1, 1}, new int[]{2, 1, 2, 1, 2, 3}, new int[]{2, 1, 2, 3, 2, 1}, new int[]{2, 3, 2, 1, 2, 1}, new int[]{1, 1, 1, 3, 2, 3}, new int[]{1, 3, 1, 1, 2, 3}, new int[]{1, 3, 1, 3, 2, 1}, new int[]{1, 1, 2, 3, 1, 3}, new int[]{1, 3, 2, 1, 1, 3}, new int[]{1, 3, 2, 3, 1, 1}, new int[]{2, 1, 1, 3, 1, 3}, new int[]{2, 3, 1, 1, 1, 3}, new int[]{2, 3, 1, 3, 1, 1}, new int[]{1, 1, 2, 1, 3, 3}, new int[]{1, 1, 2, 3, 3, 1}, new int[]{1, 3, 2, 1, 3, 1}, new int[]{1, 1, 3, 1, 2, 3}, new int[]{1, 1, 3, 3, 2, 1}, new int[]{1, 3, 3, 1, 2, 1}, new int[]{3, 1, 3, 1, 2, 1}, new int[]{2, 1, 1, 3, 3, 1}, new int[]{2, 3, 1, 1, 3, 1}, new int[]{2, 1, 3, 1, 1, 3}, new int[]{2, 1, 3, 3, 1, 1}, new int[]{2, 1, 3, 1, 3, 1}, new int[]{3, 1, 1, 1, 2, 3}, new int[]{3, 1, 1, 3, 2, 1}, new int[]{3, 3, 1, 1, 2, 1}, new int[]{3, 1, 2, 1, 1, 3}, new int[]{3, 1, 2, 3, 1, 1}, new int[]{3, 3, 2, 1, 1, 1}, new int[]{3, 1, 4, 1, 1, 1}, new int[]{2, 2, 1, 4, 1, 1}, new int[]{4, 3, 1, 1, 1, 1}, new int[]{1, 1, 1, 2, 2, 4}, new int[]{1, 1, 1, 4, 2, 2}, new int[]{1, 2, 1, 1, 2, 4}, new int[]{1, 2, 1, 4, 2, 1}, new int[]{1, 4, 1, 1, 2, 2}, new + int[]{1, 4, 1, 2, 2, 1}, new int[]{1, 1, 2, 2, 1, 4}, new int[]{1, 1, 2, 4, 1, 2}, new int[]{1, 2, 2, 1, 1, 4}, new int[]{1, 2, 2, 4, 1, 1}, new int[]{1, 4, 2, 1, 1, 2}, new int[]{1, 4, 2, 2, 1, 1}, new int[]{2, 4, 1, 2, 1, 1}, new int[]{2, 2, 1, 1, 1, 4}, new int[]{4, 1, 3, 1, 1, 1}, new int[]{2, 4, 1, 1, 1, 2}, new int[]{1, 3, 4, 1, 1, 1}, new int[]{1, 1, 1, 2, 4, 2}, new int[]{1, 2, 1, 1, 4, 2}, new int[]{1, 2, 1, 2, 4, 1}, new int[]{1, 1, 4, 2, 1, 2}, new int[]{1, 2, 4, 1, 1, 2}, new int[]{1, 2, 4, 2, 1, 1}, new int[]{4, 1, 1, 2, 1, 2}, new int[]{4, 2, 1, 1, 1, 2}, new int[]{4, 2, 1, 2, 1, 1}, new int[]{2, 1, 2, 1, 4, 1}, new int[]{2, 1, 4, 1, 2, 1}, new int[]{4, 1, 2, 1, 2, 1}, new int[]{1, 1, 1, 1, 4, 3}, new int[]{1, 1, 1, 3, 4, 1}, new int[]{1, 3, 1, 1, 4, 1}, new int[]{1, 1, 4, 1, 1, 3}, new int[]{1, 1, 4, 3, 1, 1}, new int[]{4, 1, 1, 1, 1, 3}, new int[]{4, 1, 1, 3, 1, 1}, new int[]{1, 1, 3, 1, 4, 1}, new int[]{1, 1, 4, 1, 3, 1}, new int[]{3, 1, 1, 1, 4, 1}, new int[]{4, 1, 1, 1, 3, 1}, new int[]{2, 1, 1, 4, 1, 2}, new int[]{2, 1, 1, 2, 1, 4}, new int[]{2, 1, 1, 2, 3, 2}, new int[]{2, 3, 3, 1, 1, 1, 2}}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_AVG_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static readonly int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f); + //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_INDIVIDUAL_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static readonly int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f); + + private const int CODE_SHIFT = 98; + + private const int CODE_CODE_C = 99; + private const int CODE_CODE_B = 100; + private const int CODE_CODE_A = 101; + + private const int CODE_FNC_1 = 102; + private const int CODE_FNC_2 = 97; + private const int CODE_FNC_3 = 96; + private const int CODE_FNC_4_A = 101; + private const int CODE_FNC_4_B = 100; + + private const int CODE_START_A = 103; + private const int CODE_START_B = 104; + private const int CODE_START_C = 105; + private const int CODE_STOP = 106; + + private static int[] findStartPattern(BitArray row) + { + int width = row.Size; + int rowOffset = 0; + while (rowOffset < width) + { + if (row.get_Renamed(rowOffset)) + { + break; + } + rowOffset++; + } + + int counterPosition = 0; + int[] counters = new int[6]; + int patternStart = rowOffset; + bool isWhite = false; + int patternLength = counters.Length; + + for (int i = rowOffset; i < width; i++) + { + bool pixel = row.get_Renamed(i); + if (pixel ^ isWhite) + { + counters[counterPosition]++; + } + else + { + if (counterPosition == patternLength - 1) + { + int bestVariance = MAX_AVG_VARIANCE; + int bestMatch = - 1; + for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) + { + int variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) + { + bestVariance = variance; + bestMatch = startCode; + } + } + if (bestMatch >= 0) + { + // Look for whitespace before start pattern, >= 50% of width of start pattern + if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) + { + return new int[]{patternStart, i, bestMatch}; + } + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) + { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } + else + { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException.Instance; + } + + private static int decodeCode(BitArray row, int[] counters, int rowOffset) + { + recordPattern(row, rowOffset, counters); + int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = - 1; + for (int d = 0; d < CODE_PATTERNS.Length; d++) + { + int[] pattern = CODE_PATTERNS[d]; + int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) + { + bestVariance = variance; + bestMatch = d; + } + } + // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6. + if (bestMatch >= 0) + { + return bestMatch; + } + else + { + throw ReaderException.Instance; + } + } + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + int[] startPatternInfo = findStartPattern(row); + int startCode = startPatternInfo[2]; + int codeSet; + switch (startCode) + { + + case CODE_START_A: + codeSet = CODE_CODE_A; + break; + + case CODE_START_B: + codeSet = CODE_CODE_B; + break; + + case CODE_START_C: + codeSet = CODE_CODE_C; + break; + + default: + throw ReaderException.Instance; + + } + + bool done = false; + bool isNextShifted = false; + + System.Text.StringBuilder result = new System.Text.StringBuilder(20); + int lastStart = startPatternInfo[0]; + int nextStart = startPatternInfo[1]; + int[] counters = new int[6]; + + int lastCode = 0; + int code = 0; + int checksumTotal = startCode; + int multiplier = 0; + bool lastCharacterWasPrintable = true; + + while (!done) + { + + bool unshift = isNextShifted; + isNextShifted = false; + + // Save off last code + lastCode = code; + + // Decode another code from image + code = decodeCode(row, counters, nextStart); + + // Remember whether the last code was printable or not (excluding CODE_STOP) + if (code != CODE_STOP) + { + lastCharacterWasPrintable = true; + } + + // Add to checksum computation (if not CODE_STOP of course) + if (code != CODE_STOP) + { + multiplier++; + checksumTotal += multiplier * code; + } + + // Advance to where the next code will to start + lastStart = nextStart; + for (int i = 0; i < counters.Length; i++) + { + nextStart += counters[i]; + } + + // Take care of illegal start codes + switch (code) + { + + case CODE_START_A: + case CODE_START_B: + case CODE_START_C: + throw ReaderException.Instance; + } + + switch (codeSet) + { + + + case CODE_CODE_A: + if (code < 64) + { + result.Append((char) (' ' + code)); + } + else if (code < 96) + { + result.Append((char) (code - 64)); + } + else + { + // Don't let CODE_STOP, which always appears, affect whether whether we think the last + // code was printable or not. + if (code != CODE_STOP) + { + lastCharacterWasPrintable = false; + } + switch (code) + { + + case CODE_FNC_1: + case CODE_FNC_2: + case CODE_FNC_3: + case CODE_FNC_4_A: + // do nothing? + break; + + case CODE_SHIFT: + isNextShifted = true; + codeSet = CODE_CODE_B; + break; + + case CODE_CODE_B: + codeSet = CODE_CODE_B; + break; + + case CODE_CODE_C: + codeSet = CODE_CODE_C; + break; + + case CODE_STOP: + done = true; + break; + } + } + break; + + case CODE_CODE_B: + if (code < 96) + { + result.Append((char) (' ' + code)); + } + else + { + if (code != CODE_STOP) + { + lastCharacterWasPrintable = false; + } + switch (code) + { + + case CODE_FNC_1: + case CODE_FNC_2: + case CODE_FNC_3: + case CODE_FNC_4_B: + // do nothing? + break; + + case CODE_SHIFT: + isNextShifted = true; + codeSet = CODE_CODE_C; + break; + + case CODE_CODE_A: + codeSet = CODE_CODE_A; + break; + + case CODE_CODE_C: + codeSet = CODE_CODE_C; + break; + + case CODE_STOP: + done = true; + break; + } + } + break; + + case CODE_CODE_C: + if (code < 100) + { + if (code < 10) + { + result.Append('0'); + } + result.Append(code); + } + else + { + if (code != CODE_STOP) + { + lastCharacterWasPrintable = false; + } + switch (code) + { + + case CODE_FNC_1: + // do nothing? + break; + + case CODE_CODE_A: + codeSet = CODE_CODE_A; + break; + + case CODE_CODE_B: + codeSet = CODE_CODE_B; + break; + + case CODE_STOP: + done = true; + break; + } + } + break; + } + + // Unshift back to another code set if we were shifted + if (unshift) + { + switch (codeSet) + { + + case CODE_CODE_A: + codeSet = CODE_CODE_C; + break; + + case CODE_CODE_B: + codeSet = CODE_CODE_A; + break; + + case CODE_CODE_C: + codeSet = CODE_CODE_B; + break; + } + } + } + + // Check for ample whitespace following pattern, but, to do this we first need to remember that + // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left + // to read off. Would be slightly better to properly read. Here we just skip it: + int width = row.Size; + while (nextStart < width && row.get_Renamed(nextStart)) + { + nextStart++; + } + if (!row.isRange(nextStart, System.Math.Min(width, nextStart + (nextStart - lastStart) / 2), false)) + { + throw ReaderException.Instance; + } + + // Pull out from sum the value of the penultimate check code + checksumTotal -= multiplier * lastCode; + // lastCode is the checksum then: + if (checksumTotal % 103 != lastCode) + { + throw ReaderException.Instance; + } + + // Need to pull out the check digits from string + int resultLength = result.Length; + // Only bother if the result had at least one character, and if the checksum digit happened to + // be a printable character. If it was just interpreted as a control code, nothing to remove. + if (resultLength > 0 && lastCharacterWasPrintable) + { + if (codeSet == CODE_CODE_C) + { + result.Remove(resultLength - 2, resultLength - (resultLength - 2)); + } + else + { + result.Remove(resultLength - 1, resultLength - (resultLength - 1)); + } + } + + System.String resultString = result.ToString(); + + if (resultString.Length == 0) + { + // Almost surely a false positive + throw ReaderException.Instance; + } + + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float right = (float) (nextStart + lastStart) / 2.0f; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_128); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code39Reader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code39Reader.cs new file mode 100644 index 0000000..a77e447 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code39Reader.cs @@ -0,0 +1,393 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Decodes Code 39 barcodes. This does not support "Full ASCII Code 39" yet.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Code39Reader:OneDReader + { + + internal const System.String ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; + //UPGRADE_NOTE: Final was removed from the declaration of 'ALPHABET '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] ALPHABET = ALPHABET_STRING.ToCharArray(); + + /// These represent the encodings of characters, as patterns of wide and narrow bars. + /// The 9 least-significant bits of each int correspond to the pattern of wide and narrow, + /// with 1s representing "wide" and 0s representing narrow. + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'CHARACTER_ENCODINGS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] CHARACTER_ENCODINGS = new int[]{0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'ASTERISK_ENCODING '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int ASTERISK_ENCODING = CHARACTER_ENCODINGS[39]; + + //UPGRADE_NOTE: Final was removed from the declaration of 'usingCheckDigit '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private bool usingCheckDigit; + //UPGRADE_NOTE: Final was removed from the declaration of 'extendedMode '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private bool extendedMode; + + /// Creates a reader that assumes all encoded data is data, and does not treat the final + /// character as a check digit. It will not decoded "extended Code 39" sequences. + /// + public Code39Reader() + { + usingCheckDigit = false; + extendedMode = false; + } + + /// Creates a reader that can be configured to check the last character as a check digit. + /// It will not decoded "extended Code 39" sequences. + /// + /// + /// if true, treat the last data character as a check digit, not + /// data, and verify that the checksum passes. + /// + public Code39Reader(bool usingCheckDigit) + { + this.usingCheckDigit = usingCheckDigit; + this.extendedMode = false; + } + + /// Creates a reader that can be configured to check the last character as a check digit, + /// or optionally attempt to decode "extended Code 39" sequences that are used to encode + /// the full ASCII character set. + /// + /// + /// if true, treat the last data character as a check digit, not + /// data, and verify that the checksum passes. + /// + /// if true, will attempt to decode extended Code 39 sequences in the + /// text. + /// + public Code39Reader(bool usingCheckDigit, bool extendedMode) + { + this.usingCheckDigit = usingCheckDigit; + this.extendedMode = extendedMode; + } + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + int[] start = findAsteriskPattern(row); + int nextStart = start[1]; + int end = row.Size; + + // Read off white space + while (nextStart < end && !row.get_Renamed(nextStart)) + { + nextStart++; + } + + System.Text.StringBuilder result = new System.Text.StringBuilder(20); + int[] counters = new int[9]; + char decodedChar; + int lastStart; + do + { + recordPattern(row, nextStart, counters); + int pattern = toNarrowWidePattern(counters); + if (pattern < 0) + { + throw ReaderException.Instance; + } + decodedChar = patternToChar(pattern); + result.Append(decodedChar); + lastStart = nextStart; + for (int i = 0; i < counters.Length; i++) + { + nextStart += counters[i]; + } + // Read off white space + while (nextStart < end && !row.get_Renamed(nextStart)) + { + nextStart++; + } + } + while (decodedChar != '*'); + result.Remove(result.Length - 1, 1); // remove asterisk + + // Look for whitespace after pattern: + int lastPatternSize = 0; + for (int i = 0; i < counters.Length; i++) + { + lastPatternSize += counters[i]; + } + int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; + // If 50% of last pattern size, following last pattern, is not whitespace, fail + // (but if it's whitespace to the very end of the image, that's OK) + if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) + { + throw ReaderException.Instance; + } + + if (usingCheckDigit) + { + int max = result.Length - 1; + int total = 0; + for (int i = 0; i < max; i++) + { + total += ALPHABET_STRING.IndexOf((System.Char) result[i]); + } + if (total % 43 != ALPHABET_STRING.IndexOf((System.Char) result[max])) + { + throw ReaderException.Instance; + } + result.Remove(max, 1); + } + + System.String resultString = result.ToString(); + if (extendedMode) + { + resultString = decodeExtended(resultString); + } + + if (resultString.Length == 0) + { + // Almost surely a false positive + throw ReaderException.Instance; + } + + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float left = (float) (start[1] + start[0]) / 2.0f; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float right = (float) (nextStart + lastStart) / 2.0f; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat.CODE_39); + } + + private static int[] findAsteriskPattern(BitArray row) + { + int width = row.Size; + int rowOffset = 0; + while (rowOffset < width) + { + if (row.get_Renamed(rowOffset)) + { + break; + } + rowOffset++; + } + + int counterPosition = 0; + int[] counters = new int[9]; + int patternStart = rowOffset; + bool isWhite = false; + int patternLength = counters.Length; + + for (int i = rowOffset; i < width; i++) + { + bool pixel = row.get_Renamed(i); + if (pixel ^ isWhite) + { + counters[counterPosition]++; + } + else + { + if (counterPosition == patternLength - 1) + { + if (toNarrowWidePattern(counters) == ASTERISK_ENCODING) + { + // Look for whitespace before start pattern, >= 50% of width of start pattern + if (row.isRange(System.Math.Max(0, patternStart - (i - patternStart) / 2), patternStart, false)) + { + return new int[]{patternStart, i}; + } + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) + { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } + else + { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException.Instance; + } + + // For efficiency, returns -1 on failure. Not throwing here saved as many as 700 exceptions + // per image when using some of our blackbox images. + private static int toNarrowWidePattern(int[] counters) + { + int numCounters = counters.Length; + int maxNarrowCounter = 0; + int wideCounters; + do + { + int minCounter = System.Int32.MaxValue; + for (int i = 0; i < numCounters; i++) + { + int counter = counters[i]; + if (counter < minCounter && counter > maxNarrowCounter) + { + minCounter = counter; + } + } + maxNarrowCounter = minCounter; + wideCounters = 0; + int totalWideCountersWidth = 0; + int pattern = 0; + for (int i = 0; i < numCounters; i++) + { + int counter = counters[i]; + if (counters[i] > maxNarrowCounter) + { + pattern |= 1 << (numCounters - 1 - i); + wideCounters++; + totalWideCountersWidth += counter; + } + } + if (wideCounters == 3) + { + // Found 3 wide counters, but are they close enough in width? + // We can perform a cheap, conservative check to see if any individual + // counter is more than 1.5 times the average: + for (int i = 0; i < numCounters && wideCounters > 0; i++) + { + int counter = counters[i]; + if (counters[i] > maxNarrowCounter) + { + wideCounters--; + // totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average + if ((counter << 1) >= totalWideCountersWidth) + { + return - 1; + } + } + } + return pattern; + } + } + while (wideCounters > 3); + return - 1; + } + + private static char patternToChar(int pattern) + { + for (int i = 0; i < CHARACTER_ENCODINGS.Length; i++) + { + if (CHARACTER_ENCODINGS[i] == pattern) + { + return ALPHABET[i]; + } + } + throw ReaderException.Instance; + } + + private static System.String decodeExtended(System.String encoded) + { + int length = encoded.Length; + System.Text.StringBuilder decoded = new System.Text.StringBuilder(length); + for (int i = 0; i < length; i++) + { + char c = encoded[i]; + if (c == '+' || c == '$' || c == '%' || c == '/') + { + char next = encoded[i + 1]; + char decodedChar = '\x0000'; + switch (c) + { + + case '+': + // +A to +Z map to a to z + if (next >= 'A' && next <= 'Z') + { + decodedChar = (char) (next + 32); + } + else + { + throw ReaderException.Instance; + } + break; + + case '$': + // $A to $Z map to control codes SH to SB + if (next >= 'A' && next <= 'Z') + { + decodedChar = (char) (next - 64); + } + else + { + throw ReaderException.Instance; + } + break; + + case '%': + // %A to %E map to control codes ESC to US + if (next >= 'A' && next <= 'E') + { + decodedChar = (char) (next - 38); + } + else if (next >= 'F' && next <= 'W') + { + decodedChar = (char) (next - 11); + } + else + { + throw ReaderException.Instance; + } + break; + + case '/': + // /A to /O map to ! to , and /Z maps to : + if (next >= 'A' && next <= 'O') + { + decodedChar = (char) (next - 32); + } + else if (next == 'Z') + { + decodedChar = ':'; + } + else + { + throw ReaderException.Instance; + } + break; + } + decoded.Append(decodedChar); + // bump up i again since we read two characters + i++; + } + else + { + decoded.Append(c); + } + } + return decoded.ToString(); + } + } +} diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code39Writer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code39Writer.cs new file mode 100644 index 0000000..30f4c11 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/Code39Writer.cs @@ -0,0 +1,82 @@ +/* + * Copyright 2011 ZXing authors + * + * 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. + */ +using System; +// using System.Collections; // commented by .net follower (http://dotnetfollower.com) +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +namespace com.google.zxing.oned +{ + ///

Implements decoding of the EAN-13 format.

+ /// + ///
+ /// erik.barbara@gmail.com (Erik Barbara) + /// + /// em@nerd.ocracy.org (Emanuele Aina) - Ported from ZXING Java Source + /// + public sealed class Code39Writer:UPCEANWriter + { + // public override ByteMatrix encode(string contents, BarcodeFormat format, int width, int height, Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override ByteMatrix encode(string contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + if (format != BarcodeFormat.CODE_39) { + throw new ArgumentException("Can only encode CODE_39, but got " + format); + } + return base.encode(contents, format, width, height, hints); + } + + public override sbyte[] encode(string contents) { + int length = contents.Length; + if (length > 80) { + throw new ArgumentException("Requested contents should be less than 80 digits long, but got " + length); + } + + int[] widths = new int[9]; + int codeWidth = 24 + 1 + length; + for (int i = 0; i < length; i++) { + int indexInString = Code39Reader.ALPHABET_STRING.IndexOf(contents[i]); + toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths); + for(int j = 0; j < widths.Length; j++) { + codeWidth += widths[j]; + } + } + sbyte[] result = new sbyte[codeWidth]; + toIntArray(Code39Reader.CHARACTER_ENCODINGS[39], widths); + int pos = appendPattern(result, 0, widths, 1); + int[] narrowWhite = {1}; + pos += appendPattern(result, pos, narrowWhite, 0); + //append next character to bytematrix + for(int i = length-1; i >= 0; i--) { + int indexInString = Code39Reader.ALPHABET_STRING.IndexOf(contents[i]); + toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths); + pos += appendPattern(result, pos, widths, 1); + pos += appendPattern(result, pos, narrowWhite, 0); + } + toIntArray(Code39Reader.CHARACTER_ENCODINGS[39], widths); + pos += appendPattern(result, pos, widths, 1); + return result; + } + + private static void toIntArray(int a, int[] toReturn) { + for (int i = 0; i < 9; i++) { + int temp = a & (1 << i); + toReturn[i] = (temp == 0) ? 1 : 2; + } + } + } +} diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN13Reader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN13Reader.cs new file mode 100644 index 0000000..ef96e9b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN13Reader.cs @@ -0,0 +1,154 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Implements decoding of the EAN-13 format.

+ /// + ///
+ /// dswitkin@google.com (Daniel Switkin) + /// + /// Sean Owen + /// + /// alasdair@google.com (Alasdair Mackintosh) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class EAN13Reader:UPCEANReader + { + override internal BarcodeFormat BarcodeFormat + { + get + { + return BarcodeFormat.EAN_13; + } + + } + + // For an EAN-13 barcode, the first digit is represented by the parities used + // to encode the next six digits, according to the table below. For example, + // if the barcode is 5 123456 789012 then the value of the first digit is + // signified by using odd for '1', even for '2', even for '3', odd for '4', + // odd for '5', and even for '6'. See http://en.wikipedia.org/wiki/EAN-13 + // + // Parity of next 6 digits + // Digit 0 1 2 3 4 5 + // 0 Odd Odd Odd Odd Odd Odd + // 1 Odd Odd Even Odd Even Even + // 2 Odd Odd Even Even Odd Even + // 3 Odd Odd Even Even Even Odd + // 4 Odd Even Odd Odd Even Even + // 5 Odd Even Even Odd Odd Even + // 6 Odd Even Even Even Odd Odd + // 7 Odd Even Odd Even Odd Even + // 8 Odd Even Odd Even Even Odd + // 9 Odd Even Even Odd Even Odd + // + // Note that the encoding for '0' uses the same parity as a UPC barcode. Hence + // a UPC barcode can be converted to an EAN-13 barcode by prepending a 0. + // + // The encoding is represented by the following array, which is a bit pattern + // using Odd = 0 and Even = 1. For example, 5 is represented by: + // + // Odd Even Even Odd Odd Even + // in binary: + // 0 1 1 0 0 1 == 0x19 + // + //UPGRADE_NOTE: Final was removed from the declaration of 'FIRST_DIGIT_ENCODINGS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] FIRST_DIGIT_ENCODINGS = new int[]{0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'decodeMiddleCounters '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] decodeMiddleCounters; + + public EAN13Reader() + { + decodeMiddleCounters = new int[4]; + } + + protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder resultString) + { + int[] counters = decodeMiddleCounters; + counters[0] = 0; + counters[1] = 0; + counters[2] = 0; + counters[3] = 0; + int end = row.Size; + int rowOffset = startRange[1]; + + int lgPatternFound = 0; + + for (int x = 0; x < 6 && rowOffset < end; x++) + { + int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS); + resultString.Append((char) ('0' + bestMatch % 10)); + for (int i = 0; i < counters.Length; i++) + { + rowOffset += counters[i]; + } + if (bestMatch >= 10) + { + lgPatternFound |= 1 << (5 - x); + } + } + + determineFirstDigit(resultString, lgPatternFound); + + int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN); + rowOffset = middleRange[1]; + + for (int x = 0; x < 6 && rowOffset < end; x++) + { + int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS); + resultString.Append((char) ('0' + bestMatch)); + for (int i = 0; i < counters.Length; i++) + { + rowOffset += counters[i]; + } + } + + return rowOffset; + } + + /// Based on pattern of odd-even ('L' and 'G') patterns used to encoded the explicitly-encoded + /// digits in a barcode, determines the implicitly encoded first digit and adds it to the + /// result string. + /// + /// + /// string to insert decoded first digit into + /// + /// int whose bits indicates the pattern of odd/even L/G patterns used to + /// encode digits + /// + /// ReaderException if first digit cannot be determined + private static void determineFirstDigit(System.Text.StringBuilder resultString, int lgPatternFound) + { + for (int d = 0; d < 10; d++) + { + if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) + { + // resultString.Insert(0, (char) ('0' + d)); // commented by .net follower (http://dotnetfollower.com) + resultString.Insert(0, new char[] { (char)('0' + d) } ); // added by .net follower (http://dotnetfollower.com) + return ; + } + } + throw ReaderException.Instance; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN13Writer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN13Writer.cs new file mode 100644 index 0000000..abf43e3 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN13Writer.cs @@ -0,0 +1,85 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using WriterException = com.google.zxing.WriterException; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +namespace com.google.zxing.oned +{ + + + /// This object renders an EAN13 code as a ByteMatrix 2D array of greyscale + /// values. + /// + /// + /// aripollak@gmail.com (Ari Pollak) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class EAN13Writer:UPCEANWriter + { + + private const int codeWidth = 3 + (7 * 6) + 5 + (7 * 6) + 3; // end guard + + // public override ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + if (format != BarcodeFormat.EAN_13) + { + throw new System.ArgumentException("Can only encode EAN_13, but got " + format); + } + + return base.encode(contents, format, width, height, hints); + } + + public override sbyte[] encode(System.String contents) + { + if (contents.Length != 13) + { + throw new System.ArgumentException("Requested contents should be 13 digits long, but got " + contents.Length); + } + + int firstDigit = System.Int32.Parse(contents.Substring(0, (1) - (0))); + int parities = EAN13Reader.FIRST_DIGIT_ENCODINGS[firstDigit]; + sbyte[] result = new sbyte[codeWidth]; + int pos = 0; + + pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1); + + // See {@link #EAN13Reader} for a description of how the first digit & left bars are encoded + for (int i = 1; i <= 6; i++) + { + int digit = System.Int32.Parse(contents.Substring(i, (i + 1) - (i))); + if ((parities >> (6 - i) & 1) == 1) + { + digit += 10; + } + pos += appendPattern(result, pos, UPCEANReader.L_AND_G_PATTERNS[digit], 0); + } + + pos += appendPattern(result, pos, UPCEANReader.MIDDLE_PATTERN, 0); + + for (int i = 7; i <= 12; i++) + { + int digit = System.Int32.Parse(contents.Substring(i, (i + 1) - (i))); + pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], 1); + } + pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1); + + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN8Reader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN8Reader.cs new file mode 100644 index 0000000..7fc8c0b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN8Reader.cs @@ -0,0 +1,85 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Implements decoding of the EAN-8 format.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class EAN8Reader:UPCEANReader + { + override internal BarcodeFormat BarcodeFormat + { + get + { + return BarcodeFormat.EAN_8; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'decodeMiddleCounters '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] decodeMiddleCounters; + + public EAN8Reader() + { + decodeMiddleCounters = new int[4]; + } + + protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder result) + { + int[] counters = decodeMiddleCounters; + counters[0] = 0; + counters[1] = 0; + counters[2] = 0; + counters[3] = 0; + int end = row.Size; + int rowOffset = startRange[1]; + + for (int x = 0; x < 4 && rowOffset < end; x++) + { + int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS); + result.Append((char) ('0' + bestMatch)); + for (int i = 0; i < counters.Length; i++) + { + rowOffset += counters[i]; + } + } + + int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN); + rowOffset = middleRange[1]; + + for (int x = 0; x < 4 && rowOffset < end; x++) + { + int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS); + result.Append((char) ('0' + bestMatch)); + for (int i = 0; i < counters.Length; i++) + { + rowOffset += counters[i]; + } + } + + return rowOffset; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN8Writer.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN8Writer.cs new file mode 100644 index 0000000..e82c5f0 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/EAN8Writer.cs @@ -0,0 +1,79 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using WriterException = com.google.zxing.WriterException; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +namespace com.google.zxing.oned +{ + + /// This object renders an EAN8 code as a ByteMatrix 2D array of greyscale + /// values. + /// + /// + /// aripollak@gmail.com (Ari Pollak) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class EAN8Writer:UPCEANWriter + { + + private const int codeWidth = 3 + (7 * 4) + 5 + (7 * 4) + 3; // end guard + + // public override ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + if (format != BarcodeFormat.EAN_8) + { + throw new System.ArgumentException("Can only encode EAN_8, but got " + format); + } + + return base.encode(contents, format, width, height, hints); + } + + /// a byte array of horizontal pixels (0 = white, 1 = black) + /// + public override sbyte[] encode(System.String contents) + { + if (contents.Length != 8) + { + throw new System.ArgumentException("Requested contents should be 8 digits long, but got " + contents.Length); + } + + sbyte[] result = new sbyte[codeWidth]; + int pos = 0; + + pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1); + + for (int i = 0; i <= 3; i++) + { + int digit = System.Int32.Parse(contents.Substring(i, (i + 1) - (i))); + pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], 0); + } + + pos += appendPattern(result, pos, UPCEANReader.MIDDLE_PATTERN, 0); + + for (int i = 4; i <= 7; i++) + { + int digit = System.Int32.Parse(contents.Substring(i, (i + 1) - (i))); + pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], 1); + } + pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, 1); + + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/ITFReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/ITFReader.cs new file mode 100644 index 0000000..dbc485b --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/ITFReader.cs @@ -0,0 +1,384 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Implements decoding of the ITF format.

+ /// + ///

"ITF" stands for Interleaved Two of Five. This Reader will scan ITF barcode with 6, 10 or 14 + /// digits. The checksum is optional and is not applied by this Reader. The consumer of the decoded + /// value will have to apply a checksum if required.

+ /// + ///

http://en.wikipedia.org/wiki/Interleaved_2_of_5 + /// is a great reference for Interleaved 2 of 5 information.

+ /// + ///
+ /// kevin.osullivan@sita.aero, SITA Lab. + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ITFReader:OneDReader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_AVG_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static readonly int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f); + //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_INDIVIDUAL_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static readonly int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f); + + private const int W = 3; // Pixel width of a wide line + private const int N = 1; // Pixed width of a narrow line + + //UPGRADE_NOTE: Final was removed from the declaration of 'DEFAULT_ALLOWED_LENGTHS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] DEFAULT_ALLOWED_LENGTHS = new int[]{6, 10, 14, 44}; + + // Stores the actual narrow line width of the image being decoded. + private int narrowLineWidth = - 1; + + /// Start/end guard pattern. + /// + /// Note: The end pattern is reversed because the row is reversed before + /// searching for the END_PATTERN + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'START_PATTERN '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] START_PATTERN = new int[]{N, N, N, N}; + //UPGRADE_NOTE: Final was removed from the declaration of 'END_PATTERN_REVERSED '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] END_PATTERN_REVERSED = new int[]{N, N, W}; + + /// Patterns of Wide / Narrow lines to indicate each digit + //UPGRADE_NOTE: Final was removed from the declaration of 'PATTERNS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] PATTERNS = new int[][]{new int[]{N, N, W, W, N}, new int[]{W, N, N, N, W}, new int[]{N, W, N, N, W}, new int[]{W, W, N, N, N}, new int[]{N, N, W, N, W}, new int[]{W, N, W, N, N}, new int[]{N, W, W, N, N}, new int[]{N, N, N, W, W}, new int[]{W, N, N, W, N}, new int[]{N, W, N, W, N}}; + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + // Find out where the Middle section (payload) starts & ends + int[] startRange = decodeStart(row); + int[] endRange = decodeEnd(row); + + System.Text.StringBuilder result = new System.Text.StringBuilder(20); + decodeMiddle(row, startRange[1], endRange[0], result); + System.String resultString = result.ToString(); + + int[] allowedLengths = null; + // if (hints != null) // commented by .net follower (http://dotnetfollower.com) + if (hints != null && hints.ContainsKey(DecodeHintType.ALLOWED_LENGTHS)) // added by .net follower (http://dotnetfollower.com) + { + allowedLengths = (int[]) hints[DecodeHintType.ALLOWED_LENGTHS]; + } + if (allowedLengths == null) + { + allowedLengths = DEFAULT_ALLOWED_LENGTHS; + } + + // To avoid false positives with 2D barcodes (and other patterns), make + // an assumption that the decoded string must be 6, 10 or 14 digits. + int length = resultString.Length; + bool lengthOK = false; + for (int i = 0; i < allowedLengths.Length; i++) + { + if (length == allowedLengths[i]) + { + lengthOK = true; + break; + } + } + if (!lengthOK) + { + throw ReaderException.Instance; + } + + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return new Result(resultString, null, new ResultPoint[]{new ResultPoint(startRange[1], (float) rowNumber), new ResultPoint(endRange[0], (float) rowNumber)}, BarcodeFormat.ITF); + } + + /// row of black/white values to search + /// + /// offset of start pattern + /// + /// {@link StringBuffer} to append decoded chars to + /// + /// ReaderException if decoding could not complete successfully + private static void decodeMiddle(BitArray row, int payloadStart, int payloadEnd, System.Text.StringBuilder resultString) + { + + // Digits are interleaved in pairs - 5 black lines for one digit, and the + // 5 + // interleaved white lines for the second digit. + // Therefore, need to scan 10 lines and then + // split these into two arrays + int[] counterDigitPair = new int[10]; + int[] counterBlack = new int[5]; + int[] counterWhite = new int[5]; + + while (payloadStart < payloadEnd) + { + + // Get 10 runs of black/white. + recordPattern(row, payloadStart, counterDigitPair); + // Split them into each array + for (int k = 0; k < 5; k++) + { + int twoK = k << 1; + counterBlack[k] = counterDigitPair[twoK]; + counterWhite[k] = counterDigitPair[twoK + 1]; + } + + int bestMatch = decodeDigit(counterBlack); + resultString.Append((char) ('0' + bestMatch)); + bestMatch = decodeDigit(counterWhite); + resultString.Append((char) ('0' + bestMatch)); + + for (int i = 0; i < counterDigitPair.Length; i++) + { + payloadStart += counterDigitPair[i]; + } + } + } + + /// Identify where the start of the middle / payload section starts. + /// + /// + /// row of black/white values to search + /// + /// Array, containing index of start of 'start block' and end of + /// 'start block' + /// + /// ReaderException + internal int[] decodeStart(BitArray row) + { + int endStart = skipWhiteSpace(row); + int[] startPattern = findGuardPattern(row, endStart, START_PATTERN); + + // Determine the width of a narrow line in pixels. We can do this by + // getting the width of the start pattern and dividing by 4 because its + // made up of 4 narrow lines. + this.narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2; + + validateQuietZone(row, startPattern[0]); + + return startPattern; + } + + /// The start & end patterns must be pre/post fixed by a quiet zone. This + /// zone must be at least 10 times the width of a narrow line. Scan back until + /// we either get to the start of the barcode or match the necessary number of + /// quiet zone pixels. + /// + /// Note: Its assumed the row is reversed when using this method to find + /// quiet zone after the end pattern. + /// + /// ref: http://www.barcode-1.net/i25code.html + /// + /// + /// bit array representing the scanned barcode. + /// + /// index into row of the start or end pattern. + /// + /// ReaderException if the quiet zone cannot be found, a ReaderException is thrown. + private void validateQuietZone(BitArray row, int startPattern) + { + + int quietCount = this.narrowLineWidth * 10; // expect to find this many pixels of quiet zone + + for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) + { + if (row.get_Renamed(i)) + { + break; + } + quietCount--; + } + if (quietCount != 0) + { + // Unable to find the necessary number of quiet zone pixels. + throw ReaderException.Instance; + } + } + + /// Skip all whitespace until we get to the first black line. + /// + /// + /// row of black/white values to search + /// + /// index of the first black line. + /// + /// ReaderException Throws exception if no black lines are found in the row + private static int skipWhiteSpace(BitArray row) + { + int width = row.Size; + int endStart = 0; + while (endStart < width) + { + if (row.get_Renamed(endStart)) + { + break; + } + endStart++; + } + if (endStart == width) + { + throw ReaderException.Instance; + } + + return endStart; + } + + /// Identify where the end of the middle / payload section ends. + /// + /// + /// row of black/white values to search + /// + /// Array, containing index of start of 'end block' and end of 'end + /// block' + /// + /// ReaderException + + internal int[] decodeEnd(BitArray row) + { + + // For convenience, reverse the row and then + // search from 'the start' for the end block + row.reverse(); + try + { + int endStart = skipWhiteSpace(row); + int[] endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED); + + // The start & end patterns must be pre/post fixed by a quiet zone. This + // zone must be at least 10 times the width of a narrow line. + // ref: http://www.barcode-1.net/i25code.html + validateQuietZone(row, endPattern[0]); + + // Now recalculate the indices of where the 'endblock' starts & stops to + // accommodate + // the reversed nature of the search + int temp = endPattern[0]; + endPattern[0] = row.Size - endPattern[1]; + endPattern[1] = row.Size - temp; + + return endPattern; + } + finally + { + // Put the row back the right way. + row.reverse(); + } + } + + /// row of black/white values to search + /// + /// position to start search + /// + /// pattern of counts of number of black and white pixels that are + /// being searched for as a pattern + /// + /// start/end horizontal offset of guard pattern, as an array of two + /// ints + /// + /// ReaderException if pattern is not found + private static int[] findGuardPattern(BitArray row, int rowOffset, int[] pattern) + { + + // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be + // merged to a single method. + int patternLength = pattern.Length; + int[] counters = new int[patternLength]; + int width = row.Size; + bool isWhite = false; + + int counterPosition = 0; + int patternStart = rowOffset; + for (int x = rowOffset; x < width; x++) + { + bool pixel = row.get_Renamed(x); + if (pixel ^ isWhite) + { + counters[counterPosition]++; + } + else + { + if (counterPosition == patternLength - 1) + { + if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) + { + return new int[]{patternStart, x}; + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) + { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } + else + { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException.Instance; + } + + /// Attempts to decode a sequence of ITF black/white lines into single + /// digit. + /// + /// + /// the counts of runs of observed black/white/black/... values + /// + /// The decoded digit + /// + /// ReaderException if digit cannot be decoded + private static int decodeDigit(int[] counters) + { + + int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = - 1; + int max = PATTERNS.Length; + for (int i = 0; i < max; i++) + { + int[] pattern = PATTERNS[i]; + int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) + { + bestVariance = variance; + bestMatch = i; + } + } + if (bestMatch >= 0) + { + return bestMatch; + } + else + { + throw ReaderException.Instance; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/MultiFormatOneDReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/MultiFormatOneDReader.cs new file mode 100644 index 0000000..70bc9af --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/MultiFormatOneDReader.cs @@ -0,0 +1,99 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + /// dswitkin@google.com (Daniel Switkin) + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class MultiFormatOneDReader:OneDReader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'readers '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + // private System.Collections.ArrayList readers; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.List readers; // added by .net follower (http://dotnetfollower.com) + + // public MultiFormatOneDReader(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public MultiFormatOneDReader(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + //System.Collections.ArrayList possibleFormats = hints == null?null:(System.Collections.ArrayList) hints[DecodeHintType.POSSIBLE_FORMATS]; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List possibleFormats = null; // added by .net follower (http://dotnetfollower.com) + if (hints != null && hints.ContainsKey(DecodeHintType.POSSIBLE_FORMATS)) // added by .net follower (http://dotnetfollower.com) + possibleFormats = (System.Collections.Generic.List)hints[DecodeHintType.POSSIBLE_FORMATS]; // added by .net follower (http://dotnetfollower.com) + + // bool useCode39CheckDigit = hints != null && hints[DecodeHintType.ASSUME_CODE_39_CHECK_DIGIT] != null; // commented by .net follower (http://dotnetfollower.com) + bool useCode39CheckDigit = hints != null && hints.ContainsKey(DecodeHintType.ASSUME_CODE_39_CHECK_DIGIT); // added by .net follower (http://dotnetfollower.com) + + // readers = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + readers = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + if (possibleFormats != null) + { + if (possibleFormats.Contains(BarcodeFormat.EAN_13) || possibleFormats.Contains(BarcodeFormat.UPC_A) || possibleFormats.Contains(BarcodeFormat.EAN_8) || possibleFormats.Contains(BarcodeFormat.UPC_E)) + { + readers.Add(new MultiFormatUPCEANReader(hints)); + } + if (possibleFormats.Contains(BarcodeFormat.CODE_39)) + { + readers.Add(new Code39Reader(useCode39CheckDigit)); + } + if (possibleFormats.Contains(BarcodeFormat.CODE_128)) + { + readers.Add(new Code128Reader()); + } + if (possibleFormats.Contains(BarcodeFormat.ITF)) + { + readers.Add(new ITFReader()); + } + } + if ((readers.Count == 0)) + { + readers.Add(new MultiFormatUPCEANReader(hints)); + readers.Add(new Code39Reader()); + readers.Add(new Code128Reader()); + readers.Add(new ITFReader()); + } + } + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + int size = readers.Count; + for (int i = 0; i < size; i++) + { + OneDReader reader = (OneDReader) readers[i]; + try + { + return reader.decodeRow(rowNumber, row, hints); + } + catch (ReaderException re) + { + // continue + } + } + + throw ReaderException.Instance; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/MultiFormatUPCEANReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/MultiFormatUPCEANReader.cs new file mode 100644 index 0000000..fec34ec --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/MultiFormatUPCEANReader.cs @@ -0,0 +1,117 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

A reader that can read all available UPC/EAN formats. If a caller wants to try to + /// read all such formats, it is most efficient to use this implementation rather than invoke + /// individual readers.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class MultiFormatUPCEANReader:OneDReader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'readers '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + // private System.Collections.ArrayList readers; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.List readers; // added by .net follower (http://dotnetfollower.com) + + // public MultiFormatUPCEANReader(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public MultiFormatUPCEANReader(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + // System.Collections.ArrayList possibleFormats = hints == null?null:(System.Collections.ArrayList) hints[DecodeHintType.POSSIBLE_FORMATS]; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List possibleFormats = null; // added by .net follower (http://dotnetfollower.com) + if (hints != null && hints.ContainsKey(DecodeHintType.POSSIBLE_FORMATS)) // added by .net follower (http://dotnetfollower.com) + possibleFormats = (System.Collections.Generic.List)hints[DecodeHintType.POSSIBLE_FORMATS]; // added by .net follower (http://dotnetfollower.com) + + // readers = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + readers = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + if (possibleFormats != null) + { + if (possibleFormats.Contains(BarcodeFormat.EAN_13)) + { + readers.Add(new EAN13Reader()); + } + else if (possibleFormats.Contains(BarcodeFormat.UPC_A)) + { + readers.Add(new UPCAReader()); + } + if (possibleFormats.Contains(BarcodeFormat.EAN_8)) + { + readers.Add(new EAN8Reader()); + } + if (possibleFormats.Contains(BarcodeFormat.UPC_E)) + { + readers.Add(new UPCEReader()); + } + } + if ((readers.Count == 0)) + { + readers.Add(new EAN13Reader()); + // UPC-A is covered by EAN-13 + readers.Add(new EAN8Reader()); + readers.Add(new UPCEReader()); + } + } + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + // Compute this location once and reuse it on multiple implementations + int[] startGuardPattern = UPCEANReader.findStartGuardPattern(row); + int size = readers.Count; + for (int i = 0; i < size; i++) + { + UPCEANReader reader = (UPCEANReader) readers[i]; + Result result; + try + { + result = reader.decodeRow(rowNumber, row, startGuardPattern, hints); + } + catch (ReaderException re) + { + continue; + } + // Special case: a 12-digit code encoded in UPC-A is identical to a "0" + // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, + // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". + // Individually these are correct and their readers will both read such a code + // and correctly call it EAN-13, or UPC-A, respectively. + // + // In this case, if we've been looking for both types, we'd like to call it + // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read + // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A + // result if appropriate. + if (result.BarcodeFormat.Equals(BarcodeFormat.EAN_13) && result.Text[0] == '0') + { + return new Result(result.Text.Substring(1), null, result.ResultPoints, BarcodeFormat.UPC_A); + } + return result; + } + + throw ReaderException.Instance; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/OneDReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/OneDReader.cs new file mode 100644 index 0000000..49c1384 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/OneDReader.cs @@ -0,0 +1,337 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using DecodeHintType = com.google.zxing.DecodeHintType; +using Reader = com.google.zxing.Reader; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultMetadataType = com.google.zxing.ResultMetadataType; +using ResultPoint = com.google.zxing.ResultPoint; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + /// Encapsulates functionality and implementation that is common to all families + /// of one-dimensional barcodes. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class OneDReader : Reader + { + + private const int INTEGER_MATH_SHIFT = 8; + //UPGRADE_NOTE: Final was removed from the declaration of 'PATTERN_MATCH_RESULT_SCALE_FACTOR '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT; + + public virtual Result decode(BinaryBitmap image) + { + return decode(image, null); + } + + // Note that we don't try rotation without the try harder flag, even if rotation was supported. + // public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public virtual Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + try + { + return doDecode(image, hints); + } + catch (ReaderException re) + { + bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); + if (tryHarder && image.RotateSupported) + { + BinaryBitmap rotatedImage = image.rotateCounterClockwise(); + Result result = doDecode(rotatedImage, hints); + // Record that we found it rotated 90 degrees CCW / 270 degrees CW + // System.Collections.Hashtable metadata = result.ResultMetadata; // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.Dictionary metadata = result.ResultMetadata; // added by .net follower (http://dotnetfollower.com) + int orientation = 270; + if (metadata != null && metadata.ContainsKey(ResultMetadataType.ORIENTATION)) + { + // But if we found it reversed in doDecode(), add in that result here: + orientation = (orientation + ((System.Int32) metadata[ResultMetadataType.ORIENTATION])) % 360; + } + result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) orientation); + // Update result points + ResultPoint[] points = result.ResultPoints; + int height = rotatedImage.Height; + for (int i = 0; i < points.Length; i++) + { + points[i] = new ResultPoint(height - points[i].Y - 1, points[i].X); + } + return result; + } + else + { + throw re; + } + } + } + + /// We're going to examine rows from the middle outward, searching alternately above and below the + /// middle, and farther out each time. rowStep is the number of rows between each successive + /// attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then + /// middle + rowStep, then middle - (2 * rowStep), etc. + /// rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily + /// decided that moving up and down by about 1/16 of the image is pretty good; we try more of the + /// image if "trying harder". + /// + /// + /// The image to decode + /// + /// Any hints that were requested + /// + /// The contents of the decoded barcode + /// + /// ReaderException Any spontaneous errors which occur + // private Result doDecode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + private Result doDecode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + int width = image.Width; + int height = image.Height; + BitArray row = new BitArray(width); + + int middle = height >> 1; + bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); + int rowStep = System.Math.Max(1, height >> (tryHarder?7:4)); + int maxLines; + if (tryHarder) + { + maxLines = height; // Look at the whole image, not just the center + } + else + { + maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image + } + + for (int x = 0; x < maxLines; x++) + { + + // Scanning from the middle out. Determine which row we're looking at next: + int rowStepsAboveOrBelow = (x + 1) >> 1; + bool isAbove = (x & 0x01) == 0; // i.e. is x even? + int rowNumber = middle + rowStep * (isAbove?rowStepsAboveOrBelow:- rowStepsAboveOrBelow); + if (rowNumber < 0 || rowNumber >= height) + { + // Oops, if we run off the top or bottom, stop + break; + } + + // Estimate black point for this row and load it: + try + { + row = image.getBlackRow(rowNumber, row); + } + catch (ReaderException re) + { + continue; + } + + // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to + // handle decoding upside down barcodes. + for (int attempt = 0; attempt < 2; attempt++) + { + if (attempt == 1) + { + // trying again? + row.reverse(); // reverse the row and continue + // This means we will only ever draw result points *once* in the life of this method + // since we want to avoid drawing the wrong points after flipping the row, and, + // don't want to clutter with noise from every single row scan -- just the scans + // that start on the center line. + if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) + { + // System.Collections.Hashtable newHints = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable()); // Can't use clone() in J2ME // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.Dictionary newHints = new System.Collections.Generic.Dictionary(); // Can't use clone() in J2ME // added by .net follower (http://dotnetfollower.com) + System.Collections.IEnumerator hintEnum = hints.Keys.GetEnumerator(); + //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'" + while (hintEnum.MoveNext()) + { + //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'" + System.Object key = hintEnum.Current; + if (!key.Equals(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) + { + newHints[key] = hints[key]; + } + } + hints = newHints; + } + } + try + { + // Look for a barcode + Result result = decodeRow(rowNumber, row, hints); + // We found our barcode + if (attempt == 1) + { + // But it was upside down, so note that + result.putMetadata(ResultMetadataType.ORIENTATION, (System.Object) 180); + // And remember to flip the result points horizontally. + ResultPoint[] points = result.ResultPoints; + points[0] = new ResultPoint(width - points[0].X - 1, points[0].Y); + points[1] = new ResultPoint(width - points[1].X - 1, points[1].Y); + } + return result; + } + catch (ReaderException re) + { + // continue -- just couldn't decode this row + } + } + } + + throw ReaderException.Instance; + } + + /// Records the size of successive runs of white and black pixels in a row, starting at a given point. + /// The values are recorded in the given array, and the number of runs recorded is equal to the size + /// of the array. If the row starts on a white pixel at the given start point, then the first count + /// recorded is the run of white pixels starting from that point; likewise it is the count of a run + /// of black pixels if the row begin on a black pixels at that point. + /// + /// + /// row to count from + /// + /// offset into row to start at + /// + /// array into which to record counts + /// + /// ReaderException if counters cannot be filled entirely from row before running out + /// of pixels + /// + internal static void recordPattern(BitArray row, int start, int[] counters) + { + int numCounters = counters.Length; + for (int i = 0; i < numCounters; i++) + { + counters[i] = 0; + } + int end = row.Size; + if (start >= end) + { + throw ReaderException.Instance; + } + bool isWhite = !row.get_Renamed(start); + int counterPosition = 0; + int i2 = start; + while (i2 < end) + { + bool pixel = row.get_Renamed(i2); + if (pixel ^ isWhite) + { + // that is, exactly one is true + counters[counterPosition]++; + } + else + { + counterPosition++; + if (counterPosition == numCounters) + { + break; + } + else + { + counters[counterPosition] = 1; + isWhite ^= true; // isWhite = !isWhite; + } + } + i2++; + } + // If we read fully the last section of pixels and filled up our counters -- or filled + // the last counter but ran off the side of the image, OK. Otherwise, a problem. + if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i2 == end))) + { + throw ReaderException.Instance; + } + } + + /// Determines how closely a set of observed counts of runs of black/white values matches a given + /// target pattern. This is reported as the ratio of the total variance from the expected pattern + /// proportions across all pattern elements, to the length of the pattern. + /// + /// + /// observed counters + /// + /// expected pattern + /// + /// The most any counter can differ before we give up + /// + /// ratio of total variance between counters and pattern compared to total pattern size, + /// where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means + /// the total variance between counters and patterns equals the pattern length, higher values mean + /// even more variance + /// + internal static int patternMatchVariance(int[] counters, int[] pattern, int maxIndividualVariance) + { + int numCounters = counters.Length; + int total = 0; + int patternLength = 0; + for (int i = 0; i < numCounters; i++) + { + total += counters[i]; + patternLength += pattern[i]; + } + if (total < patternLength) + { + // If we don't even have one pixel per unit of bar width, assume this is too small + // to reliably match, so fail: + return System.Int32.MaxValue; + } + // We're going to fake floating-point math in integers. We just need to use more bits. + // Scale up patternLength so that intermediate values below like scaledCounter will have + // more "significant digits" + int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength; + maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT; + + int totalVariance = 0; + for (int x = 0; x < numCounters; x++) + { + int counter = counters[x] << INTEGER_MATH_SHIFT; + int scaledPattern = pattern[x] * unitBarWidth; + int variance = counter > scaledPattern?counter - scaledPattern:scaledPattern - counter; + if (variance > maxIndividualVariance) + { + return System.Int32.MaxValue; + } + totalVariance += variance; + } + return totalVariance / total; + } + + ///

Attempts to decode a one-dimensional barcode format given a single row of + /// an image.

+ /// + ///
+ /// row number from top of the row + /// + /// the black/white pixel data of the row + /// + /// decode hints + /// + /// {@link Result} containing encoded string and start/end of barcode + /// + /// ReaderException if an error occurs or barcode cannot be found + // public abstract Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints); // commented by .net follower (http://dotnetfollower.com) + public abstract Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints); // added by .net follower (http://dotnetfollower.com) + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCAReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCAReader.cs new file mode 100644 index 0000000..5466670 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCAReader.cs @@ -0,0 +1,89 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Implements decoding of the UPC-A format.

+ /// + ///
+ /// dswitkin@google.com (Daniel Switkin) + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class UPCAReader:UPCEANReader + { + override internal BarcodeFormat BarcodeFormat + { + get + { + return BarcodeFormat.UPC_A; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'ean13Reader '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private UPCEANReader ean13Reader = new EAN13Reader(); + + // public override Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange, hints)); + } + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, hints)); + } + + public override Result decode(BinaryBitmap image) + { + return maybeReturnResult(ean13Reader.decode(image)); + } + + // public override Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + return maybeReturnResult(ean13Reader.decode(image, hints)); + } + + protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder resultString) + { + return ean13Reader.decodeMiddle(row, startRange, resultString); + } + + private static Result maybeReturnResult(Result result) + { + System.String text = result.Text; + if (text[0] == '0') + { + return new Result(text.Substring(1), null, result.ResultPoints, BarcodeFormat.UPC_A); + } + else + { + throw ReaderException.Instance; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEANReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEANReader.cs new file mode 100644 index 0000000..2978f70 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEANReader.cs @@ -0,0 +1,364 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPointCallback = com.google.zxing.ResultPointCallback; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ResultPoint = com.google.zxing.ResultPoint; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Encapsulates functionality and implementation that is common to UPC and EAN families + /// of one-dimensional barcodes.

+ /// + ///
+ /// dswitkin@google.com (Daniel Switkin) + /// + /// Sean Owen + /// + /// alasdair@google.com (Alasdair Mackintosh) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class UPCEANReader:OneDReader + { + /// Get the format of this decoder. + /// + /// + /// The 1D format. + /// + internal abstract BarcodeFormat BarcodeFormat{get;} + + // These two values are critical for determining how permissive the decoding will be. + // We've arrived at these values through a lot of trial and error. Setting them any higher + // lets false positives creep in quickly. + //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_AVG_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static readonly int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f); + //UPGRADE_NOTE: Final was removed from the declaration of 'MAX_INDIVIDUAL_VARIANCE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static readonly int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f); + + /// Start/end guard pattern. + //UPGRADE_NOTE: Final was removed from the declaration of 'START_END_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] START_END_PATTERN = new int[]{1, 1, 1}; + + /// Pattern marking the middle of a UPC/EAN pattern, separating the two halves. + //UPGRADE_NOTE: Final was removed from the declaration of 'MIDDLE_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] MIDDLE_PATTERN = new int[]{1, 1, 1, 1, 1}; + + /// "Odd", or "L" patterns used to encode UPC/EAN digits. + //UPGRADE_NOTE: Final was removed from the declaration of 'L_PATTERNS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[][] L_PATTERNS = new int[][]{new int[]{3, 2, 1, 1}, new int[]{2, 2, 2, 1}, new int[]{2, 1, 2, 2}, new int[]{1, 4, 1, 1}, new int[]{1, 1, 3, 2}, new int[]{1, 2, 3, 1}, new int[]{1, 1, 1, 4}, new int[]{1, 3, 1, 2}, new int[]{1, 2, 1, 3}, new int[]{3, 1, 1, 2}}; + + /// As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. + internal static int[][] L_AND_G_PATTERNS; + + //UPGRADE_NOTE: Final was removed from the declaration of 'decodeRowStringBuffer '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.Text.StringBuilder decodeRowStringBuffer; + + protected internal UPCEANReader() + { + decodeRowStringBuffer = new System.Text.StringBuilder(20); + } + + internal static int[] findStartGuardPattern(BitArray row) + { + bool foundStart = false; + int[] startRange = null; + int nextStart = 0; + while (!foundStart) + { + startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN); + int start = startRange[0]; + nextStart = startRange[1]; + // Make sure there is a quiet zone at least as big as the start pattern before the barcode. + // If this check would run off the left edge of the image, do not accept this barcode, + // as it is very likely to be a false positive. + int quietStart = start - (nextStart - start); + if (quietStart >= 0) + { + foundStart = row.isRange(quietStart, start, false); + } + } + return startRange; + } + + // public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public override Result decodeRow(int rowNumber, BitArray row, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + return decodeRow(rowNumber, row, findStartGuardPattern(row), hints); + } + + ///

Like {@link #decodeRow(int, BitArray, java.util.Hashtable)}, but + /// allows caller to inform method about where the UPC/EAN start pattern is + /// found. This allows this to be computed once and reused across many implementations.

+ ///
+ // public virtual Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public virtual Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + // ResultPointCallback resultPointCallback = hints == null?null:(ResultPointCallback) hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]; // commented by .net follower (http://dotnetfollower.com) + ResultPointCallback resultPointCallback = null; // added by .net follower (http://dotnetfollower.com) + if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) // added by .net follower (http://dotnetfollower.com) + resultPointCallback = (ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]; // added by .net follower (http://dotnetfollower.com) + + if (resultPointCallback != null) + { + resultPointCallback.foundPossibleResultPoint(new ResultPoint((startGuardRange[0] + startGuardRange[1]) / 2.0f, rowNumber)); + } + + System.Text.StringBuilder result = decodeRowStringBuffer; + result.Length = 0; + int endStart = decodeMiddle(row, startGuardRange, result); + + if (resultPointCallback != null) + { + resultPointCallback.foundPossibleResultPoint(new ResultPoint(endStart, rowNumber)); + } + + int[] endRange = decodeEnd(row, endStart); + + if (resultPointCallback != null) + { + resultPointCallback.foundPossibleResultPoint(new ResultPoint((endRange[0] + endRange[1]) / 2.0f, rowNumber)); + } + + + // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The + // spec might want more whitespace, but in practice this is the maximum we can count on. + int end = endRange[1]; + int quietEnd = end + (end - endRange[0]); + if (quietEnd >= row.Size || !row.isRange(end, quietEnd, false)) + { + throw ReaderException.Instance; + } + + System.String resultString = result.ToString(); + if (!checkChecksum(resultString)) + { + throw ReaderException.Instance; + } + + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float right = (float) (endRange[1] + endRange[0]) / 2.0f; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return new Result(resultString, null, new ResultPoint[]{new ResultPoint(left, (float) rowNumber), new ResultPoint(right, (float) rowNumber)}, BarcodeFormat); + } + + /// {@link #checkStandardUPCEANChecksum(String)} + /// + //UPGRADE_NOTE: Access modifiers of method 'checkChecksum' were changed to 'protected'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1204'" + protected internal virtual bool checkChecksum(System.String s) + { + return checkStandardUPCEANChecksum(s); + } + + /// Computes the UPC/EAN checksum on a string of digits, and reports + /// whether the checksum is correct or not. + /// + /// + /// string of digits to check + /// + /// true iff string of digits passes the UPC/EAN checksum algorithm + /// + /// ReaderException if the string does not contain only digits + private static bool checkStandardUPCEANChecksum(System.String s) + { + int length = s.Length; + if (length == 0) + { + return false; + } + + int sum = 0; + for (int i = length - 2; i >= 0; i -= 2) + { + int digit = (int) s[i] - (int) '0'; + if (digit < 0 || digit > 9) + { + throw ReaderException.Instance; + } + sum += digit; + } + sum *= 3; + for (int i = length - 1; i >= 0; i -= 2) + { + int digit = (int) s[i] - (int) '0'; + if (digit < 0 || digit > 9) + { + throw ReaderException.Instance; + } + sum += digit; + } + return sum % 10 == 0; + } + + //UPGRADE_NOTE: Access modifiers of method 'decodeEnd' were changed to 'protected'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1204'" + protected internal virtual int[] decodeEnd(BitArray row, int endStart) + { + return findGuardPattern(row, endStart, false, START_END_PATTERN); + } + + /// row of black/white values to search + /// + /// position to start search + /// + /// if true, indicates that the pattern specifies white/black/white/... + /// pixel counts, otherwise, it is interpreted as black/white/black/... + /// + /// pattern of counts of number of black and white pixels that are being + /// searched for as a pattern + /// + /// start/end horizontal offset of guard pattern, as an array of two ints + /// + /// ReaderException if pattern is not found + internal static int[] findGuardPattern(BitArray row, int rowOffset, bool whiteFirst, int[] pattern) + { + int patternLength = pattern.Length; + int[] counters = new int[patternLength]; + int width = row.Size; + bool isWhite = false; + while (rowOffset < width) + { + isWhite = !row.get_Renamed(rowOffset); + if (whiteFirst == isWhite) + { + break; + } + rowOffset++; + } + + int counterPosition = 0; + int patternStart = rowOffset; + for (int x = rowOffset; x < width; x++) + { + bool pixel = row.get_Renamed(x); + if (pixel ^ isWhite) + { + counters[counterPosition]++; + } + else + { + if (counterPosition == patternLength - 1) + { + if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) + { + return new int[]{patternStart, x}; + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) + { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } + else + { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException.Instance; + } + + /// Attempts to decode a single UPC/EAN-encoded digit. + /// + /// + /// row of black/white values to decode + /// + /// the counts of runs of observed black/white/black/... values + /// + /// horizontal offset to start decoding from + /// + /// the set of patterns to use to decode -- sometimes different encodings + /// for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should + /// be used + /// + /// horizontal offset of first pixel beyond the decoded digit + /// + /// ReaderException if digit cannot be decoded + internal static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns) + { + recordPattern(row, rowOffset, counters); + int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = - 1; + int max = patterns.Length; + for (int i = 0; i < max; i++) + { + int[] pattern = patterns[i]; + int variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) + { + bestVariance = variance; + bestMatch = i; + } + } + if (bestMatch >= 0) + { + return bestMatch; + } + else + { + throw ReaderException.Instance; + } + } + + /// Subclasses override this to decode the portion of a barcode between the start + /// and end guard patterns. + /// + /// + /// row of black/white values to search + /// + /// start/end offset of start guard pattern + /// + /// {@link StringBuffer} to append decoded chars to + /// + /// horizontal offset of first pixel after the "middle" that was decoded + /// + /// ReaderException if decoding could not complete successfully + protected internal abstract int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder resultString); + static UPCEANReader() + { + { + L_AND_G_PATTERNS = new int[20][]; + for (int i = 0; i < 10; i++) + { + L_AND_G_PATTERNS[i] = L_PATTERNS[i]; + } + for (int i = 10; i < 20; i++) + { + int[] widths = L_PATTERNS[i - 10]; + int[] reversedWidths = new int[widths.Length]; + for (int j = 0; j < widths.Length; j++) + { + reversedWidths[j] = widths[widths.Length - j - 1]; + } + L_AND_G_PATTERNS[i] = reversedWidths; + } + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEANWriter.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEANWriter.cs new file mode 100644 index 0000000..fa009a0 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEANWriter.cs @@ -0,0 +1,146 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using Writer = com.google.zxing.Writer; +using WriterException = com.google.zxing.WriterException; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +namespace com.google.zxing.oned +{ + + ///

Encapsulates functionality and implementation that is common to UPC and EAN families + /// of one-dimensional barcodes.

+ /// + ///
+ /// aripollak@gmail.com (Ari Pollak) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public abstract class UPCEANWriter : Writer + { + + public virtual ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height) + { + return encode(contents, format, width, height, null); + } + + // public virtual ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public virtual ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + if (contents == null || contents.Length == 0) + { + throw new System.ArgumentException("Found empty contents"); + } + + if (width < 0 || height < 0) + { + throw new System.ArgumentException("Requested dimensions are too small: " + width + 'x' + height); + } + + sbyte[] code = encode(contents); + return renderResult(code, width, height); + } + + /// a byte array of horizontal pixels (0 = white, 1 = black) + /// + private static ByteMatrix renderResult(sbyte[] code, int width, int height) + { + int inputWidth = code.Length; + // Add quiet zone on both sides + int fullWidth = inputWidth + (UPCEANReader.START_END_PATTERN.Length << 1); + int outputWidth = System.Math.Max(width, fullWidth); + int outputHeight = System.Math.Max(1, height); + + int multiple = outputWidth / fullWidth; + int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; + + ByteMatrix output = new ByteMatrix(outputWidth, outputHeight); + sbyte[][] outputArray = output.Array; + + sbyte[] row = new sbyte[outputWidth]; + + // a. Write the white pixels at the left of each row + for (int x = 0; x < leftPadding; x++) + { + row[x] = (sbyte) SupportClass.Identity(255); + } + + // b. Write the contents of this row of the barcode + int offset = leftPadding; + for (int x = 0; x < inputWidth; x++) + { + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // type cased 0 with sbyte + sbyte value_Renamed = (code[x] == 1) ? (sbyte)0 : (sbyte)SupportClass.Identity(255); + for (int z = 0; z < multiple; z++) + { + row[offset + z] = value_Renamed; + } + offset += multiple; + } + + // c. Write the white pixels at the right of each row + offset = leftPadding + (inputWidth * multiple); + for (int x = offset; x < outputWidth; x++) + { + row[x] = (sbyte) SupportClass.Identity(255); + } + + // d. Write the completed row multiple times + for (int z = 0; z < outputHeight; z++) + { + Array.Copy(row, 0, outputArray[z], 0, outputWidth); + } + + return output; + } + + + /// Appends the given pattern to the target array starting at pos. + /// + /// + /// starting color - 0 for white, 1 for black + /// + /// the number of elements added to target. + /// + protected internal static int appendPattern(sbyte[] target, int pos, int[] pattern, int startColor) + { + if (startColor != 0 && startColor != 1) + { + throw new System.ArgumentException("startColor must be either 0 or 1, but got: " + startColor); + } + + sbyte color = (sbyte) startColor; + int numAdded = 0; + for (int i = 0; i < pattern.Length; i++) + { + for (int j = 0; j < pattern[i]; j++) + { + target[pos] = color; + pos += 1; + numAdded += 1; + } + color ^= 1; // flip color after each segment + } + return numAdded; + } + + /// a byte array of horizontal pixels (0 = white, 1 = black) + /// + public abstract sbyte[] encode(System.String contents); + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEReader.cs new file mode 100644 index 0000000..a01d342 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/oned/UPCEReader.cs @@ -0,0 +1,174 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using ReaderException = com.google.zxing.ReaderException; +using BitArray = com.google.zxing.common.BitArray; +namespace com.google.zxing.oned +{ + + ///

Implements decoding of the UPC-E format.

+ ///

+ ///

This is a great reference for + /// UPC-E information.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class UPCEReader:UPCEANReader + { + override internal BarcodeFormat BarcodeFormat + { + get + { + return BarcodeFormat.UPC_E; + } + + } + + /// The pattern that marks the middle, and end, of a UPC-E pattern. + /// There is no "second half" to a UPC-E barcode. + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'MIDDLE_END_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] MIDDLE_END_PATTERN = new int[]{1, 1, 1, 1, 1, 1}; + + /// See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of + /// even-odd parity encodings of digits that imply both the number system (0 or 1) + /// used, and the check digit. + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'NUMSYS_AND_CHECK_DIGIT_PATTERNS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] NUMSYS_AND_CHECK_DIGIT_PATTERNS = new int[][]{new int[]{0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25}, new int[]{0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'decodeMiddleCounters '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] decodeMiddleCounters; + + public UPCEReader() + { + decodeMiddleCounters = new int[4]; + } + + protected internal override int decodeMiddle(BitArray row, int[] startRange, System.Text.StringBuilder result) + { + int[] counters = decodeMiddleCounters; + counters[0] = 0; + counters[1] = 0; + counters[2] = 0; + counters[3] = 0; + int end = row.Size; + int rowOffset = startRange[1]; + + int lgPatternFound = 0; + + for (int x = 0; x < 6 && rowOffset < end; x++) + { + int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS); + result.Append((char) ('0' + bestMatch % 10)); + for (int i = 0; i < counters.Length; i++) + { + rowOffset += counters[i]; + } + if (bestMatch >= 10) + { + lgPatternFound |= 1 << (5 - x); + } + } + + determineNumSysAndCheckDigit(result, lgPatternFound); + + return rowOffset; + } + + protected internal override int[] decodeEnd(BitArray row, int endStart) + { + return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN); + } + + protected internal override bool checkChecksum(System.String s) + { + return base.checkChecksum(convertUPCEtoUPCA(s)); + } + + private static void determineNumSysAndCheckDigit(System.Text.StringBuilder resultString, int lgPatternFound) + { + + for (int numSys = 0; numSys <= 1; numSys++) + { + for (int d = 0; d < 10; d++) + { + if (lgPatternFound == NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) + { + // resultString.Insert(0, (char) ('0' + numSys)); // commented by .net follower (http://dotnetfollower.com) + resultString.Insert(0, new char[] { (char)('0' + numSys) } ); // added by .net follower (http://dotnetfollower.com) + resultString.Append((char) ('0' + d)); + return ; + } + } + } + throw ReaderException.Instance; + } + + /// Expands a UPC-E value back into its full, equivalent UPC-A code value. + /// + /// + /// UPC-E code as string of digits + /// + /// equivalent UPC-A code as string of digits + /// + public static System.String convertUPCEtoUPCA(System.String upce) + { + char[] upceChars = new char[6]; + SupportClass.GetCharsFromString(upce, 1, 7, upceChars, 0); + System.Text.StringBuilder result = new System.Text.StringBuilder(12); + result.Append(upce[0]); + char lastChar = upceChars[5]; + switch (lastChar) + { + + case '0': + case '1': + case '2': + result.Append(upceChars, 0, 2); + result.Append(lastChar); + result.Append("0000"); + result.Append(upceChars, 2, 3); + break; + + case '3': + result.Append(upceChars, 0, 3); + result.Append("00000"); + result.Append(upceChars, 3, 2); + break; + + case '4': + result.Append(upceChars, 0, 4); + result.Append("00000"); + result.Append(upceChars[4]); + break; + + default: + result.Append(upceChars, 0, 5); + result.Append("0000"); + result.Append(lastChar); + break; + + } + result.Append(upce[7]); + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/PDF417Reader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/PDF417Reader.cs new file mode 100644 index 0000000..0de9232 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/PDF417Reader.cs @@ -0,0 +1,162 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using DecodeHintType = com.google.zxing.DecodeHintType; +using Reader = com.google.zxing.Reader; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DecoderResult = com.google.zxing.common.DecoderResult; +using DetectorResult = com.google.zxing.common.DetectorResult; +using Decoder = com.google.zxing.pdf417.decoder.Decoder; +using Detector = com.google.zxing.pdf417.detector.Detector; +namespace com.google.zxing.pdf417 +{ + + /// This implementation can detect and decode PDF417 codes in an image. + /// + /// + /// SITA Lab (kevin.osullivan@sita.aero) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class PDF417Reader : Reader + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'NO_POINTS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly ResultPoint[] NO_POINTS = new ResultPoint[0]; + + //UPGRADE_NOTE: Final was removed from the declaration of 'decoder '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private Decoder decoder = new Decoder(); + + /// Locates and decodes a PDF417 code in an image. + /// + /// + /// a String representing the content encoded by the PDF417 code + /// + /// ReaderException if a PDF417 code cannot be found, or cannot be decoded + public Result decode(BinaryBitmap image) + { + return decode(image, null); + } + + // public Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + DecoderResult decoderResult; + ResultPoint[] points; + if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE)) + { + BitMatrix bits = extractPureBits(image); + decoderResult = decoder.decode(bits); + points = NO_POINTS; + } + else + { + DetectorResult detectorResult = new Detector(image).detect(); + decoderResult = decoder.decode(detectorResult.Bits); + points = detectorResult.Points; + } + return new Result(decoderResult.Text, decoderResult.RawBytes, points, BarcodeFormat.PDF417); + } + + /// This method detects a barcode in a "pure" image -- that is, pure monochrome image + /// which contains only an unrotated, unskewed, image of a barcode, with some white border + /// around it. This is a specialized method that works exceptionally fast in this special + /// case. + /// + private static BitMatrix extractPureBits(BinaryBitmap image) + { + // Now need to determine module size in pixels + BitMatrix matrix = image.BlackMatrix; + int height = matrix.Height; + int width = matrix.Width; + int minDimension = System.Math.Min(height, width); + + // First, skip white border by tracking diagonally from the top left down and to the right: + int borderWidth = 0; + while (borderWidth < minDimension && !matrix.get_Renamed(borderWidth, borderWidth)) + { + borderWidth++; + } + if (borderWidth == minDimension) + { + throw ReaderException.Instance; + } + + // And then keep tracking across the top-left black module to determine module size + int moduleEnd = borderWidth; + while (moduleEnd < minDimension && matrix.get_Renamed(moduleEnd, moduleEnd)) + { + moduleEnd++; + } + if (moduleEnd == minDimension) + { + throw ReaderException.Instance; + } + + int moduleSize = moduleEnd - borderWidth; + + // And now find where the rightmost black module on the first row ends + int rowEndOfSymbol = width - 1; + while (rowEndOfSymbol >= 0 && !matrix.get_Renamed(rowEndOfSymbol, borderWidth)) + { + rowEndOfSymbol--; + } + if (rowEndOfSymbol < 0) + { + throw ReaderException.Instance; + } + rowEndOfSymbol++; + + // Make sure width of barcode is a multiple of module size + if ((rowEndOfSymbol - borderWidth) % moduleSize != 0) + { + throw ReaderException.Instance; + } + int dimension = (rowEndOfSymbol - borderWidth) / moduleSize; + + // Push in the "border" by half the module width so that we start + // sampling in the middle of the module. Just in case the image is a + // little off, this will help recover. + borderWidth += (moduleSize >> 1); + + int sampleDimension = borderWidth + (dimension - 1) * moduleSize; + if (sampleDimension >= width || sampleDimension >= height) + { + throw ReaderException.Instance; + } + + // Now just read off the bits + BitMatrix bits = new BitMatrix(dimension); + for (int y = 0; y < dimension; y++) + { + int iOffset = borderWidth + y * moduleSize; + for (int x = 0; x < dimension; x++) + { + if (matrix.get_Renamed(borderWidth + x * moduleSize, iOffset)) + { + bits.set_Renamed(x, y); + } + } + } + return bits; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/BitMatrixParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/BitMatrixParser.cs new file mode 100644 index 0000000..2b78f69 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/BitMatrixParser.cs @@ -0,0 +1,630 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.pdf417.decoder +{ + + ///

+ /// This class parses the BitMatrix image into codewords. + ///

+ /// + ///
+ /// SITA Lab (kevin.osullivan@sita.aero) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class BitMatrixParser + { + /// Returns an array of locations representing the erasures. + public int[] Erasures + { + get + { + return erasures; + } + + } + public int ECLevel + { + get + { + return ecLevel; + } + + } + + private const int MAX_ROW_DIFFERENCE = 6; + private const int MAX_ROWS = 90; + //private static final int MAX_COLUMNS = 30; + // Maximum Codewords (Data + Error) + private const int MAX_CW_CAPACITY = 929; + private const int MODULES_IN_SYMBOL = 17; + + //UPGRADE_NOTE: Final was removed from the declaration of 'bitMatrix '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix bitMatrix; + private int rows = 0; + //private int columns = 0; + + private int leftColumnECData = 0; + private int rightColumnECData = 0; + private int eraseCount = 0; + private int[] erasures = null; + private int ecLevel = - 1; + + internal BitMatrixParser(BitMatrix bitMatrix) + { + this.bitMatrix = bitMatrix; + } + + /// To ensure separability of rows, codewords of consecutive rows belong to + /// different subsets of all possible codewords. This routine scans the + /// symbols in the barcode. When it finds a number of consecutive rows which + /// are the same, it assumes that this is a row of codewords and processes + /// them into a codeword array. + /// + /// + /// an array of codewords. + /// + internal int[] readCodewords() + { + int width = bitMatrix.Dimension; + // TODO should be a rectangular matrix + int height = width; + + erasures = new int[MAX_CW_CAPACITY]; + + // Get the number of pixels in a module across the X dimension + //float moduleWidth = bitMatrix.getModuleWidth(); + float moduleWidth = 1.0f; // Image has been sampled and reduced + + int[] rowCounters = new int[width]; + int[] codewords = new int[MAX_CW_CAPACITY]; + int next = 0; + int matchingConsecutiveScans = 0; + bool rowInProgress = false; + int rowNumber = 0; + int rowHeight = 0; + for (int i = 1; i < height; i++) + { + if (rowNumber >= MAX_ROWS) + { + // Something is wrong, since we have exceeded + // the maximum rows in the specification. + // TODO Maybe return error code + return null; + } + int rowDifference = 0; + // Scan a line of modules and check the + // difference between this and the previous line + for (int j = 0; j < width; j++) + { + // Accumulate differences between this line and the + // previous line. + if (bitMatrix.get_Renamed(j, i) != bitMatrix.get_Renamed(j, i - 1)) + { + rowDifference++; + } + } + if (rowDifference <= moduleWidth * MAX_ROW_DIFFERENCE) + { + for (int j = 0; j < width; j++) + { + // Accumulate the black pixels on this line + if (bitMatrix.get_Renamed(j, i)) + { + rowCounters[j]++; + } + } + // Increment the number of consecutive rows of pixels + // that are more or less the same + matchingConsecutiveScans++; + // Height of a row is a multiple of the module size in pixels + // Usually at least 3 times the module size + if (matchingConsecutiveScans >= moduleWidth * 2) + { + // MGMG + // We have some previous matches as well as a match here + // Set processing a unique row. + rowInProgress = true; + } + } + else + { + if (rowInProgress) + { + // Process Row + next = processRow(rowCounters, rowNumber, rowHeight, codewords, next); + if (next == - 1) + { + // Something is wrong, since we have exceeded + // the maximum columns in the specification. + // TODO Maybe return error code + return null; + } + // Reinitialize the row counters. + for (int j = 0; j < rowCounters.Length; j++) + { + rowCounters[j] = 0; + } + rowNumber++; + rowHeight = 0; + } + matchingConsecutiveScans = 0; + rowInProgress = false; + } + rowHeight++; + } + // Check for a row that was in progress before we exited above. + if (rowInProgress) + { + // Process Row + if (rowNumber >= MAX_ROWS) + { + // Something is wrong, since we have exceeded + // the maximum rows in the specification. + // TODO Maybe return error code + return null; + } + next = processRow(rowCounters, rowNumber, rowHeight, codewords, next); + rowNumber++; + rows = rowNumber; + } + erasures = trimArray(erasures, eraseCount); + return trimArray(codewords, next); + } + + /// Trim the array to the required size. + /// + /// + /// the array + /// + /// the size to trim it to + /// + /// the new trimmed array + /// + private static int[] trimArray(int[] array, int size) + { + if (size > 0) + { + int[] a = new int[size]; + for (int i = 0; i < size; i++) + { + a[i] = array[i]; + } + return a; + } + else + { + return null; + } + } + + /// Convert the symbols in the row to codewords. + /// Each PDF417 symbol character consists of four bar elements and four space + /// elements, each of which can be one to six modules wide. The four bar and + /// four space elements shall measure 17 modules in total. + /// + /// + /// an array containing the counts of black pixels for each column + /// in the row. + /// + /// the current row number of codewords. + /// + /// the height of this row in pixels. + /// + /// the codeword array to save codewords into. + /// + /// the next available index into the codewords array. + /// + /// the next available index into the codeword array after processing + /// this row. + /// + internal int processRow(int[] rowCounters, int rowNumber, int rowHeight, int[] codewords, int next) + { + int width = bitMatrix.Dimension; + int columnNumber = 0; + long symbol = 0; + for (int i = 0; i < width; i += MODULES_IN_SYMBOL) + { + for (int mask = MODULES_IN_SYMBOL - 1; mask >= 0; mask--) + { + if (rowCounters[i + (MODULES_IN_SYMBOL - 1 - mask)] >= SupportClass.URShift(rowHeight, 1)) + { + symbol |= 1L << mask; + } + } + if (columnNumber > 0) + { + int cw = getCodeword(symbol); + // if (debug) System.out.println(" " + Long.toBinaryString(symbol) + + // " cw=" +cw + " ColumnNumber=" +columnNumber + "i=" +i); + if (cw < 0 && i < width - MODULES_IN_SYMBOL) + { + // Skip errors on the Right row indicator column + erasures[eraseCount] = next; + next++; + eraseCount++; + } + else + { + codewords[next++] = cw; + } + } + else + { + // Left row indicator column + int cw = getCodeword(symbol); + // if (debug) System.out.println(" " + Long.toBinaryString(symbol) + + // " cw=" +cw + " ColumnNumber=" +columnNumber + "i=" +i); + if (ecLevel < 0) + { + switch (rowNumber % 3) + { + + case 0: + break; + + case 1: + leftColumnECData = cw; + break; + + case 2: + break; + } + } + } + symbol = 0; + //columns = columnNumber; + columnNumber++; + } + if (columnNumber > 1) + { + // Right row indicator column is in codeword[next] + //columns--; + // Overwrite the last codeword i.e. Right Row Indicator + --next; + if (ecLevel < 0) + { + switch (rowNumber % 3) + { + + case 0: + break; + + case 1: + break; + + case 2: + rightColumnECData = codewords[next]; + if (rightColumnECData == leftColumnECData && leftColumnECData != 0) + { + ecLevel = ((rightColumnECData % 30) - rows % 3) / 3; + } + break; + } + } + codewords[next] = 0; + } + return next; + } + + /// Build a symbol from the pixels. + /// Each symbol character is defined by an 8-digit bar-space sequence which + /// represents the module widths of the eight elements of that symbol + /// character. + /// + /// + /// array of pixel counter corresponding to each Bar/Space pattern. + /// + /// the symbol + /// + /* + private static long getSymbol(int[] counters, float moduleWidth) { + int pixelsInSymbol = 0; + for (int j = 0; j < counters.length; j++) { + pixelsInSymbol += counters[j]; + } + float avgModuleWidth = (pixelsInSymbol / 17.0f); + boolean toggle = true; + int shift = 0; + int symbol = 0; + for (int j = 0; j < counters.length; j++) { + if (counters[j] < moduleWidth && counters[j] > 0) { + // Give a very narrow bar/space a chance + counters[j] = (int) moduleWidth; + } + // Calculate number of modules in the symbol. + // int modules = (int)(counters[j]/moduleWidth); + // int modules = round(counters[j]/moduleWidth); + int modules = round(counters[j] / avgModuleWidth); + if (modules > 6) { + // Maximum size is 6 modules + modules = 6; + } else if (modules < 1) { + modules = 1; + } + if (toggle) { + for (int k = 0; k < modules; k++) { + symbol |= 1 << (16 - k - shift); + } + toggle = false; + } else { + toggle = true; + } + shift += modules; + } + return symbol; + } + */ + + /// Translate the symbol into a codeword. + /// + /// + /// + /// + /// the codeword corresponding to the symbol. + /// + private static int getCodeword(long symbol) + { + long sym = symbol; + sym &= 0x3ffff; + int i = findCodewordIndex(sym); + if (i == - 1) + { + return - 1; + } + else + { + long cw = CODEWORD_TABLE[i] - 1; + cw %= 929; + return (int) cw; + } + } + + /// Use a binary search to find the index of the codeword corresponding to + /// this symbol. + /// + /// + /// the symbol from the barcode. + /// + /// the index into the codeword table. + /// + private static int findCodewordIndex(long symbol) + { + int first = 0; + int upto = SYMBOL_TABLE.Length; + while (first < upto) + { + int mid = SupportClass.URShift((first + upto), 1); // Compute mid point. + if (symbol < SYMBOL_TABLE[mid]) + { + upto = mid; // repeat search in bottom half. + } + else if (symbol > SYMBOL_TABLE[mid]) + { + first = mid + 1; // Repeat search in top half. + } + else + { + return mid; // Found it. return position + } + } + return - 1; + // if (debug) System.out.println("Failed to find codeword for Symbol=" + + // symbol); + } + + /// Ends up being a bit faster than Math.round(). This merely rounds its + /// argument to the nearest int, where x.5 rounds up. + /// + /* + private static int round(float d) { + return (int) (d + 0.5f); + } + */ + + /// Convert the symbols in the row to codewords. + /// Each PDF417 symbol character consists of four bar elements and four space + /// elements, each of which can be one to six modules wide. The four bar and + /// four space elements shall measure 17 modules in total. + /// + /// + /// an array containing the counts of black pixels for each column + /// in the row. + /// + /// the current row number of codewords. + /// + /// the height of this row in pixels. + /// + /// the size of a module in pixels. + /// + /// the codeword array to save codewords into. + /// + /// the next available index into the codewords array. + /// + /// the next available index into the codeword array after processing + /// this row. + /// + /// ReaderException + /* + int processRow1(int[] rowCounters, int rowNumber, int rowHeight, + float moduleWidth, int[] codewords, int next) { + int width = bitMatrix.getDimension(); + int firstBlack = 0; + + for (firstBlack = 0; firstBlack < width; firstBlack++) { + // Step forward until we find the first black pixels + if (rowCounters[firstBlack] >= rowHeight >>> 1) { + break; + } + } + + int[] counters = new int[8]; + int state = 0; // In black pixels, looking for white, first or second time + long symbol = 0; + int columnNumber = 0; + for (int i = firstBlack; i < width; i++) { + if (state == 1 || state == 3 || state == 5 || state == 7) { // In white + // pixels, + // looking + // for + // black + // If more than half the column is black + if (rowCounters[i] >= rowHeight >>> 1 || i == width - 1) { + if (i == width - 1) { + counters[state]++; + } + // In black pixels or the end of a row + state++; + if (state < 8) { + // Don't count the last one + counters[state]++; + } + } else { + counters[state]++; + } + } else { + if (rowCounters[i] < rowHeight >>> 1) { + // Found white pixels + state++; + if (state == 7 && i == width - 1) { + // Its found white pixels at the end of the row, + // give it a chance to exit gracefully + i--; + } else { + // Found white pixels + counters[state]++; + } + } else { + if (state < 8) { + // Still in black pixels + counters[state]++; + } + } + } + if (state == 8) { // Found black, white, black, white, black, white, + // black, white and stumbled back onto black; done + if (columnNumber >= MAX_COLUMNS) { + // Something is wrong, since we have exceeded + // the maximum columns in the specification. + // TODO Maybe return error code + return -1; + } + if (columnNumber > 0) { + symbol = getSymbol(counters, moduleWidth); + int cw = getCodeword(symbol); + // if (debug) System.out.println(" " + + // Long.toBinaryString(symbol) + " cw=" +cw + " ColumnNumber=" + // +columnNumber + "i=" +i); + if (cw < 0) { + erasures[eraseCount] = next; + next++; + eraseCount++; + } else { + codewords[next++] = cw; + } + } else { + // Left row indicator column + symbol = getSymbol(counters, moduleWidth); + int cw = getCodeword(symbol); + if (ecLevel < 0) { + switch (rowNumber % 3) { + case 0: + break; + case 1: + leftColumnECData = cw; + break; + case 2: + break; + } + } + } + // Step back so that this pixel can be examined during the next + // pass. + i--; + counters = new int[8]; + columns = columnNumber; + columnNumber++; + // Introduce some errors if (rowNumber == 0 && columnNumber == 4) + // { codewords[next-1] = 0; erasures[eraseCount] = next-1; + // eraseCount++; } if (rowNumber == 0 && columnNumber == 6) { + // codewords[next-1] = 10; erasures[eraseCount] = next-1; + // eraseCount++; } if (rowNumber == 0 && columnNumber == 8) { + // codewords[next-1] = 10; erasures[eraseCount] = next-1; + // eraseCount++; } + state = 0; + symbol = 0; + } + } + if (columnNumber > 1) { + // Right row indicator column is in codeword[next] + columns--; + // Overwrite the last codeword i.e. Right Row Indicator + --next; + if (ecLevel < 0) { + switch (rowNumber % 3) { + case 0: + break; + case 1: + break; + case 2: + rightColumnECData = codewords[next]; + if (rightColumnECData == leftColumnECData + && leftColumnECData != 0) { + ecLevel = ((rightColumnECData % 30) - rows % 3) / 3; + } + break; + } + } + codewords[next] = 0; + } + return next; + } + */ + + /// The sorted table of all possible symbols. Extracted from the PDF417 + /// specification. The index of a symbol in this table corresponds to the + /// index into the codeword table. + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'SYMBOL_TABLE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] SYMBOL_TABLE = new int[]{0x1025e, 0x1027a, 0x1029e, 0x102bc, 0x102f2, 0x102f4, 0x1032e, 0x1034e, 0x1035c, 0x10396, 0x103a6, 0x103ac, 0x10422, 0x10428, 0x10436, 0x10442, 0x10444, 0x10448, 0x10450, 0x1045e, 0x10466, 0x1046c, 0x1047a, 0x10482, 0x1049e, 0x104a0, 0x104bc, 0x104c6, 0x104d8, 0x104ee, 0x104f2, 0x104f4, 0x10504, 0x10508, 0x10510, 0x1051e, 0x10520, 0x1053c, 0x10540, 0x10578, 0x10586, 0x1058c, 0x10598, 0x105b0, 0x105be, 0x105ce, 0x105dc, 0x105e2, 0x105e4, 0x105e8, 0x105f6, 0x1062e, 0x1064e, 0x1065c, 0x1068e, 0x1069c, 0x106b8, 0x106de, 0x106fa, 0x10716, 0x10726, 0x1072c, 0x10746, 0x1074c, 0x10758, 0x1076e, 0x10792, 0x10794, 0x107a2, 0x107a4, 0x107a8, 0x107b6, 0x10822, 0x10828, 0x10842, 0x10848, 0x10850, 0x1085e, 0x10866, 0x1086c, 0x1087a, 0x10882, 0x10884, 0x10890, 0x1089e, 0x108a0, 0x108bc, 0x108c6, 0x108cc, 0x108d8, 0x108ee, 0x108f2, 0x108f4, 0x10902, 0x10908, 0x1091e, 0x10920, 0x1093c, 0x10940, 0x10978, 0x10986, 0x10998, 0x109b0, 0x109be, 0x109ce, 0x109dc, 0x109e2, 0x109e4, 0x109e8, 0x109f6, 0x10a08, 0x10a10, 0x10a1e, 0x10a20, 0x10a3c, 0x10a40, 0x10a78, 0x10af0, 0x10b06, 0x10b0c, 0x10b18, 0x10b30, 0x10b3e, 0x10b60, 0x10b7c, 0x10b8e, 0x10b9c, 0x10bb8, 0x10bc2, 0x10bc4, 0x10bc8, 0x10bd0, 0x10bde, 0x10be6, 0x10bec, 0x10c2e, 0x10c4e, 0x10c5c, 0x10c62, 0x10c64, 0x10c68, 0x10c76, 0x10c8e, 0x10c9c, 0x10cb8, 0x10cc2, 0x10cc4, 0x10cc8, 0x10cd0, 0x10cde, 0x10ce6, 0x10cec, 0x10cfa, 0x10d0e, 0x10d1c, 0x10d38, 0x10d70, 0x10d7e, 0x10d82, 0x10d84, 0x10d88, 0x10d90, 0x10d9e, 0x10da0, 0x10dbc, 0x10dc6, 0x10dcc, 0x10dd8, 0x10dee, 0x10df2, 0x10df4, 0x10e16, 0x10e26, 0x10e2c, 0x10e46, 0x10e58, 0x10e6e, 0x10e86, 0x10e8c, 0x10e98, 0x10eb0, 0x10ebe, 0x10ece, 0x10edc, 0x10f0a, 0x10f12, 0x10f14, 0x10f22, 0x10f28, 0x10f36, 0x10f42, 0x10f44, 0x10f48, 0x10f50, 0x10f5e, 0x10f66, 0x10f6c, 0x10fb2, 0x10fb4, 0x11022, 0x11028, 0x11042, 0x11048, 0x11050, 0x1105e, 0x1107a, 0x11082, 0x11084, 0x11090, 0x1109e, 0x110a0, 0x110bc, 0x110c6, 0x110cc, 0x110d8, 0x110ee, 0x110f2, 0x110f4, 0x11102, 0x1111e, + 0x11120, 0x1113c, 0x11140, 0x11178, 0x11186, 0x11198, 0x111b0, 0x111be, 0x111ce, 0x111dc, 0x111e2, 0x111e4, 0x111e8, 0x111f6, 0x11208, 0x1121e, 0x11220, 0x11278, 0x112f0, 0x1130c, 0x11330, 0x1133e, 0x11360, 0x1137c, 0x1138e, 0x1139c, 0x113b8, 0x113c2, 0x113c8, 0x113d0, 0x113de, 0x113e6, 0x113ec, 0x11408, 0x11410, 0x1141e, 0x11420, 0x1143c, 0x11440, 0x11478, 0x114f0, 0x115e0, 0x1160c, 0x11618, 0x11630, 0x1163e, 0x11660, 0x1167c, 0x116c0, 0x116f8, 0x1171c, 0x11738, 0x11770, 0x1177e, 0x11782, 0x11784, 0x11788, 0x11790, 0x1179e, 0x117a0, 0x117bc, 0x117c6, 0x117cc, 0x117d8, 0x117ee, 0x1182e, 0x11834, 0x1184e, 0x1185c, 0x11862, 0x11864, 0x11868, 0x11876, 0x1188e, 0x1189c, 0x118b8, 0x118c2, 0x118c8, 0x118d0, 0x118de, 0x118e6, 0x118ec, 0x118fa, 0x1190e, 0x1191c, 0x11938, 0x11970, 0x1197e, 0x11982, 0x11984, 0x11990, 0x1199e, 0x119a0, 0x119bc, 0x119c6, 0x119cc, 0x119d8, 0x119ee, 0x119f2, 0x119f4, 0x11a0e, 0x11a1c, 0x11a38, 0x11a70, 0x11a7e, 0x11ae0, 0x11afc, 0x11b08, 0x11b10, 0x11b1e, 0x11b20, 0x11b3c, 0x11b40, 0x11b78, 0x11b8c, 0x11b98, 0x11bb0, 0x11bbe, 0x11bce, 0x11bdc, 0x11be2, 0x11be4, 0x11be8, 0x11bf6, 0x11c16, 0x11c26, 0x11c2c, 0x11c46, 0x11c4c, 0x11c58, 0x11c6e, 0x11c86, 0x11c98, 0x11cb0, 0x11cbe, 0x11cce, 0x11cdc, 0x11ce2, 0x11ce4, 0x11ce8, 0x11cf6, 0x11d06, 0x11d0c, 0x11d18, 0x11d30, 0x11d3e, 0x11d60, 0x11d7c, 0x11d8e, 0x11d9c, 0x11db8, 0x11dc4, 0x11dc8, 0x11dd0, 0x11dde, 0x11de6, 0x11dec, 0x11dfa, 0x11e0a, 0x11e12, 0x11e14, 0x11e22, 0x11e24, 0x11e28, 0x11e36, 0x11e42, 0x11e44, 0x11e50, 0x11e5e, 0x11e66, 0x11e6c, 0x11e82, 0x11e84, 0x11e88, 0x11e90, 0x11e9e, 0x11ea0, 0x11ebc, 0x11ec6, 0x11ecc, 0x11ed8, 0x11eee, 0x11f1a, 0x11f2e, 0x11f32, 0x11f34, 0x11f4e, 0x11f5c, 0x11f62, 0x11f64, 0x11f68, 0x11f76, 0x12048, 0x1205e, 0x12082, 0x12084, 0x12090, 0x1209e, 0x120a0, 0x120bc, 0x120d8, 0x120f2, 0x120f4, 0x12108, 0x1211e, 0x12120, 0x1213c, 0x12140, 0x12178, 0x12186, 0x12198, 0x121b0, 0x121be, 0x121e2, 0x121e4, 0x121e8, 0x121f6, 0x12204, 0x12210, 0x1221e, 0x12220, 0x12278, 0x122f0, 0x12306, 0x1230c, + 0x12330, 0x1233e, 0x12360, 0x1237c, 0x1238e, 0x1239c, 0x123b8, 0x123c2, 0x123c8, 0x123d0, 0x123e6, 0x123ec, 0x1241e, 0x12420, 0x1243c, 0x124f0, 0x125e0, 0x12618, 0x1263e, 0x12660, 0x1267c, 0x126c0, 0x126f8, 0x12738, 0x12770, 0x1277e, 0x12782, 0x12784, 0x12790, 0x1279e, 0x127a0, 0x127bc, 0x127c6, 0x127cc, 0x127d8, 0x127ee, 0x12820, 0x1283c, 0x12840, 0x12878, 0x128f0, 0x129e0, 0x12bc0, 0x12c18, 0x12c30, 0x12c3e, 0x12c60, 0x12c7c, 0x12cc0, 0x12cf8, 0x12df0, 0x12e1c, 0x12e38, 0x12e70, 0x12e7e, 0x12ee0, 0x12efc, 0x12f04, 0x12f08, 0x12f10, 0x12f20, 0x12f3c, 0x12f40, 0x12f78, 0x12f86, 0x12f8c, 0x12f98, 0x12fb0, 0x12fbe, 0x12fce, 0x12fdc, 0x1302e, 0x1304e, 0x1305c, 0x13062, 0x13068, 0x1308e, 0x1309c, 0x130b8, 0x130c2, 0x130c8, 0x130d0, 0x130de, 0x130ec, 0x130fa, 0x1310e, 0x13138, 0x13170, 0x1317e, 0x13182, 0x13184, 0x13190, 0x1319e, 0x131a0, 0x131bc, 0x131c6, 0x131cc, 0x131d8, 0x131f2, 0x131f4, 0x1320e, 0x1321c, 0x13270, 0x1327e, 0x132e0, 0x132fc, 0x13308, 0x1331e, 0x13320, 0x1333c, 0x13340, 0x13378, 0x13386, 0x13398, 0x133b0, 0x133be, 0x133ce, 0x133dc, 0x133e2, 0x133e4, 0x133e8, 0x133f6, 0x1340e, 0x1341c, 0x13438, 0x13470, 0x1347e, 0x134e0, 0x134fc, 0x135c0, 0x135f8, 0x13608, 0x13610, 0x1361e, 0x13620, 0x1363c, 0x13640, 0x13678, 0x136f0, 0x1370c, 0x13718, 0x13730, 0x1373e, 0x13760, 0x1377c, 0x1379c, 0x137b8, 0x137c2, 0x137c4, 0x137c8, 0x137d0, 0x137de, 0x137e6, 0x137ec, 0x13816, 0x13826, 0x1382c, 0x13846, 0x1384c, 0x13858, 0x1386e, 0x13874, 0x13886, 0x13898, 0x138b0, 0x138be, 0x138ce, 0x138dc, 0x138e2, 0x138e4, 0x138e8, 0x13906, 0x1390c, 0x13930, 0x1393e, 0x13960, 0x1397c, 0x1398e, 0x1399c, 0x139b8, 0x139c8, 0x139d0, 0x139de, 0x139e6, 0x139ec, 0x139fa, 0x13a06, 0x13a0c, 0x13a18, 0x13a30, 0x13a3e, 0x13a60, 0x13a7c, 0x13ac0, 0x13af8, 0x13b0e, 0x13b1c, 0x13b38, 0x13b70, 0x13b7e, 0x13b88, 0x13b90, 0x13b9e, 0x13ba0, 0x13bbc, 0x13bcc, 0x13bd8, 0x13bee, 0x13bf2, 0x13bf4, 0x13c12, 0x13c14, 0x13c22, 0x13c24, 0x13c28, 0x13c36, 0x13c42, 0x13c48, 0x13c50, 0x13c5e, 0x13c66, 0x13c6c, 0x13c82, 0x13c84, 0x13c90, + 0x13c9e, 0x13ca0, 0x13cbc, 0x13cc6, 0x13ccc, 0x13cd8, 0x13cee, 0x13d02, 0x13d04, 0x13d08, 0x13d10, 0x13d1e, 0x13d20, 0x13d3c, 0x13d40, 0x13d78, 0x13d86, 0x13d8c, 0x13d98, 0x13db0, 0x13dbe, 0x13dce, 0x13ddc, 0x13de4, 0x13de8, 0x13df6, 0x13e1a, 0x13e2e, 0x13e32, 0x13e34, 0x13e4e, 0x13e5c, 0x13e62, 0x13e64, 0x13e68, 0x13e76, 0x13e8e, 0x13e9c, 0x13eb8, 0x13ec2, 0x13ec4, 0x13ec8, 0x13ed0, 0x13ede, 0x13ee6, 0x13eec, 0x13f26, 0x13f2c, 0x13f3a, 0x13f46, 0x13f4c, 0x13f58, 0x13f6e, 0x13f72, 0x13f74, 0x14082, 0x1409e, 0x140a0, 0x140bc, 0x14104, 0x14108, 0x14110, 0x1411e, 0x14120, 0x1413c, 0x14140, 0x14178, 0x1418c, 0x14198, 0x141b0, 0x141be, 0x141e2, 0x141e4, 0x141e8, 0x14208, 0x14210, 0x1421e, 0x14220, 0x1423c, 0x14240, 0x14278, 0x142f0, 0x14306, 0x1430c, 0x14318, 0x14330, 0x1433e, 0x14360, 0x1437c, 0x1438e, 0x143c2, 0x143c4, 0x143c8, 0x143d0, 0x143e6, 0x143ec, 0x14408, 0x14410, 0x1441e, 0x14420, 0x1443c, 0x14440, 0x14478, 0x144f0, 0x145e0, 0x1460c, 0x14618, 0x14630, 0x1463e, 0x14660, 0x1467c, 0x146c0, 0x146f8, 0x1471c, 0x14738, 0x14770, 0x1477e, 0x14782, 0x14784, 0x14788, 0x14790, 0x147a0, 0x147bc, 0x147c6, 0x147cc, 0x147d8, 0x147ee, 0x14810, 0x14820, 0x1483c, 0x14840, 0x14878, 0x148f0, 0x149e0, 0x14bc0, 0x14c30, 0x14c3e, 0x14c60, 0x14c7c, 0x14cc0, 0x14cf8, 0x14df0, 0x14e38, 0x14e70, 0x14e7e, 0x14ee0, 0x14efc, 0x14f04, 0x14f08, 0x14f10, 0x14f1e, 0x14f20, 0x14f3c, 0x14f40, 0x14f78, 0x14f86, 0x14f8c, 0x14f98, 0x14fb0, 0x14fce, 0x14fdc, 0x15020, 0x15040, 0x15078, 0x150f0, 0x151e0, 0x153c0, 0x15860, 0x1587c, 0x158c0, 0x158f8, 0x159f0, 0x15be0, 0x15c70, 0x15c7e, 0x15ce0, 0x15cfc, 0x15dc0, 0x15df8, 0x15e08, 0x15e10, 0x15e20, 0x15e40, 0x15e78, 0x15ef0, 0x15f0c, 0x15f18, 0x15f30, 0x15f60, 0x15f7c, 0x15f8e, 0x15f9c, 0x15fb8, 0x1604e, 0x1605c, 0x1608e, 0x1609c, 0x160b8, 0x160c2, 0x160c4, 0x160c8, 0x160de, 0x1610e, 0x1611c, 0x16138, 0x16170, 0x1617e, 0x16184, 0x16188, 0x16190, 0x1619e, 0x161a0, 0x161bc, 0x161c6, 0x161cc, 0x161d8, 0x161f2, 0x161f4, 0x1620e, 0x1621c, 0x16238, 0x16270, 0x1627e, 0x162e0, 0x162fc, + 0x16304, 0x16308, 0x16310, 0x1631e, 0x16320, 0x1633c, 0x16340, 0x16378, 0x16386, 0x1638c, 0x16398, 0x163b0, 0x163be, 0x163ce, 0x163dc, 0x163e2, 0x163e4, 0x163e8, 0x163f6, 0x1640e, 0x1641c, 0x16438, 0x16470, 0x1647e, 0x164e0, 0x164fc, 0x165c0, 0x165f8, 0x16610, 0x1661e, 0x16620, 0x1663c, 0x16640, 0x16678, 0x166f0, 0x16718, 0x16730, 0x1673e, 0x16760, 0x1677c, 0x1678e, 0x1679c, 0x167b8, 0x167c2, 0x167c4, 0x167c8, 0x167d0, 0x167de, 0x167e6, 0x167ec, 0x1681c, 0x16838, 0x16870, 0x168e0, 0x168fc, 0x169c0, 0x169f8, 0x16bf0, 0x16c10, 0x16c1e, 0x16c20, 0x16c3c, 0x16c40, 0x16c78, 0x16cf0, 0x16de0, 0x16e18, 0x16e30, 0x16e3e, 0x16e60, 0x16e7c, 0x16ec0, 0x16ef8, 0x16f1c, 0x16f38, 0x16f70, 0x16f7e, 0x16f84, 0x16f88, 0x16f90, 0x16f9e, 0x16fa0, 0x16fbc, 0x16fc6, 0x16fcc, 0x16fd8, 0x17026, 0x1702c, 0x17046, 0x1704c, 0x17058, 0x1706e, 0x17086, 0x1708c, 0x17098, 0x170b0, 0x170be, 0x170ce, 0x170dc, 0x170e8, 0x17106, 0x1710c, 0x17118, 0x17130, 0x1713e, 0x17160, 0x1717c, 0x1718e, 0x1719c, 0x171b8, 0x171c2, 0x171c4, 0x171c8, 0x171d0, 0x171de, 0x171e6, 0x171ec, 0x171fa, 0x17206, 0x1720c, 0x17218, 0x17230, 0x1723e, 0x17260, 0x1727c, 0x172c0, 0x172f8, 0x1730e, 0x1731c, 0x17338, 0x17370, 0x1737e, 0x17388, 0x17390, 0x1739e, 0x173a0, 0x173bc, 0x173cc, 0x173d8, 0x173ee, 0x173f2, 0x173f4, 0x1740c, 0x17418, 0x17430, 0x1743e, 0x17460, 0x1747c, 0x174c0, 0x174f8, 0x175f0, 0x1760e, 0x1761c, 0x17638, 0x17670, 0x1767e, 0x176e0, 0x176fc, 0x17708, 0x17710, 0x1771e, 0x17720, 0x1773c, 0x17740, 0x17778, 0x17798, 0x177b0, 0x177be, 0x177dc, 0x177e2, 0x177e4, 0x177e8, 0x17822, 0x17824, 0x17828, 0x17836, 0x17842, 0x17844, 0x17848, 0x17850, 0x1785e, 0x17866, 0x1786c, 0x17882, 0x17884, 0x17888, 0x17890, 0x1789e, 0x178a0, 0x178bc, 0x178c6, 0x178cc, 0x178d8, 0x178ee, 0x178f2, 0x178f4, 0x17902, 0x17904, 0x17908, 0x17910, 0x1791e, 0x17920, 0x1793c, 0x17940, 0x17978, 0x17986, 0x1798c, 0x17998, 0x179b0, 0x179be, 0x179ce, 0x179dc, 0x179e2, 0x179e4, 0x179e8, 0x179f6, 0x17a04, 0x17a08, 0x17a10, 0x17a1e, 0x17a20, 0x17a3c, 0x17a40, 0x17a78, 0x17af0, + 0x17b06, 0x17b0c, 0x17b18, 0x17b30, 0x17b3e, 0x17b60, 0x17b7c, 0x17b8e, 0x17b9c, 0x17bb8, 0x17bc4, 0x17bc8, 0x17bd0, 0x17bde, 0x17be6, 0x17bec, 0x17c2e, 0x17c32, 0x17c34, 0x17c4e, 0x17c5c, 0x17c62, 0x17c64, 0x17c68, 0x17c76, 0x17c8e, 0x17c9c, 0x17cb8, 0x17cc2, 0x17cc4, 0x17cc8, 0x17cd0, 0x17cde, 0x17ce6, 0x17cec, 0x17d0e, 0x17d1c, 0x17d38, 0x17d70, 0x17d82, 0x17d84, 0x17d88, 0x17d90, 0x17d9e, 0x17da0, 0x17dbc, 0x17dc6, 0x17dcc, 0x17dd8, 0x17dee, 0x17e26, 0x17e2c, 0x17e3a, 0x17e46, 0x17e4c, 0x17e58, 0x17e6e, 0x17e72, 0x17e74, 0x17e86, 0x17e8c, 0x17e98, 0x17eb0, 0x17ece, 0x17edc, 0x17ee2, 0x17ee4, 0x17ee8, 0x17ef6, 0x1813a, 0x18172, 0x18174, 0x18216, 0x18226, 0x1823a, 0x1824c, 0x18258, 0x1826e, 0x18272, 0x18274, 0x18298, 0x182be, 0x182e2, 0x182e4, 0x182e8, 0x182f6, 0x1835e, 0x1837a, 0x183ae, 0x183d6, 0x18416, 0x18426, 0x1842c, 0x1843a, 0x18446, 0x18458, 0x1846e, 0x18472, 0x18474, 0x18486, 0x184b0, 0x184be, 0x184ce, 0x184dc, 0x184e2, 0x184e4, 0x184e8, 0x184f6, 0x18506, 0x1850c, 0x18518, 0x18530, 0x1853e, 0x18560, 0x1857c, 0x1858e, 0x1859c, 0x185b8, 0x185c2, 0x185c4, 0x185c8, 0x185d0, 0x185de, 0x185e6, 0x185ec, 0x185fa, 0x18612, 0x18614, 0x18622, 0x18628, 0x18636, 0x18642, 0x18650, 0x1865e, 0x1867a, 0x18682, 0x18684, 0x18688, 0x18690, 0x1869e, 0x186a0, 0x186bc, 0x186c6, 0x186cc, 0x186d8, 0x186ee, 0x186f2, 0x186f4, 0x1872e, 0x1874e, 0x1875c, 0x18796, 0x187a6, 0x187ac, 0x187d2, 0x187d4, 0x18826, 0x1882c, 0x1883a, 0x18846, 0x1884c, 0x18858, 0x1886e, 0x18872, 0x18874, 0x18886, 0x18898, 0x188b0, 0x188be, 0x188ce, 0x188dc, 0x188e2, 0x188e4, 0x188e8, 0x188f6, 0x1890c, 0x18930, 0x1893e, 0x18960, 0x1897c, 0x1898e, 0x189b8, 0x189c2, 0x189c8, 0x189d0, 0x189de, 0x189e6, 0x189ec, 0x189fa, 0x18a18, 0x18a30, 0x18a3e, 0x18a60, 0x18a7c, 0x18ac0, 0x18af8, 0x18b1c, 0x18b38, 0x18b70, 0x18b7e, 0x18b82, 0x18b84, 0x18b88, 0x18b90, 0x18b9e, 0x18ba0, 0x18bbc, 0x18bc6, 0x18bcc, 0x18bd8, 0x18bee, 0x18bf2, 0x18bf4, 0x18c22, 0x18c24, 0x18c28, 0x18c36, 0x18c42, 0x18c48, 0x18c50, 0x18c5e, 0x18c66, 0x18c7a, 0x18c82, 0x18c84, + 0x18c90, 0x18c9e, 0x18ca0, 0x18cbc, 0x18ccc, 0x18cf2, 0x18cf4, 0x18d04, 0x18d08, 0x18d10, 0x18d1e, 0x18d20, 0x18d3c, 0x18d40, 0x18d78, 0x18d86, 0x18d98, 0x18dce, 0x18de2, 0x18de4, 0x18de8, 0x18e2e, 0x18e32, 0x18e34, 0x18e4e, 0x18e5c, 0x18e62, 0x18e64, 0x18e68, 0x18e8e, 0x18e9c, 0x18eb8, 0x18ec2, 0x18ec4, 0x18ec8, 0x18ed0, 0x18efa, 0x18f16, 0x18f26, 0x18f2c, 0x18f46, 0x18f4c, 0x18f58, 0x18f6e, 0x18f8a, 0x18f92, 0x18f94, 0x18fa2, 0x18fa4, 0x18fa8, 0x18fb6, 0x1902c, 0x1903a, 0x19046, 0x1904c, 0x19058, 0x19072, 0x19074, 0x19086, 0x19098, 0x190b0, 0x190be, 0x190ce, 0x190dc, 0x190e2, 0x190e8, 0x190f6, 0x19106, 0x1910c, 0x19130, 0x1913e, 0x19160, 0x1917c, 0x1918e, 0x1919c, 0x191b8, 0x191c2, 0x191c8, 0x191d0, 0x191de, 0x191e6, 0x191ec, 0x191fa, 0x19218, 0x1923e, 0x19260, 0x1927c, 0x192c0, 0x192f8, 0x19338, 0x19370, 0x1937e, 0x19382, 0x19384, 0x19390, 0x1939e, 0x193a0, 0x193bc, 0x193c6, 0x193cc, 0x193d8, 0x193ee, 0x193f2, 0x193f4, 0x19430, 0x1943e, 0x19460, 0x1947c, 0x194c0, 0x194f8, 0x195f0, 0x19638, 0x19670, 0x1967e, 0x196e0, 0x196fc, 0x19702, 0x19704, 0x19708, 0x19710, 0x19720, 0x1973c, 0x19740, 0x19778, 0x19786, 0x1978c, 0x19798, 0x197b0, 0x197be, 0x197ce, 0x197dc, 0x197e2, 0x197e4, 0x197e8, 0x19822, 0x19824, 0x19842, 0x19848, 0x19850, 0x1985e, 0x19866, 0x1987a, 0x19882, 0x19884, 0x19890, 0x1989e, 0x198a0, 0x198bc, 0x198cc, 0x198f2, 0x198f4, 0x19902, 0x19908, 0x1991e, 0x19920, 0x1993c, 0x19940, 0x19978, 0x19986, 0x19998, 0x199ce, 0x199e2, 0x199e4, 0x199e8, 0x19a08, 0x19a10, 0x19a1e, 0x19a20, 0x19a3c, 0x19a40, 0x19a78, 0x19af0, 0x19b18, 0x19b3e, 0x19b60, 0x19b9c, 0x19bc2, 0x19bc4, 0x19bc8, 0x19bd0, 0x19be6, 0x19c2e, 0x19c34, 0x19c4e, 0x19c5c, 0x19c62, 0x19c64, 0x19c68, 0x19c8e, 0x19c9c, 0x19cb8, 0x19cc2, 0x19cc8, 0x19cd0, 0x19ce6, 0x19cfa, 0x19d0e, 0x19d1c, 0x19d38, 0x19d70, 0x19d7e, 0x19d82, 0x19d84, 0x19d88, 0x19d90, 0x19da0, 0x19dcc, 0x19df2, 0x19df4, 0x19e16, 0x19e26, 0x19e2c, 0x19e46, 0x19e4c, 0x19e58, 0x19e74, 0x19e86, 0x19e8c, 0x19e98, 0x19eb0, 0x19ebe, 0x19ece, 0x19ee2, 0x19ee4, 0x19ee8, + 0x19f0a, 0x19f12, 0x19f14, 0x19f22, 0x19f24, 0x19f28, 0x19f42, 0x19f44, 0x19f48, 0x19f50, 0x19f5e, 0x19f6c, 0x19f9a, 0x19fae, 0x19fb2, 0x19fb4, 0x1a046, 0x1a04c, 0x1a072, 0x1a074, 0x1a086, 0x1a08c, 0x1a098, 0x1a0b0, 0x1a0be, 0x1a0e2, 0x1a0e4, 0x1a0e8, 0x1a0f6, 0x1a106, 0x1a10c, 0x1a118, 0x1a130, 0x1a13e, 0x1a160, 0x1a17c, 0x1a18e, 0x1a19c, 0x1a1b8, 0x1a1c2, 0x1a1c4, 0x1a1c8, 0x1a1d0, 0x1a1de, 0x1a1e6, 0x1a1ec, 0x1a218, 0x1a230, 0x1a23e, 0x1a260, 0x1a27c, 0x1a2c0, 0x1a2f8, 0x1a31c, 0x1a338, 0x1a370, 0x1a37e, 0x1a382, 0x1a384, 0x1a388, 0x1a390, 0x1a39e, 0x1a3a0, 0x1a3bc, 0x1a3c6, 0x1a3cc, 0x1a3d8, 0x1a3ee, 0x1a3f2, 0x1a3f4, 0x1a418, 0x1a430, 0x1a43e, 0x1a460, 0x1a47c, 0x1a4c0, 0x1a4f8, 0x1a5f0, 0x1a61c, 0x1a638, 0x1a670, 0x1a67e, 0x1a6e0, 0x1a6fc, 0x1a702, 0x1a704, 0x1a708, 0x1a710, 0x1a71e, 0x1a720, 0x1a73c, 0x1a740, 0x1a778, 0x1a786, 0x1a78c, 0x1a798, 0x1a7b0, 0x1a7be, 0x1a7ce, 0x1a7dc, 0x1a7e2, 0x1a7e4, 0x1a7e8, 0x1a830, 0x1a860, 0x1a87c, 0x1a8c0, 0x1a8f8, 0x1a9f0, 0x1abe0, 0x1ac70, 0x1ac7e, 0x1ace0, 0x1acfc, 0x1adc0, 0x1adf8, 0x1ae04, 0x1ae08, 0x1ae10, 0x1ae20, 0x1ae3c, 0x1ae40, 0x1ae78, 0x1aef0, 0x1af06, 0x1af0c, 0x1af18, 0x1af30, 0x1af3e, 0x1af60, 0x1af7c, 0x1af8e, 0x1af9c, 0x1afb8, 0x1afc4, 0x1afc8, 0x1afd0, 0x1afde, 0x1b042, 0x1b05e, 0x1b07a, 0x1b082, 0x1b084, 0x1b088, 0x1b090, 0x1b09e, 0x1b0a0, 0x1b0bc, 0x1b0cc, 0x1b0f2, 0x1b0f4, 0x1b102, 0x1b104, 0x1b108, 0x1b110, 0x1b11e, 0x1b120, 0x1b13c, 0x1b140, 0x1b178, 0x1b186, 0x1b198, 0x1b1ce, 0x1b1e2, 0x1b1e4, 0x1b1e8, 0x1b204, 0x1b208, 0x1b210, 0x1b21e, 0x1b220, 0x1b23c, 0x1b240, 0x1b278, 0x1b2f0, 0x1b30c, 0x1b33e, 0x1b360, 0x1b39c, 0x1b3c2, 0x1b3c4, 0x1b3c8, 0x1b3d0, 0x1b3e6, 0x1b410, 0x1b41e, 0x1b420, 0x1b43c, 0x1b440, 0x1b478, 0x1b4f0, 0x1b5e0, 0x1b618, 0x1b660, 0x1b67c, 0x1b6c0, 0x1b738, 0x1b782, 0x1b784, 0x1b788, 0x1b790, 0x1b79e, 0x1b7a0, 0x1b7cc, 0x1b82e, 0x1b84e, 0x1b85c, 0x1b88e, 0x1b89c, 0x1b8b8, 0x1b8c2, 0x1b8c4, 0x1b8c8, 0x1b8d0, 0x1b8e6, 0x1b8fa, 0x1b90e, 0x1b91c, 0x1b938, 0x1b970, 0x1b97e, 0x1b982, 0x1b984, 0x1b988, 0x1b990, + 0x1b99e, 0x1b9a0, 0x1b9cc, 0x1b9f2, 0x1b9f4, 0x1ba0e, 0x1ba1c, 0x1ba38, 0x1ba70, 0x1ba7e, 0x1bae0, 0x1bafc, 0x1bb08, 0x1bb10, 0x1bb20, 0x1bb3c, 0x1bb40, 0x1bb98, 0x1bbce, 0x1bbe2, 0x1bbe4, 0x1bbe8, 0x1bc16, 0x1bc26, 0x1bc2c, 0x1bc46, 0x1bc4c, 0x1bc58, 0x1bc72, 0x1bc74, 0x1bc86, 0x1bc8c, 0x1bc98, 0x1bcb0, 0x1bcbe, 0x1bcce, 0x1bce2, 0x1bce4, 0x1bce8, 0x1bd06, 0x1bd0c, 0x1bd18, 0x1bd30, 0x1bd3e, 0x1bd60, 0x1bd7c, 0x1bd9c, 0x1bdc2, 0x1bdc4, 0x1bdc8, 0x1bdd0, 0x1bde6, 0x1bdfa, 0x1be12, 0x1be14, 0x1be22, 0x1be24, 0x1be28, 0x1be42, 0x1be44, 0x1be48, 0x1be50, 0x1be5e, 0x1be66, 0x1be82, 0x1be84, 0x1be88, 0x1be90, 0x1be9e, 0x1bea0, 0x1bebc, 0x1becc, 0x1bef4, 0x1bf1a, 0x1bf2e, 0x1bf32, 0x1bf34, 0x1bf4e, 0x1bf5c, 0x1bf62, 0x1bf64, 0x1bf68, 0x1c09a, 0x1c0b2, 0x1c0b4, 0x1c11a, 0x1c132, 0x1c134, 0x1c162, 0x1c164, 0x1c168, 0x1c176, 0x1c1ba, 0x1c21a, 0x1c232, 0x1c234, 0x1c24e, 0x1c25c, 0x1c262, 0x1c264, 0x1c268, 0x1c276, 0x1c28e, 0x1c2c2, 0x1c2c4, 0x1c2c8, 0x1c2d0, 0x1c2de, 0x1c2e6, 0x1c2ec, 0x1c2fa, 0x1c316, 0x1c326, 0x1c33a, 0x1c346, 0x1c34c, 0x1c372, 0x1c374, 0x1c41a, 0x1c42e, 0x1c432, 0x1c434, 0x1c44e, 0x1c45c, 0x1c462, 0x1c464, 0x1c468, 0x1c476, 0x1c48e, 0x1c49c, 0x1c4b8, 0x1c4c2, 0x1c4c8, 0x1c4d0, 0x1c4de, 0x1c4e6, 0x1c4ec, 0x1c4fa, 0x1c51c, 0x1c538, 0x1c570, 0x1c57e, 0x1c582, 0x1c584, 0x1c588, 0x1c590, 0x1c59e, 0x1c5a0, 0x1c5bc, 0x1c5c6, 0x1c5cc, 0x1c5d8, 0x1c5ee, 0x1c5f2, 0x1c5f4, 0x1c616, 0x1c626, 0x1c62c, 0x1c63a, 0x1c646, 0x1c64c, 0x1c658, 0x1c66e, 0x1c672, 0x1c674, 0x1c686, 0x1c68c, 0x1c698, 0x1c6b0, 0x1c6be, 0x1c6ce, 0x1c6dc, 0x1c6e2, 0x1c6e4, 0x1c6e8, 0x1c712, 0x1c714, 0x1c722, 0x1c728, 0x1c736, 0x1c742, 0x1c744, 0x1c748, 0x1c750, 0x1c75e, 0x1c766, 0x1c76c, 0x1c77a, 0x1c7ae, 0x1c7d6, 0x1c7ea, 0x1c81a, 0x1c82e, 0x1c832, 0x1c834, 0x1c84e, 0x1c85c, 0x1c862, 0x1c864, 0x1c868, 0x1c876, 0x1c88e, 0x1c89c, 0x1c8b8, 0x1c8c2, 0x1c8c8, 0x1c8d0, 0x1c8de, 0x1c8e6, 0x1c8ec, 0x1c8fa, 0x1c90e, 0x1c938, 0x1c970, 0x1c97e, 0x1c982, 0x1c984, 0x1c990, 0x1c99e, 0x1c9a0, 0x1c9bc, 0x1c9c6, 0x1c9cc, 0x1c9d8, 0x1c9ee, + 0x1c9f2, 0x1c9f4, 0x1ca38, 0x1ca70, 0x1ca7e, 0x1cae0, 0x1cafc, 0x1cb02, 0x1cb04, 0x1cb08, 0x1cb10, 0x1cb20, 0x1cb3c, 0x1cb40, 0x1cb78, 0x1cb86, 0x1cb8c, 0x1cb98, 0x1cbb0, 0x1cbbe, 0x1cbce, 0x1cbdc, 0x1cbe2, 0x1cbe4, 0x1cbe8, 0x1cbf6, 0x1cc16, 0x1cc26, 0x1cc2c, 0x1cc3a, 0x1cc46, 0x1cc58, 0x1cc72, 0x1cc74, 0x1cc86, 0x1ccb0, 0x1ccbe, 0x1ccce, 0x1cce2, 0x1cce4, 0x1cce8, 0x1cd06, 0x1cd0c, 0x1cd18, 0x1cd30, 0x1cd3e, 0x1cd60, 0x1cd7c, 0x1cd9c, 0x1cdc2, 0x1cdc4, 0x1cdc8, 0x1cdd0, 0x1cdde, 0x1cde6, 0x1cdfa, 0x1ce22, 0x1ce28, 0x1ce42, 0x1ce50, 0x1ce5e, 0x1ce66, 0x1ce7a, 0x1ce82, 0x1ce84, 0x1ce88, 0x1ce90, 0x1ce9e, 0x1cea0, 0x1cebc, 0x1cecc, 0x1cef2, 0x1cef4, 0x1cf2e, 0x1cf32, 0x1cf34, 0x1cf4e, 0x1cf5c, 0x1cf62, 0x1cf64, 0x1cf68, 0x1cf96, 0x1cfa6, 0x1cfac, 0x1cfca, 0x1cfd2, 0x1cfd4, 0x1d02e, 0x1d032, 0x1d034, 0x1d04e, 0x1d05c, 0x1d062, 0x1d064, 0x1d068, 0x1d076, 0x1d08e, 0x1d09c, 0x1d0b8, 0x1d0c2, 0x1d0c4, 0x1d0c8, 0x1d0d0, 0x1d0de, 0x1d0e6, 0x1d0ec, 0x1d0fa, 0x1d11c, 0x1d138, 0x1d170, 0x1d17e, 0x1d182, 0x1d184, 0x1d188, 0x1d190, 0x1d19e, 0x1d1a0, 0x1d1bc, 0x1d1c6, 0x1d1cc, 0x1d1d8, 0x1d1ee, 0x1d1f2, 0x1d1f4, 0x1d21c, 0x1d238, 0x1d270, 0x1d27e, 0x1d2e0, 0x1d2fc, 0x1d302, 0x1d304, 0x1d308, 0x1d310, 0x1d31e, 0x1d320, 0x1d33c, 0x1d340, 0x1d378, 0x1d386, 0x1d38c, 0x1d398, 0x1d3b0, 0x1d3be, 0x1d3ce, 0x1d3dc, 0x1d3e2, 0x1d3e4, 0x1d3e8, 0x1d3f6, 0x1d470, 0x1d47e, 0x1d4e0, 0x1d4fc, 0x1d5c0, 0x1d5f8, 0x1d604, 0x1d608, 0x1d610, 0x1d620, 0x1d640, 0x1d678, 0x1d6f0, 0x1d706, 0x1d70c, 0x1d718, 0x1d730, 0x1d73e, 0x1d760, 0x1d77c, 0x1d78e, 0x1d79c, 0x1d7b8, 0x1d7c2, 0x1d7c4, 0x1d7c8, 0x1d7d0, 0x1d7de, 0x1d7e6, 0x1d7ec, 0x1d826, 0x1d82c, 0x1d83a, 0x1d846, 0x1d84c, 0x1d858, 0x1d872, 0x1d874, 0x1d886, 0x1d88c, 0x1d898, 0x1d8b0, 0x1d8be, 0x1d8ce, 0x1d8e2, 0x1d8e4, 0x1d8e8, 0x1d8f6, 0x1d90c, 0x1d918, 0x1d930, 0x1d93e, 0x1d960, 0x1d97c, 0x1d99c, 0x1d9c2, 0x1d9c4, 0x1d9c8, 0x1d9d0, 0x1d9e6, 0x1d9fa, 0x1da0c, 0x1da18, 0x1da30, 0x1da3e, 0x1da60, 0x1da7c, 0x1dac0, 0x1daf8, 0x1db38, 0x1db82, 0x1db84, 0x1db88, 0x1db90, 0x1db9e, + 0x1dba0, 0x1dbcc, 0x1dbf2, 0x1dbf4, 0x1dc22, 0x1dc42, 0x1dc44, 0x1dc48, 0x1dc50, 0x1dc5e, 0x1dc66, 0x1dc7a, 0x1dc82, 0x1dc84, 0x1dc88, 0x1dc90, 0x1dc9e, 0x1dca0, 0x1dcbc, 0x1dccc, 0x1dcf2, 0x1dcf4, 0x1dd04, 0x1dd08, 0x1dd10, 0x1dd1e, 0x1dd20, 0x1dd3c, 0x1dd40, 0x1dd78, 0x1dd86, 0x1dd98, 0x1ddce, 0x1dde2, 0x1dde4, 0x1dde8, 0x1de2e, 0x1de32, 0x1de34, 0x1de4e, 0x1de5c, 0x1de62, 0x1de64, 0x1de68, 0x1de8e, 0x1de9c, 0x1deb8, 0x1dec2, 0x1dec4, 0x1dec8, 0x1ded0, 0x1dee6, 0x1defa, 0x1df16, 0x1df26, 0x1df2c, 0x1df46, 0x1df4c, 0x1df58, 0x1df72, 0x1df74, 0x1df8a, 0x1df92, 0x1df94, 0x1dfa2, 0x1dfa4, 0x1dfa8, 0x1e08a, 0x1e092, 0x1e094, 0x1e0a2, 0x1e0a4, 0x1e0a8, 0x1e0b6, 0x1e0da, 0x1e10a, 0x1e112, 0x1e114, 0x1e122, 0x1e124, 0x1e128, 0x1e136, 0x1e142, 0x1e144, 0x1e148, 0x1e150, 0x1e166, 0x1e16c, 0x1e17a, 0x1e19a, 0x1e1b2, 0x1e1b4, 0x1e20a, 0x1e212, 0x1e214, 0x1e222, 0x1e224, 0x1e228, 0x1e236, 0x1e242, 0x1e248, 0x1e250, 0x1e25e, 0x1e266, 0x1e26c, 0x1e27a, 0x1e282, 0x1e284, 0x1e288, 0x1e290, 0x1e2a0, 0x1e2bc, 0x1e2c6, 0x1e2cc, 0x1e2d8, 0x1e2ee, 0x1e2f2, 0x1e2f4, 0x1e31a, 0x1e332, 0x1e334, 0x1e35c, 0x1e362, 0x1e364, 0x1e368, 0x1e3ba, 0x1e40a, 0x1e412, 0x1e414, 0x1e422, 0x1e428, 0x1e436, 0x1e442, 0x1e448, 0x1e450, 0x1e45e, 0x1e466, 0x1e46c, 0x1e47a, 0x1e482, 0x1e484, 0x1e490, 0x1e49e, 0x1e4a0, 0x1e4bc, 0x1e4c6, 0x1e4cc, 0x1e4d8, 0x1e4ee, 0x1e4f2, 0x1e4f4, 0x1e502, 0x1e504, 0x1e508, 0x1e510, 0x1e51e, 0x1e520, 0x1e53c, 0x1e540, 0x1e578, 0x1e586, 0x1e58c, 0x1e598, 0x1e5b0, 0x1e5be, 0x1e5ce, 0x1e5dc, 0x1e5e2, 0x1e5e4, 0x1e5e8, 0x1e5f6, 0x1e61a, 0x1e62e, 0x1e632, 0x1e634, 0x1e64e, 0x1e65c, 0x1e662, 0x1e668, 0x1e68e, 0x1e69c, 0x1e6b8, 0x1e6c2, 0x1e6c4, 0x1e6c8, 0x1e6d0, 0x1e6e6, 0x1e6fa, 0x1e716, 0x1e726, 0x1e72c, 0x1e73a, 0x1e746, 0x1e74c, 0x1e758, 0x1e772, 0x1e774, 0x1e792, 0x1e794, 0x1e7a2, 0x1e7a4, 0x1e7a8, 0x1e7b6, 0x1e812, 0x1e814, 0x1e822, 0x1e824, 0x1e828, 0x1e836, 0x1e842, 0x1e844, 0x1e848, 0x1e850, 0x1e85e, 0x1e866, 0x1e86c, 0x1e87a, 0x1e882, 0x1e884, 0x1e888, 0x1e890, 0x1e89e, 0x1e8a0, 0x1e8bc, 0x1e8c6, + 0x1e8cc, 0x1e8d8, 0x1e8ee, 0x1e8f2, 0x1e8f4, 0x1e902, 0x1e904, 0x1e908, 0x1e910, 0x1e920, 0x1e93c, 0x1e940, 0x1e978, 0x1e986, 0x1e98c, 0x1e998, 0x1e9b0, 0x1e9be, 0x1e9ce, 0x1e9dc, 0x1e9e2, 0x1e9e4, 0x1e9e8, 0x1e9f6, 0x1ea04, 0x1ea08, 0x1ea10, 0x1ea20, 0x1ea40, 0x1ea78, 0x1eaf0, 0x1eb06, 0x1eb0c, 0x1eb18, 0x1eb30, 0x1eb3e, 0x1eb60, 0x1eb7c, 0x1eb8e, 0x1eb9c, 0x1ebb8, 0x1ebc2, 0x1ebc4, 0x1ebc8, 0x1ebd0, 0x1ebde, 0x1ebe6, 0x1ebec, 0x1ec1a, 0x1ec2e, 0x1ec32, 0x1ec34, 0x1ec4e, 0x1ec5c, 0x1ec62, 0x1ec64, 0x1ec68, 0x1ec8e, 0x1ec9c, 0x1ecb8, 0x1ecc2, 0x1ecc4, 0x1ecc8, 0x1ecd0, 0x1ece6, 0x1ecfa, 0x1ed0e, 0x1ed1c, 0x1ed38, 0x1ed70, 0x1ed7e, 0x1ed82, 0x1ed84, 0x1ed88, 0x1ed90, 0x1ed9e, 0x1eda0, 0x1edcc, 0x1edf2, 0x1edf4, 0x1ee16, 0x1ee26, 0x1ee2c, 0x1ee3a, 0x1ee46, 0x1ee4c, 0x1ee58, 0x1ee6e, 0x1ee72, 0x1ee74, 0x1ee86, 0x1ee8c, 0x1ee98, 0x1eeb0, 0x1eebe, 0x1eece, 0x1eedc, 0x1eee2, 0x1eee4, 0x1eee8, 0x1ef12, 0x1ef22, 0x1ef24, 0x1ef28, 0x1ef36, 0x1ef42, 0x1ef44, 0x1ef48, 0x1ef50, 0x1ef5e, 0x1ef66, 0x1ef6c, 0x1ef7a, 0x1efae, 0x1efb2, 0x1efb4, 0x1efd6, 0x1f096, 0x1f0a6, 0x1f0ac, 0x1f0ba, 0x1f0ca, 0x1f0d2, 0x1f0d4, 0x1f116, 0x1f126, 0x1f12c, 0x1f13a, 0x1f146, 0x1f14c, 0x1f158, 0x1f16e, 0x1f172, 0x1f174, 0x1f18a, 0x1f192, 0x1f194, 0x1f1a2, 0x1f1a4, 0x1f1a8, 0x1f1da, 0x1f216, 0x1f226, 0x1f22c, 0x1f23a, 0x1f246, 0x1f258, 0x1f26e, 0x1f272, 0x1f274, 0x1f286, 0x1f28c, 0x1f298, 0x1f2b0, 0x1f2be, 0x1f2ce, 0x1f2dc, 0x1f2e2, 0x1f2e4, 0x1f2e8, 0x1f2f6, 0x1f30a, 0x1f312, 0x1f314, 0x1f322, 0x1f328, 0x1f342, 0x1f344, 0x1f348, 0x1f350, 0x1f35e, 0x1f366, 0x1f37a, 0x1f39a, 0x1f3ae, 0x1f3b2, 0x1f3b4, 0x1f416, 0x1f426, 0x1f42c, 0x1f43a, 0x1f446, 0x1f44c, 0x1f458, 0x1f46e, 0x1f472, 0x1f474, 0x1f486, 0x1f48c, 0x1f498, 0x1f4b0, 0x1f4be, 0x1f4ce, 0x1f4dc, 0x1f4e2, 0x1f4e4, 0x1f4e8, 0x1f4f6, 0x1f506, 0x1f50c, 0x1f518, 0x1f530, 0x1f53e, 0x1f560, 0x1f57c, 0x1f58e, 0x1f59c, 0x1f5b8, 0x1f5c2, 0x1f5c4, 0x1f5c8, 0x1f5d0, 0x1f5de, 0x1f5e6, 0x1f5ec, 0x1f5fa, 0x1f60a, 0x1f612, 0x1f614, 0x1f622, 0x1f624, 0x1f628, 0x1f636, 0x1f642, 0x1f644, + 0x1f648, 0x1f650, 0x1f65e, 0x1f666, 0x1f67a, 0x1f682, 0x1f684, 0x1f688, 0x1f690, 0x1f69e, 0x1f6a0, 0x1f6bc, 0x1f6cc, 0x1f6f2, 0x1f6f4, 0x1f71a, 0x1f72e, 0x1f732, 0x1f734, 0x1f74e, 0x1f75c, 0x1f762, 0x1f764, 0x1f768, 0x1f776, 0x1f796, 0x1f7a6, 0x1f7ac, 0x1f7ba, 0x1f7d2, 0x1f7d4, 0x1f89a, 0x1f8ae, 0x1f8b2, 0x1f8b4, 0x1f8d6, 0x1f8ea, 0x1f91a, 0x1f92e, 0x1f932, 0x1f934, 0x1f94e, 0x1f95c, 0x1f962, 0x1f964, 0x1f968, 0x1f976, 0x1f996, 0x1f9a6, 0x1f9ac, 0x1f9ba, 0x1f9ca, 0x1f9d2, 0x1f9d4, 0x1fa1a, 0x1fa2e, 0x1fa32, 0x1fa34, 0x1fa4e, 0x1fa5c, 0x1fa62, 0x1fa64, 0x1fa68, 0x1fa76, 0x1fa8e, 0x1fa9c, 0x1fab8, 0x1fac2, 0x1fac4, 0x1fac8, 0x1fad0, 0x1fade, 0x1fae6, 0x1faec, 0x1fb16, 0x1fb26, 0x1fb2c, 0x1fb3a, 0x1fb46, 0x1fb4c, 0x1fb58, 0x1fb6e, 0x1fb72, 0x1fb74, 0x1fb8a, 0x1fb92, 0x1fb94, 0x1fba2, 0x1fba4, 0x1fba8, 0x1fbb6, 0x1fbda}; + + /// This table contains to codewords for all symbols. + //UPGRADE_NOTE: Final was removed from the declaration of 'CODEWORD_TABLE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] CODEWORD_TABLE = new int[]{2627, 1819, 2622, 2621, 1813, 1812, 2729, 2724, 2723, 2779, 2774, 2773, 902, 896, 908, 868, 865, 861, 859, 2511, 873, 871, 1780, 835, 2493, 825, 2491, 842, 837, 844, 1764, 1762, 811, 810, 809, 2483, 807, 2482, 806, 2480, 815, 814, 813, 812, 2484, 817, 816, 1745, 1744, 1742, 1746, 2655, 2637, 2635, 2626, 2625, 2623, 2628, 1820, 2752, 2739, 2737, 2728, 2727, 2725, 2730, 2785, 2783, 2778, 2777, 2775, 2780, 787, 781, 747, 739, 736, 2413, 754, 752, 1719, 692, 689, 681, 2371, 678, 2369, 700, 697, 694, 703, 1688, 1686, 642, 638, 2343, 631, 2341, 627, 2338, 651, 646, 643, 2345, 654, 652, 1652, 1650, 1647, 1654, 601, 599, 2322, 596, 2321, 594, 2319, 2317, 611, 610, 608, 606, 2324, 603, 2323, 615, 614, 612, 1617, 1616, 1614, 1612, 616, 1619, 1618, 2575, 2538, 2536, 905, 901, 898, 909, 2509, 2507, 2504, 870, 867, 864, 860, 2512, 875, 872, 1781, 2490, 2489, 2487, 2485, 1748, 836, 834, 832, 830, 2494, 827, 2492, 843, 841, 839, 845, 1765, 1763, 2701, 2676, 2674, 2653, 2648, 2656, 2634, 2633, 2631, 2629, 1821, 2638, 2636, 2770, 2763, 2761, 2750, 2745, 2753, 2736, 2735, 2733, 2731, 1848, 2740, 2738, 2786, 2784, 591, 588, 576, 569, 566, 2296, 1590, 537, 534, 526, 2276, 522, 2274, 545, 542, 539, 548, 1572, 1570, 481, 2245, 466, 2242, 462, 2239, 492, 485, 482, 2249, 496, 494, 1534, 1531, 1528, 1538, 413, 2196, 406, 2191, 2188, 425, 419, 2202, 415, 2199, 432, 430, 427, 1472, 1467, 1464, 433, 1476, 1474, 368, 367, 2160, 365, 2159, 362, 2157, 2155, 2152, 378, 377, 375, 2166, 372, 2165, 369, 2162, 383, 381, 379, 2168, 1419, 1418, 1416, 1414, 385, 1411, 384, 1423, 1422, 1420, 1424, 2461, 802, 2441, 2439, 790, 786, 783, 794, 2409, 2406, 2403, 750, 742, 738, 2414, 756, 753, 1720, 2367, 2365, 2362, 2359, 1663, 693, 691, 684, 2373, 680, 2370, 702, 699, 696, 704, 1690, 1687, 2337, 2336, 2334, 2332, 1624, 2329, 1622, 640, 637, 2344, 634, 2342, 630, 2340, 650, 648, 645, 2346, 655, 653, 1653, 1651, 1649, 1655, 2612, 2597, 2595, 2571, 2568, 2565, 2576, 2534, 2529, 2526, 1787, + 2540, 2537, 907, 904, 900, 910, 2503, 2502, 2500, 2498, 1768, 2495, 1767, 2510, 2508, 2506, 869, 866, 863, 2513, 876, 874, 1782, 2720, 2713, 2711, 2697, 2694, 2691, 2702, 2672, 2670, 2664, 1828, 2678, 2675, 2647, 2646, 2644, 2642, 1823, 2639, 1822, 2654, 2652, 2650, 2657, 2771, 1855, 2765, 2762, 1850, 1849, 2751, 2749, 2747, 2754, 353, 2148, 344, 342, 336, 2142, 332, 2140, 345, 1375, 1373, 306, 2130, 299, 2128, 295, 2125, 319, 314, 311, 2132, 1354, 1352, 1349, 1356, 262, 257, 2101, 253, 2096, 2093, 274, 273, 267, 2107, 263, 2104, 280, 278, 275, 1316, 1311, 1308, 1320, 1318, 2052, 202, 2050, 2044, 2040, 219, 2063, 212, 2060, 208, 2055, 224, 221, 2066, 1260, 1258, 1252, 231, 1248, 229, 1266, 1264, 1261, 1268, 155, 1998, 153, 1996, 1994, 1991, 1988, 165, 164, 2007, 162, 2006, 159, 2003, 2000, 172, 171, 169, 2012, 166, 2010, 1186, 1184, 1182, 1179, 175, 1176, 173, 1192, 1191, 1189, 1187, 176, 1194, 1193, 2313, 2307, 2305, 592, 589, 2294, 2292, 2289, 578, 572, 568, 2297, 580, 1591, 2272, 2267, 2264, 1547, 538, 536, 529, 2278, 525, 2275, 547, 544, 541, 1574, 1571, 2237, 2235, 2229, 1493, 2225, 1489, 478, 2247, 470, 2244, 465, 2241, 493, 488, 484, 2250, 498, 495, 1536, 1533, 1530, 1539, 2187, 2186, 2184, 2182, 1432, 2179, 1430, 2176, 1427, 414, 412, 2197, 409, 2195, 405, 2193, 2190, 426, 424, 421, 2203, 418, 2201, 431, 429, 1473, 1471, 1469, 1466, 434, 1477, 1475, 2478, 2472, 2470, 2459, 2457, 2454, 2462, 803, 2437, 2432, 2429, 1726, 2443, 2440, 792, 789, 785, 2401, 2399, 2393, 1702, 2389, 1699, 2411, 2408, 2405, 745, 741, 2415, 758, 755, 1721, 2358, 2357, 2355, 2353, 1661, 2350, 1660, 2347, 1657, 2368, 2366, 2364, 2361, 1666, 690, 687, 2374, 683, 2372, 701, 698, 705, 1691, 1689, 2619, 2617, 2610, 2608, 2605, 2613, 2593, 2588, 2585, 1803, 2599, 2596, 2563, 2561, 2555, 1797, 2551, 1795, 2573, 2570, 2567, 2577, 2525, 2524, 2522, 2520, 1786, 2517, 1785, 2514, 1783, 2535, 2533, 2531, 2528, 1788, 2541, 2539, 906, 903, 911, 2721, 1844, 2715, 2712, 1838, 1836, 2699, 2696, 2693, 2703, 1827, 1826, 1824, 2673, + 2671, 2669, 2666, 1829, 2679, 2677, 1858, 1857, 2772, 1854, 1853, 1851, 1856, 2766, 2764, 143, 1987, 139, 1986, 135, 133, 131, 1984, 128, 1983, 125, 1981, 138, 137, 136, 1985, 1133, 1132, 1130, 112, 110, 1974, 107, 1973, 104, 1971, 1969, 122, 121, 119, 117, 1977, 114, 1976, 124, 1115, 1114, 1112, 1110, 1117, 1116, 84, 83, 1953, 81, 1952, 78, 1950, 1948, 1945, 94, 93, 91, 1959, 88, 1958, 85, 1955, 99, 97, 95, 1961, 1086, 1085, 1083, 1081, 1078, 100, 1090, 1089, 1087, 1091, 49, 47, 1917, 44, 1915, 1913, 1910, 1907, 59, 1926, 56, 1925, 53, 1922, 1919, 66, 64, 1931, 61, 1929, 1042, 1040, 1038, 71, 1035, 70, 1032, 68, 1048, 1047, 1045, 1043, 1050, 1049, 12, 10, 1869, 1867, 1864, 1861, 21, 1880, 19, 1877, 1874, 1871, 28, 1888, 25, 1886, 22, 1883, 982, 980, 977, 974, 32, 30, 991, 989, 987, 984, 34, 995, 994, 992, 2151, 2150, 2147, 2146, 2144, 356, 355, 354, 2149, 2139, 2138, 2136, 2134, 1359, 343, 341, 338, 2143, 335, 2141, 348, 347, 346, 1376, 1374, 2124, 2123, 2121, 2119, 1326, 2116, 1324, 310, 308, 305, 2131, 302, 2129, 298, 2127, 320, 318, 316, 313, 2133, 322, 321, 1355, 1353, 1351, 1357, 2092, 2091, 2089, 2087, 1276, 2084, 1274, 2081, 1271, 259, 2102, 256, 2100, 252, 2098, 2095, 272, 269, 2108, 266, 2106, 281, 279, 277, 1317, 1315, 1313, 1310, 282, 1321, 1319, 2039, 2037, 2035, 2032, 1203, 2029, 1200, 1197, 207, 2053, 205, 2051, 201, 2049, 2046, 2043, 220, 218, 2064, 215, 2062, 211, 2059, 228, 226, 223, 2069, 1259, 1257, 1254, 232, 1251, 230, 1267, 1265, 1263, 2316, 2315, 2312, 2311, 2309, 2314, 2304, 2303, 2301, 2299, 1593, 2308, 2306, 590, 2288, 2287, 2285, 2283, 1578, 2280, 1577, 2295, 2293, 2291, 579, 577, 574, 571, 2298, 582, 581, 1592, 2263, 2262, 2260, 2258, 1545, 2255, 1544, 2252, 1541, 2273, 2271, 2269, 2266, 1550, 535, 532, 2279, 528, 2277, 546, 543, 549, 1575, 1573, 2224, 2222, 2220, 1486, 2217, 1485, 2214, 1482, 1479, 2238, 2236, 2234, 2231, 1496, 2228, 1492, 480, 477, 2248, 473, 2246, 469, 2243, 490, 487, 2251, 497, 1537, 1535, 1532, 2477, 2476, 2474, 2479, 2469, 2468, 2466, 2464, 1730 + , 2473, 2471, 2453, 2452, 2450, 2448, 1729, 2445, 1728, 2460, 2458, 2456, 2463, 805, 804, 2428, 2427, 2425, 2423, 1725, 2420, 1724, 2417, 1722, 2438, 2436, 2434, 2431, 1727, 2444, 2442, 793, 791, 788, 795, 2388, 2386, 2384, 1697, 2381, 1696, 2378, 1694, 1692, 2402, 2400, 2398, 2395, 1703, 2392, 1701, 2412, 2410, 2407, 751, 748, 744, 2416, 759, 757, 1807, 2620, 2618, 1806, 1805, 2611, 2609, 2607, 2614, 1802, 1801, 1799, 2594, 2592, 2590, 2587, 1804, 2600, 2598, 1794, 1793, 1791, 1789, 2564, 2562, 2560, 2557, 1798, 2554, 1796, 2574, 2572, 2569, 2578, 1847, 1846, 2722, 1843, 1842, 1840, 1845, 2716, 2714, 1835, 1834, 1832, 1830, 1839, 1837, 2700, 2698, 2695, 2704, 1817, 1811, 1810, 897, 862, 1777, 829, 826, 838, 1760, 1758, 808, 2481, 1741, 1740, 1738, 1743, 2624, 1818, 2726, 2776, 782, 740, 737, 1715, 686, 679, 695, 1682, 1680, 639, 628, 2339, 647, 644, 1645, 1643, 1640, 1648, 602, 600, 597, 595, 2320, 593, 2318, 609, 607, 604, 1611, 1610, 1608, 1606, 613, 1615, 1613, 2328, 926, 924, 892, 886, 899, 857, 850, 2505, 1778, 824, 823, 821, 819, 2488, 818, 2486, 833, 831, 828, 840, 1761, 1759, 2649, 2632, 2630, 2746, 2734, 2732, 2782, 2781, 570, 567, 1587, 531, 527, 523, 540, 1566, 1564, 476, 467, 463, 2240, 486, 483, 1524, 1521, 1518, 1529, 411, 403, 2192, 399, 2189, 423, 416, 1462, 1457, 1454, 428, 1468, 1465, 2210, 366, 363, 2158, 360, 2156, 357, 2153, 376, 373, 370, 2163, 1410, 1409, 1407, 1405, 382, 1402, 380, 1417, 1415, 1412, 1421, 2175, 2174, 777, 774, 771, 784, 732, 725, 722, 2404, 743, 1716, 676, 674, 668, 2363, 665, 2360, 685, 1684, 1681, 626, 624, 622, 2335, 620, 2333, 617, 2330, 641, 635, 649, 1646, 1644, 1642, 2566, 928, 925, 2530, 2527, 894, 891, 888, 2501, 2499, 2496, 858, 856, 854, 851, 1779, 2692, 2668, 2665, 2645, 2643, 2640, 2651, 2768, 2759, 2757, 2744, 2743, 2741, 2748, 352, 1382, 340, 337, 333, 1371, 1369, 307, 300, 296, 2126, 315, 312, 1347, 1342, 1350, 261, 258, 250, 2097, 246, 2094, 271, 268, 264, 1306, 1301, 1298, 276, 1312, 1309, 2115, 203, 2048, 195, 2045, 191, 2041, 213, 209 + , 2056, 1246, 1244, 1238, 225, 1234, 222, 1256, 1253, 1249, 1262, 2080, 2079, 154, 1997, 150, 1995, 147, 1992, 1989, 163, 160, 2004, 156, 2001, 1175, 1174, 1172, 1170, 1167, 170, 1164, 167, 1185, 1183, 1180, 1177, 174, 1190, 1188, 2025, 2024, 2022, 587, 586, 564, 559, 556, 2290, 573, 1588, 520, 518, 512, 2268, 508, 2265, 530, 1568, 1565, 461, 457, 2233, 450, 2230, 446, 2226, 479, 471, 489, 1526, 1523, 1520, 397, 395, 2185, 392, 2183, 389, 2180, 2177, 410, 2194, 402, 422, 1463, 1461, 1459, 1456, 1470, 2455, 799, 2433, 2430, 779, 776, 773, 2397, 2394, 2390, 734, 728, 724, 746, 1717, 2356, 2354, 2351, 2348, 1658, 677, 675, 673, 670, 667, 688, 1685, 1683, 2606, 2589, 2586, 2559, 2556, 2552, 927, 2523, 2521, 2518, 2515, 1784, 2532, 895, 893, 890, 2718, 2709, 2707, 2689, 2687, 2684, 2663, 2662, 2660, 2658, 1825, 2667, 2769, 1852, 2760, 2758, 142, 141, 1139, 1138, 134, 132, 129, 126, 1982, 1129, 1128, 1126, 1131, 113, 111, 108, 105, 1972, 101, 1970, 120, 118, 115, 1109, 1108, 1106, 1104, 123, 1113, 1111, 82, 79, 1951, 75, 1949, 72, 1946, 92, 89, 86, 1956, 1077, 1076, 1074, 1072, 98, 1069, 96, 1084, 1082, 1079, 1088, 1968, 1967, 48, 45, 1916, 42, 1914, 39, 1911, 1908, 60, 57, 54, 1923, 50, 1920, 1031, 1030, 1028, 1026, 67, 1023, 65, 1020, 62, 1041, 1039, 1036, 1033, 69, 1046, 1044, 1944, 1943, 1941, 11, 9, 1868, 7, 1865, 1862, 1859, 20, 1878, 16, 1875, 13, 1872, 970, 968, 966, 963, 29, 960, 26, 23, 983, 981, 978, 975, 33, 971, 31, 990, 988, 985, 1906, 1904, 1902, 993, 351, 2145, 1383, 331, 330, 328, 326, 2137, 323, 2135, 339, 1372, 1370, 294, 293, 291, 289, 2122, 286, 2120, 283, 2117, 309, 303, 317, 1348, 1346, 1344, 245, 244, 242, 2090, 239, 2088, 236, 2085, 2082, 260, 2099, 249, 270, 1307, 1305, 1303, 1300, 1314, 189, 2038, 186, 2036, 183, 2033, 2030, 2026, 206, 198, 2047, 194, 216, 1247, 1245, 1243, 1240, 227, 1237, 1255, 2310, 2302, 2300, 2286, 2284, 2281, 565, 563, 561, 558, 575, 1589, 2261, 2259, 2256, 2253, 1542, 521, 519, 517, 514, 2270, 511, 533, 1569, 1567, 2223, 2221, 2218, 2215, 1483, 2211, + 1480, 459, 456, 453, 2232, 449, 474, 491, 1527, 1525, 1522, 2475, 2467, 2465, 2451, 2449, 2446, 801, 800, 2426, 2424, 2421, 2418, 1723, 2435, 780, 778, 775, 2387, 2385, 2382, 2379, 1695, 2375, 1693, 2396, 735, 733, 730, 727, 749, 1718, 2616, 2615, 2604, 2603, 2601, 2584, 2583, 2581, 2579, 1800, 2591, 2550, 2549, 2547, 2545, 1792, 2542, 1790, 2558, 929, 2719, 1841, 2710, 2708, 1833, 1831, 2690, 2688, 2686, 1815, 1809, 1808, 1774, 1756, 1754, 1737, 1736, 1734, 1739, 1816, 1711, 1676, 1674, 633, 629, 1638, 1636, 1633, 1641, 598, 1605, 1604, 1602, 1600, 605, 1609, 1607, 2327, 887, 853, 1775, 822, 820, 1757, 1755, 1584, 524, 1560, 1558, 468, 464, 1514, 1511, 1508, 1519, 408, 404, 400, 1452, 1447, 1444, 417, 1458, 1455, 2208, 364, 361, 358, 2154, 1401, 1400, 1398, 1396, 374, 1393, 371, 1408, 1406, 1403, 1413, 2173, 2172, 772, 726, 723, 1712, 672, 669, 666, 682, 1678, 1675, 625, 623, 621, 618, 2331, 636, 632, 1639, 1637, 1635, 920, 918, 884, 880, 889, 849, 848, 847, 846, 2497, 855, 852, 1776, 2641, 2742, 2787, 1380, 334, 1367, 1365, 301, 297, 1340, 1338, 1335, 1343, 255, 251, 247, 1296, 1291, 1288, 265, 1302, 1299, 2113, 204, 196, 192, 2042, 1232, 1230, 1224, 214, 1220, 210, 1242, 1239, 1235, 1250, 2077, 2075, 151, 148, 1993, 144, 1990, 1163, 1162, 1160, 1158, 1155, 161, 1152, 157, 1173, 1171, 1168, 1165, 168, 1181, 1178, 2021, 2020, 2018, 2023, 585, 560, 557, 1585, 516, 509, 1562, 1559, 458, 447, 2227, 472, 1516, 1513, 1510, 398, 396, 393, 390, 2181, 386, 2178, 407, 1453, 1451, 1449, 1446, 420, 1460, 2209, 769, 764, 720, 712, 2391, 729, 1713, 664, 663, 661, 659, 2352, 656, 2349, 671, 1679, 1677, 2553, 922, 919, 2519, 2516, 885, 883, 881, 2685, 2661, 2659, 2767, 2756, 2755, 140, 1137, 1136, 130, 127, 1125, 1124, 1122, 1127, 109, 106, 102, 1103, 1102, 1100, 1098, 116, 1107, 1105, 1980, 80, 76, 73, 1947, 1068, 1067, 1065, 1063, 90, 1060, 87, 1075, 1073, 1070, 1080, 1966, 1965, 46, 43, 40, 1912, 36, 1909, 1019, 1018, 1016, 1014, 58, 1011, 55, 1008, 51, 1029, 1027, 1024, 1021, 63, 1037, 1034, 1940, 1939, + 1937, 1942, 8, 1866, 4, 1863, 1, 1860, 956, 954, 952, 949, 946, 17, 14, 969, 967, 964, 961, 27, 957, 24, 979, 976, 972, 1901, 1900, 1898, 1896, 986, 1905, 1903, 350, 349, 1381, 329, 327, 324, 1368, 1366, 292, 290, 287, 284, 2118, 304, 1341, 1339, 1337, 1345, 243, 240, 237, 2086, 233, 2083, 254, 1297, 1295, 1293, 1290, 1304, 2114, 190, 187, 184, 2034, 180, 2031, 177, 2027, 199, 1233, 1231, 1229, 1226, 217, 1223, 1241, 2078, 2076, 584, 555, 554, 552, 550, 2282, 562, 1586, 507, 506, 504, 502, 2257, 499, 2254, 515, 1563, 1561, 445, 443, 441, 2219, 438, 2216, 435, 2212, 460, 454, 475, 1517, 1515, 1512, 2447, 798, 797, 2422, 2419, 770, 768, 766, 2383, 2380, 2376, 721, 719, 717, 714, 731, 1714, 2602, 2582, 2580, 2548, 2546, 2543, 923, 921, 2717, 2706, 2705, 2683, 2682, 2680, 1771, 1752, 1750, 1733, 1732, 1731, 1735, 1814, 1707, 1670, 1668, 1631, 1629, 1626, 1634, 1599, 1598, 1596, 1594, 1603, 1601, 2326, 1772, 1753, 1751, 1581, 1554, 1552, 1504, 1501, 1498, 1509, 1442, 1437, 1434, 401, 1448, 1445, 2206, 1392, 1391, 1389, 1387, 1384, 359, 1399, 1397, 1394, 1404, 2171, 2170, 1708, 1672, 1669, 619, 1632, 1630, 1628, 1773, 1378, 1363, 1361, 1333, 1328, 1336, 1286, 1281, 1278, 248, 1292, 1289, 2111, 1218, 1216, 1210, 197, 1206, 193, 1228, 1225, 1221, 1236, 2073, 2071, 1151, 1150, 1148, 1146, 152, 1143, 149, 1140, 145, 1161, 1159, 1156, 1153, 158, 1169, 1166, 2017, 2016, 2014, 2019, 1582, 510, 1556, 1553, 452, 448, 1506, 1500, 394, 391, 387, 1443, 1441, 1439, 1436, 1450, 2207, 765, 716, 713, 1709, 662, 660, 657, 1673, 1671, 916, 914, 879, 878, 877, 882, 1135, 1134, 1121, 1120, 1118, 1123, 1097, 1096, 1094, 1092, 103, 1101, 1099, 1979, 1059, 1058, 1056, 1054, 77, 1051, 74, 1066, 1064, 1061, 1071, 1964, 1963, 1007, 1006, 1004, 1002, 999, 41, 996, 37, 1017, 1015, 1012, 1009, 52, 1025, 1022, 1936, 1935, 1933, 1938, 942, 940, 938, 935, 932, 5, 2, 955, 953, 950, 947, 18, 943, 15, 965, 962, 958, 1895, 1894, 1892, 1890, 973, 1899, 1897, 1379, 325, 1364, 1362, 288, 285, 1334, 1332, 1330, 241, 238, 234, 1287, 1285, + 1283, 1280, 1294, 2112, 188, 185, 181, 178, 2028, 1219, 1217, 1215, 1212, 200, 1209, 1227, 2074, 2072, 583, 553, 551, 1583, 505, 503, 500, 513, 1557, 1555, 444, 442, 439, 436, 2213, 455, 451, 1507, 1505, 1502, 796, 763, 762, 760, 767, 711, 710, 708, 706, 2377, 718, 715, 1710, 2544, 917, 915, 2681, 1627, 1597, 1595, 2325, 1769, 1749, 1747, 1499, 1438, 1435, 2204, 1390, 1388, 1385, 1395, 2169, 2167, 1704, 1665, 1662, 1625, 1623, 1620, 1770, 1329, 1282, 1279, 2109, 1214, 1207, 1222, 2068, 2065, 1149, 1147, 1144, 1141, 146, 1157, 1154, 2013, 2011, 2008, 2015, 1579, 1549, 1546, 1495, 1487, 1433, 1431, 1428, 1425, 388, 1440, 2205, 1705, 658, 1667, 1664, 1119, 1095, 1093, 1978, 1057, 1055, 1052, 1062, 1962, 1960, 1005, 1003, 1000, 997, 38, 1013, 1010, 1932, 1930, 1927, 1934, 941, 939, 936, 933, 6, 930, 3, 951, 948, 944, 1889, 1887, 1884, 1881, 959, 1893, 1891, 35, 1377, 1360, 1358, 1327, 1325, 1322, 1331, 1277, 1275, 1272, 1269, 235, 1284, 2110, 1205, 1204, 1201, 1198, 182, 1195, 179, 1213, 2070, 2067, 1580, 501, 1551, 1548, 440, 437, 1497, 1494, 1490, 1503, 761, 709, 707, 1706, 913, 912, 2198, 1386, 2164, 2161, 1621, 1766, 2103, 1208, 2058, 2054, 1145, 1142, 2005, 2002, 1999, 2009, 1488, 1429, 1426, 2200, 1698, 1659, 1656, 1975, 1053, 1957, 1954, 1001, 998, 1924, 1921, 1918, 1928, 937, 934, 931, 1879, 1876, 1873, 1870, 945, 1885, 1882, 1323, 1273, 1270, 2105, 1202, 1199, 1196, 1211, 2061, 2057, 1576, 1543, 1540, 1484, 1481, 1478, 1491, 1700}; + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/DecodedBitStreamParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/DecodedBitStreamParser.cs new file mode 100644 index 0000000..f3bbaf6 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/DecodedBitStreamParser.cs @@ -0,0 +1,735 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using DecoderResult = com.google.zxing.common.DecoderResult; +namespace com.google.zxing.pdf417.decoder +{ + + ///

This class contains the methods for decoding the PDF417 codewords.

+ /// + ///
+ /// SITA Lab (kevin.osullivan@sita.aero) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class DecodedBitStreamParser + { + + private const int TEXT_COMPACTION_MODE_LATCH = 900; + private const int BYTE_COMPACTION_MODE_LATCH = 901; + private const int NUMERIC_COMPACTION_MODE_LATCH = 902; + private const int BYTE_COMPACTION_MODE_LATCH_6 = 924; + private const int BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928; + private const int BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923; + private const int MACRO_PDF417_TERMINATOR = 922; + private const int MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913; + private const int MAX_NUMERIC_CODEWORDS = 15; + + private const int ALPHA = 0; + private const int LOWER = 1; + private const int MIXED = 2; + private const int PUNCT = 3; + private const int PUNCT_SHIFT = 4; + + private const int PL = 25; + private const int LL = 27; + private const int AS = 27; + private const int ML = 28; + private const int AL = 28; + private const int PS = 29; + private const int PAL = 29; + + //UPGRADE_NOTE: Final was removed from the declaration of 'PUNCT_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] PUNCT_CHARS = new char[]{';', '<', '>', '@', '[', (char) (92), '}', '_', (char) (96), '~', '!', (char) (13), (char) (9), ',', ':', (char) (10), '-', '.', '$', '/', (char) (34), '|', '*', '(', ')', '?', '{', '}', (char) (39)}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'MIXED_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] MIXED_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&', (char) (13), (char) (9), ',', ':', '#', '-', '.', '$', '/', '+', '%', '*', '=', '^'}; + + // Table containing values for the exponent of 900. + // This is used in the numeric compaction decode algorithm. + //UPGRADE_NOTE: Final was removed from the declaration of 'EXP900'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly System.String[] EXP900 = new System.String[]{"000000000000000000000000000000000000000000001", "000000000000000000000000000000000000000000900", "000000000000000000000000000000000000000810000", "000000000000000000000000000000000000729000000", "000000000000000000000000000000000656100000000", "000000000000000000000000000000590490000000000", "000000000000000000000000000531441000000000000", "000000000000000000000000478296900000000000000", "000000000000000000000430467210000000000000000", "000000000000000000387420489000000000000000000", "000000000000000348678440100000000000000000000", "000000000000313810596090000000000000000000000", "000000000282429536481000000000000000000000000", "000000254186582832900000000000000000000000000", "000228767924549610000000000000000000000000000", "205891132094649000000000000000000000000000000"}; + + private DecodedBitStreamParser() + { + } + + internal static DecoderResult decode(int[] codewords) + { + System.Text.StringBuilder result = new System.Text.StringBuilder(100); + // Get compaction mode + int codeIndex = 1; + int code = codewords[codeIndex++]; + while (codeIndex < codewords[0]) + { + switch (code) + { + + case TEXT_COMPACTION_MODE_LATCH: { + codeIndex = textCompaction(codewords, codeIndex, result); + break; + } + + case BYTE_COMPACTION_MODE_LATCH: { + codeIndex = byteCompaction(code, codewords, codeIndex, result); + break; + } + + case NUMERIC_COMPACTION_MODE_LATCH: { + codeIndex = numericCompaction(codewords, codeIndex, result); + break; + } + + case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: { + codeIndex = byteCompaction(code, codewords, codeIndex, result); + break; + } + + case BYTE_COMPACTION_MODE_LATCH_6: { + codeIndex = byteCompaction(code, codewords, codeIndex, result); + break; + } + + default: { + // Default to text compaction. During testing numerous barcodes + // appeared to be missing the starting mode. In these cases defaulting + // to text compaction seems to work. + codeIndex--; + codeIndex = textCompaction(codewords, codeIndex, result); + break; + } + + } + if (codeIndex < codewords.Length) + { + code = codewords[codeIndex++]; + } + else + { + throw ReaderException.Instance; + } + } + return new DecoderResult(null, result.ToString(), null, null); + } + + /// Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters to be + /// encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV), as + /// well as selected control characters. + /// + /// + /// The array of codewords (data + error) + /// + /// The current index into the codeword array. + /// + /// The decoded data is appended to the result. + /// + /// The next index into the codeword array. + /// + private static int textCompaction(int[] codewords, int codeIndex, System.Text.StringBuilder result) + { + // 2 character per codeword + int[] textCompactionData = new int[codewords[0] << 1]; + // Used to hold the byte compaction value if there is a mode shift + int[] byteCompactionData = new int[codewords[0] << 1]; + + int index = 0; + bool end = false; + while ((codeIndex < codewords[0]) && !end) + { + int code = codewords[codeIndex++]; + if (code < TEXT_COMPACTION_MODE_LATCH) + { + textCompactionData[index] = code / 30; + textCompactionData[index + 1] = code % 30; + index += 2; + } + else + { + switch (code) + { + + case TEXT_COMPACTION_MODE_LATCH: { + codeIndex--; + end = true; + break; + } + + case BYTE_COMPACTION_MODE_LATCH: { + codeIndex--; + end = true; + break; + } + + case NUMERIC_COMPACTION_MODE_LATCH: { + codeIndex--; + end = true; + break; + } + + case MODE_SHIFT_TO_BYTE_COMPACTION_MODE: { + // The Mode Shift codeword 913 shall cause a temporary + // switch from Text Compaction mode to Byte Compaction mode. + // This switch shall be in effect for only the next codeword, + // after which the mode shall revert to the prevailing sub-mode + // of the Text Compaction mode. Codeword 913 is only available + // in Text Compaction mode; its use is described in 5.4.2.4. + textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE; + byteCompactionData[index] = code; //Integer.toHexString(code); + index++; + break; + } + + case BYTE_COMPACTION_MODE_LATCH_6: { + codeIndex--; + end = true; + break; + } + } + } + } + decodeTextCompaction(textCompactionData, byteCompactionData, index, result); + return codeIndex; + } + + /// The Text Compaction mode includes all the printable ASCII characters + /// (i.e. values from 32 to 126) and three ASCII control characters: HT or tab + /// (ASCII value 9), LF or line feed (ASCII value 10), and CR or carriage + /// return (ASCII value 13). The Text Compaction mode also includes various latch + /// and shift characters which are used exclusively within the mode. The Text + /// Compaction mode encodes up to 2 characters per codeword. The compaction rules + /// for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-mode + /// switches are defined in 5.4.2.3. + /// + /// + /// The text compaction data. + /// + /// The byte compaction data if there + /// was a mode shift. + /// + /// The size of the text compaction and byte compaction data. + /// + /// The decoded data is appended to the result. + /// + private static void decodeTextCompaction(int[] textCompactionData, int[] byteCompactionData, int length, System.Text.StringBuilder result) + { + // Beginning from an initial state of the Alpha sub-mode + // The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text + // Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword from another mode to the Text + // Compaction mode shall always switch to the Text Compaction Alpha sub-mode. + int subMode = ALPHA; + int priorToShiftMode = ALPHA; + int i = 0; + while (i < length) + { + int subModeCh = textCompactionData[i]; + char ch = (char) (0); + switch (subMode) + { + + case ALPHA: + // Alpha (uppercase alphabetic) + if (subModeCh < 26) + { + // Upper case Alpha Character + ch = (char) ('A' + subModeCh); + } + else + { + if (subModeCh == 26) + { + ch = ' '; + } + else if (subModeCh == LL) + { + subMode = LOWER; + } + else if (subModeCh == ML) + { + subMode = MIXED; + } + else if (subModeCh == PS) + { + // Shift to punctuation + priorToShiftMode = subMode; + subMode = PUNCT_SHIFT; + } + else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) + { + result.Append((char) byteCompactionData[i]); + } + } + break; + + + case LOWER: + // Lower (lowercase alphabetic) + if (subModeCh < 26) + { + ch = (char) ('a' + subModeCh); + } + else + { + if (subModeCh == 26) + { + ch = ' '; + } + else if (subModeCh == AL) + { + subMode = ALPHA; + } + else if (subModeCh == ML) + { + subMode = MIXED; + } + else if (subModeCh == PS) + { + // Shift to punctuation + priorToShiftMode = subMode; + subMode = PUNCT_SHIFT; + } + else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) + { + result.Append((char) byteCompactionData[i]); + } + } + break; + + + case MIXED: + // Mixed (numeric and some punctuation) + if (subModeCh < PL) + { + ch = MIXED_CHARS[subModeCh]; + } + else + { + if (subModeCh == PL) + { + subMode = PUNCT; + } + else if (subModeCh == 26) + { + ch = ' '; + } + else if (subModeCh == AS) + { + //mode_change = true; + } + else if (subModeCh == AL) + { + subMode = ALPHA; + } + else if (subModeCh == PS) + { + // Shift to punctuation + priorToShiftMode = subMode; + subMode = PUNCT_SHIFT; + } + else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) + { + result.Append((char) byteCompactionData[i]); + } + } + break; + + + case PUNCT: + // Punctuation + if (subModeCh < PS) + { + ch = PUNCT_CHARS[subModeCh]; + } + else + { + if (subModeCh == PAL) + { + subMode = ALPHA; + } + else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) + { + result.Append((char) byteCompactionData[i]); + } + } + break; + + + case PUNCT_SHIFT: + // Restore sub-mode + subMode = priorToShiftMode; + if (subModeCh < PS) + { + ch = PUNCT_CHARS[subModeCh]; + } + else + { + if (subModeCh == PAL) + { + subMode = ALPHA; + } + } + break; + } + if (ch != 0) + { + // Append decoded character to result + result.Append(ch); + } + i++; + } + } + + /// Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded. + /// This includes all ASCII characters value 0 to 127 inclusive and provides for international + /// character set support. + /// + /// + /// The byte compaction mode i.e. 901 or 924 + /// + /// The array of codewords (data + error) + /// + /// The current index into the codeword array. + /// + /// The decoded data is appended to the result. + /// + /// The next index into the codeword array. + /// + private static int byteCompaction(int mode, int[] codewords, int codeIndex, System.Text.StringBuilder result) + { + if (mode == BYTE_COMPACTION_MODE_LATCH) + { + // Total number of Byte Compaction characters to be encoded + // is not a multiple of 6 + int count = 0; + long value_Renamed = 0; + char[] decodedData = new char[6]; + int[] byteCompactedCodewords = new int[6]; + bool end = false; + while ((codeIndex < codewords[0]) && !end) + { + int code = codewords[codeIndex++]; + if (code < TEXT_COMPACTION_MODE_LATCH) + { + byteCompactedCodewords[count] = code; + count++; + // Base 900 + value_Renamed *= 900; + value_Renamed += code; + } + else + { + if ((code == TEXT_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) || (code == NUMERIC_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH_6) || (code == BEGIN_MACRO_PDF417_CONTROL_BLOCK) || (code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) || (code == MACRO_PDF417_TERMINATOR)) + { + } + codeIndex--; + end = true; + } + if ((count % 5 == 0) && (count > 0)) + { + // Decode every 5 codewords + // Convert to Base 256 + for (int j = 0; j < 6; ++j) + { + decodedData[5 - j] = (char) (value_Renamed % 256); + value_Renamed >>= 8; + } + result.Append(decodedData); + count = 0; + } + } + // If Byte Compaction mode is invoked with codeword 901, + // the final group of codewords is interpreted directly + // as one byte per codeword, without compaction. + for (int i = ((count / 5) * 5); i < count; i++) + { + result.Append((char) byteCompactedCodewords[i]); + } + } + else if (mode == BYTE_COMPACTION_MODE_LATCH_6) + { + // Total number of Byte Compaction characters to be encoded + // is an integer multiple of 6 + int count = 0; + long value_Renamed = 0; + bool end = false; + while ((codeIndex < codewords[0]) && !end) + { + int code = codewords[codeIndex++]; + if (code < TEXT_COMPACTION_MODE_LATCH) + { + count += 1; + // Base 900 + value_Renamed *= 900; + value_Renamed += code; + } + else + { + if ((code == TEXT_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) || (code == NUMERIC_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH_6) || (code == BEGIN_MACRO_PDF417_CONTROL_BLOCK) || (code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) || (code == MACRO_PDF417_TERMINATOR)) + { + } + codeIndex--; + end = true; + } + if ((count % 5 == 0) && (count > 0)) + { + // Decode every 5 codewords + // Convert to Base 256 + char[] decodedData = new char[6]; + for (int j = 0; j < 6; ++j) + { + decodedData[5 - j] = (char) (value_Renamed % 256); + value_Renamed >>= 8; + } + result.Append(decodedData); + } + } + } + return codeIndex; + } + + /// Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric data strings. + /// + /// + /// The array of codewords (data + error) + /// + /// The current index into the codeword array. + /// + /// The decoded data is appended to the result. + /// + /// The next index into the codeword array. + /// + private static int numericCompaction(int[] codewords, int codeIndex, System.Text.StringBuilder result) + { + int count = 0; + bool end = false; + + int[] numericCodewords = new int[MAX_NUMERIC_CODEWORDS]; + + while ((codeIndex < codewords.Length) && !end) + { + int code = codewords[codeIndex++]; + if (code < TEXT_COMPACTION_MODE_LATCH) + { + numericCodewords[count] = code; + count++; + } + else + { + if ((code == TEXT_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH) || (code == BYTE_COMPACTION_MODE_LATCH_6) || (code == BEGIN_MACRO_PDF417_CONTROL_BLOCK) || (code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) || (code == MACRO_PDF417_TERMINATOR)) + { + } + codeIndex--; + end = true; + } + if ((count % MAX_NUMERIC_CODEWORDS) == 0 || code == NUMERIC_COMPACTION_MODE_LATCH) + { + // Re-invoking Numeric Compaction mode (by using codeword 902 + // while in Numeric Compaction mode) serves to terminate the + // current Numeric Compaction mode grouping as described in 5.4.4.2, + // and then to start a new one grouping. + System.String s = decodeBase900toBase10(numericCodewords, count); + result.Append(s); + count = 0; + } + } + return codeIndex; + } + + /// Convert a list of Numeric Compacted codewords from Base 900 to Base 10. + /// + /// + /// The array of codewords + /// + /// The number of codewords + /// + /// The decoded string representing the Numeric data. + /// + /* + EXAMPLE + Encode the fifteen digit numeric string 000213298174000 + Prefix the numeric string with a 1 and set the initial value of + t = 1 000 213 298 174 000 + Calculate codeword 0 + d0 = 1 000 213 298 174 000 mod 900 = 200 + + t = 1 000 213 298 174 000 div 900 = 1 111 348 109 082 + Calculate codeword 1 + d1 = 1 111 348 109 082 mod 900 = 282 + + t = 1 111 348 109 082 div 900 = 1 234 831 232 + Calculate codeword 2 + d2 = 1 234 831 232 mod 900 = 632 + + t = 1 234 831 232 div 900 = 1 372 034 + Calculate codeword 3 + d3 = 1 372 034 mod 900 = 434 + + t = 1 372 034 div 900 = 1 524 + Calculate codeword 4 + d4 = 1 524 mod 900 = 624 + + t = 1 524 div 900 = 1 + Calculate codeword 5 + d5 = 1 mod 900 = 1 + t = 1 div 900 = 0 + Codeword sequence is: 1, 624, 434, 632, 282, 200 + + Decode the above codewords involves + 1 x 900 power of 5 + 624 x 900 power of 4 + 434 x 900 power of 3 + + 632 x 900 power of 2 + 282 x 900 power of 1 + 200 x 900 power of 0 = 1000213298174000 + + Remove leading 1 => Result is 000213298174000 + + As there are huge numbers involved here we must use fake out the maths using string + tokens for the numbers. + BigDecimal is not supported by J2ME. + */ + private static System.String decodeBase900toBase10(int[] codewords, int count) + { + System.Text.StringBuilder accum = null; + for (int i = 0; i < count; i++) + { + System.Text.StringBuilder value_Renamed = multiply(EXP900[count - i - 1], codewords[i]); + if (accum == null) + { + // First time in accum=0 + accum = value_Renamed; + } + else + { + accum = add(accum.ToString(), value_Renamed.ToString()); + } + } + System.String result = null; + // Remove leading '1' which was inserted to preserve + // leading zeros + for (int i = 0; i < accum.Length; i++) + { + if (accum[i] == '1') + { + //result = accum.substring(i + 1); + result = accum.ToString().Substring(i + 1); + break; + } + } + if (result == null) + { + // No leading 1 => just write the converted number. + result = accum.ToString(); + } + return result; + } + + /// Multiplies two String numbers + /// + /// + /// Any number represented as a string. + /// + /// A number <= 999. + /// + /// the result of value1 * value2. + /// + private static System.Text.StringBuilder multiply(System.String value1, int value2) + { + System.Text.StringBuilder result = new System.Text.StringBuilder(value1.Length); + for (int i = 0; i < value1.Length; i++) + { + // Put zeros into the result. + result.Append('0'); + } + int hundreds = value2 / 100; + int tens = (value2 / 10) % 10; + int ones = value2 % 10; + // Multiply by ones + for (int j = 0; j < ones; j++) + { + result = add(result.ToString(), value1); + } + // Multiply by tens + for (int j = 0; j < tens; j++) + { + result = add(result.ToString(), (value1 + '0').Substring(1)); + } + // Multiply by hundreds + for (int j = 0; j < hundreds; j++) + { + result = add(result.ToString(), (value1 + "00").Substring(2)); + } + return result; + } + + /// Add two numbers which are represented as strings. + /// + /// + /// + /// + /// + /// + /// the result of value1 + value2 + /// + private static System.Text.StringBuilder add(System.String value1, System.String value2) + { + System.Text.StringBuilder temp1 = new System.Text.StringBuilder(5); + System.Text.StringBuilder temp2 = new System.Text.StringBuilder(5); + System.Text.StringBuilder result = new System.Text.StringBuilder(value1.Length); + for (int i = 0; i < value1.Length; i++) + { + // Put zeros into the result. + result.Append('0'); + } + int carry = 0; + for (int i = value1.Length - 3; i > - 1; i -= 3) + { + + temp1.Length = 0; + temp1.Append(value1[i]); + temp1.Append(value1[i + 1]); + temp1.Append(value1[i + 2]); + + temp2.Length = 0; + temp2.Append(value2[i]); + temp2.Append(value2[i + 1]); + temp2.Append(value2[i + 2]); + + int intValue1 = System.Int32.Parse(temp1.ToString()); + int intValue2 = System.Int32.Parse(temp2.ToString()); + + int sumval = (intValue1 + intValue2 + carry) % 1000; + carry = (intValue1 + intValue2 + carry) / 1000; + + result[i + 2] = (char) ((sumval % 10) + '0'); + result[i + 1] = (char) (((sumval / 10) % 10) + '0'); + result[i] = (char) ((sumval / 100) + '0'); + } + return result; + } + + /* + private static String decodeBase900toBase10(int codewords[], int count) { + BigInteger accum = BigInteger.valueOf(0); + BigInteger value = null; + for (int i = 0; i < count; i++) { + value = BigInteger.valueOf(900).pow(count - i - 1); + value = value.multiply(BigInteger.valueOf(codewords[i])); + accum = accum.add(value); + } + if (debug) System.out.println("Big Integer " + accum); + String result = accum.toString().substring(1); + return result; + } + */ + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/Decoder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/Decoder.cs new file mode 100644 index 0000000..d3fc47f --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/decoder/Decoder.cs @@ -0,0 +1,171 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DecoderResult = com.google.zxing.common.DecoderResult; +namespace com.google.zxing.pdf417.decoder +{ + //import com.google.zxing.pdf417.reedsolomon.ReedSolomonDecoder; + + ///

The main class which implements PDF417 Code decoding -- as + /// opposed to locating and extracting the PDF417 Code from an image.

+ /// + ///
+ /// SITA Lab (kevin.osullivan@sita.aero) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Decoder + { + + private const int MAX_ERRORS = 3; + private const int MAX_EC_CODEWORDS = 512; + //private final ReedSolomonDecoder rsDecoder; + + public Decoder() + { + // TODO MGMG + //rsDecoder = new ReedSolomonDecoder(); + } + + ///

Convenience method that can decode a PDF417 Code represented as a 2D array of booleans. + /// "true" is taken to mean a black module.

+ /// + ///
+ /// booleans representing white/black PDF417 modules + /// + /// text and bytes encoded within the PDF417 Code + /// + /// ReaderException if the PDF417 Code cannot be decoded + public DecoderResult decode(bool[][] image) + { + int dimension = image.Length; + BitMatrix bits = new BitMatrix(dimension); + for (int i = 0; i < dimension; i++) + { + for (int j = 0; j < dimension; j++) + { + if (image[j][i]) + { + bits.set_Renamed(j, i); + } + } + } + return decode(bits); + } + + ///

Decodes a PDF417 Code represented as a {@link BitMatrix}. + /// A 1 or "true" is taken to mean a black module.

+ /// + ///
+ /// booleans representing white/black PDF417 Code modules + /// + /// text and bytes encoded within the PDF417 Code + /// + /// ReaderException if the PDF417 Code cannot be decoded + public DecoderResult decode(BitMatrix bits) + { + // Construct a parser to read the data codewords and error-correction level + BitMatrixParser parser = new BitMatrixParser(bits); + int[] codewords = parser.readCodewords(); + if (codewords == null || codewords.Length == 0) + { + throw ReaderException.Instance; + } + + int ecLevel = parser.ECLevel; + int numECCodewords = 1 << (ecLevel + 1); + int[] erasures = parser.Erasures; + + correctErrors(codewords, erasures, numECCodewords); + verifyCodewordCount(codewords, numECCodewords); + + // Decode the codewords + return DecodedBitStreamParser.decode(codewords); + } + + /// Verify that all is OK with the codeword array. + /// + /// + /// + /// + /// an index to the first data codeword. + /// + /// ReaderException + private static void verifyCodewordCount(int[] codewords, int numECCodewords) + { + if (codewords.Length < 4) + { + // Codeword array size should be at least 4 allowing for + // Count CW, At least one Data CW, Error Correction CW, Error Correction CW + throw ReaderException.Instance; + } + // The first codeword, the Symbol Length Descriptor, shall always encode the total number of data + // codewords in the symbol, including the Symbol Length Descriptor itself, data codewords and pad + // codewords, but excluding the number of error correction codewords. + int numberOfCodewords = codewords[0]; + if (numberOfCodewords > codewords.Length) + { + throw ReaderException.Instance; + } + if (numberOfCodewords == 0) + { + // Reset to the length of the array - 8 (Allow for at least level 3 Error Correction (8 Error Codewords) + if (numECCodewords < codewords.Length) + { + codewords[0] = codewords.Length - numECCodewords; + } + else + { + throw ReaderException.Instance; + } + } + } + + ///

Given data and error-correction codewords received, possibly corrupted by errors, attempts to + /// correct the errors in-place using Reed-Solomon error correction.

+ /// + ///
+ /// data and error correction codewords + /// + /// ReaderException if error correction fails + private static int correctErrors(int[] codewords, int[] erasures, int numECCodewords) + { + if ((erasures != null && erasures.Length > numECCodewords / 2 + MAX_ERRORS) || (numECCodewords < 0 || numECCodewords > MAX_EC_CODEWORDS)) + { + // Too many errors or EC Codewords is corrupted + throw ReaderException.Instance; + } + // Try to correct the errors + int result = 0; // rsDecoder.correctErrors(codewords, numECCodewords); + if (erasures != null) + { + int numErasures = erasures.Length; + if (result > 0) + { + numErasures -= result; + } + if (numErasures > MAX_ERRORS) + { + // Still too many errors + throw ReaderException.Instance; + } + } + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/detector/Detector.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/detector/Detector.cs new file mode 100644 index 0000000..16e4758 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/pdf417/detector/Detector.cs @@ -0,0 +1,575 @@ +/* +* Copyright 2009 ZXing authors +* +* 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. +*/ +using System; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DetectorResult = com.google.zxing.common.DetectorResult; +using GridSampler = com.google.zxing.common.GridSampler; +namespace com.google.zxing.pdf417.detector +{ + + ///

Encapsulates logic that can detect a PDF417 Code in an image, even if the + /// PDF417 Code is rotated or skewed, or partially obscured.

+ /// + ///
+ /// SITA Lab (kevin.osullivan@sita.aero) + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Detector + { + + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static int MAX_AVG_VARIANCE = (int) SupportClass.Identity(((1 << 8) * 0.42f)); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + private static int MAX_INDIVIDUAL_VARIANCE = (int) SupportClass.Identity(((1 << 8) * 0.8f)); + private const int SKEW_THRESHOLD = 2; + + // B S B S B S B S Bar/Space pattern + // 11111111 0 1 0 1 0 1 000 + //UPGRADE_NOTE: Final was removed from the declaration of 'START_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] START_PATTERN = new int[]{8, 1, 1, 1, 1, 1, 1, 3}; + + // 11111111 0 1 0 1 0 1 000 + //UPGRADE_NOTE: Final was removed from the declaration of 'START_PATTERN_REVERSE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] START_PATTERN_REVERSE = new int[]{3, 1, 1, 1, 1, 1, 1, 8}; + + // 1111111 0 1 000 1 0 1 00 1 + //UPGRADE_NOTE: Final was removed from the declaration of 'STOP_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] STOP_PATTERN = new int[]{7, 1, 1, 3, 1, 1, 1, 2, 1}; + + // B S B S B S B S B Bar/Space pattern + // 1111111 0 1 000 1 0 1 00 1 + //UPGRADE_NOTE: Final was removed from the declaration of 'STOP_PATTERN_REVERSE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] STOP_PATTERN_REVERSE = new int[]{1, 2, 1, 1, 1, 3, 1, 1, 7}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BinaryBitmap image; + + public Detector(BinaryBitmap image) + { + this.image = image; + } + + ///

Detects a PDF417 Code in an image, simply.

+ /// + ///
+ /// {@link DetectorResult} encapsulating results of detecting a PDF417 Code + /// + /// ReaderException if no QR Code can be found + public DetectorResult detect() + { + return detect(null); + } + + ///

Detects a PDF417 Code in an image. Only checks 0 and 180 degree rotations.

+ /// + ///
+ /// optional hints to detector + /// + /// {@link DetectorResult} encapsulating results of detecting a PDF417 Code + /// + /// ReaderException if no PDF417 Code can be found + // public DetectorResult detect(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public DetectorResult detect(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + // Fetch the 1 bit matrix once up front. + BitMatrix matrix = image.BlackMatrix; + + // Try to find the vertices assuming the image is upright. + ResultPoint[] vertices = findVertices(matrix); + if (vertices == null) + { + // Maybe the image is rotated 180 degrees? + vertices = findVertices180(matrix); + if (vertices != null) + { + correctCodeWordVertices(vertices, true); + } + } + else + { + correctCodeWordVertices(vertices, false); + } + + if (vertices != null) + { + float moduleWidth = computeModuleWidth(vertices); + if (moduleWidth < 1.0f) + { + throw ReaderException.Instance; + } + + int dimension = computeDimension(vertices[4], vertices[6], vertices[5], vertices[7], moduleWidth); + if (dimension < 1) + { + throw ReaderException.Instance; + } + + // Deskew and sample image. + BitMatrix bits = sampleGrid(matrix, vertices[4], vertices[5], vertices[6], vertices[7], dimension); + return new DetectorResult(bits, new ResultPoint[]{vertices[4], vertices[5], vertices[6], vertices[7]}); + } + else + { + throw ReaderException.Instance; + } + } + + /// Locate the vertices and the codewords area of a black blob using the Start + /// and Stop patterns as locators. Assumes that the barcode begins in the left half + /// of the image, and ends in the right half. + /// TODO: Fix this assumption, allowing the barcode to be anywhere in the image. + /// TODO: Scanning every row is very expensive. We should only do this for TRY_HARDER. + /// + /// + /// the scanned barcode image. + /// + /// an array containing the vertices: + /// vertices[0] x, y top left barcode + /// vertices[1] x, y bottom left barcode + /// vertices[2] x, y top right barcode + /// vertices[3] x, y bottom right barcode + /// vertices[4] x, y top left codeword area + /// vertices[5] x, y bottom left codeword area + /// vertices[6] x, y top right codeword area + /// vertices[7] x, y bottom right codeword area + /// + private static ResultPoint[] findVertices(BitMatrix matrix) + { + int height = matrix.Height; + int width = matrix.Width; + int halfWidth = width >> 1; + + ResultPoint[] result = new ResultPoint[8]; + bool found = false; + + // Top Left + for (int i = 0; i < height; i++) + { + int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, START_PATTERN); + if (loc != null) + { + result[0] = new ResultPoint(loc[0], i); + result[4] = new ResultPoint(loc[1], i); + found = true; + break; + } + } + // Bottom left + if (found) + { + // Found the Top Left vertex + found = false; + for (int i = height - 1; i > 0; i--) + { + int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, START_PATTERN); + if (loc != null) + { + result[1] = new ResultPoint(loc[0], i); + result[5] = new ResultPoint(loc[1], i); + found = true; + break; + } + } + } + // Top right + if (found) + { + // Found the Bottom Left vertex + found = false; + for (int i = 0; i < height; i++) + { + int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, false, STOP_PATTERN); + if (loc != null) + { + result[2] = new ResultPoint(loc[1], i); + result[6] = new ResultPoint(loc[0], i); + found = true; + break; + } + } + } + // Bottom right + if (found) + { + // Found the Top right vertex + found = false; + for (int i = height - 1; i > 0; i--) + { + int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, false, STOP_PATTERN); + if (loc != null) + { + result[3] = new ResultPoint(loc[1], i); + result[7] = new ResultPoint(loc[0], i); + found = true; + break; + } + } + } + return found?result:null; + } + + /// Locate the vertices and the codewords area of a black blob using the Start + /// and Stop patterns as locators. This assumes that the image is rotated 180 + /// degrees and if it locates the start and stop patterns at it will re-map + /// the vertices for a 0 degree rotation. + /// TODO: Change assumption about barcode location. + /// TODO: Scanning every row is very expensive. We should only do this for TRY_HARDER. + /// + /// + /// the scanned barcode image. + /// + /// an array containing the vertices: + /// vertices[0] x, y top left barcode + /// vertices[1] x, y bottom left barcode + /// vertices[2] x, y top right barcode + /// vertices[3] x, y bottom right barcode + /// vertices[4] x, y top left codeword area + /// vertices[5] x, y bottom left codeword area + /// vertices[6] x, y top right codeword area + /// vertices[7] x, y bottom right codeword area + /// + private static ResultPoint[] findVertices180(BitMatrix matrix) + { + int height = matrix.Height; + int width = matrix.Width; + int halfWidth = width >> 1; + + ResultPoint[] result = new ResultPoint[8]; + bool found = false; + + // Top Left + for (int i = height - 1; i > 0; i--) + { + int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); + if (loc != null) + { + result[0] = new ResultPoint(loc[1], i); + result[4] = new ResultPoint(loc[0], i); + found = true; + break; + } + } + // Bottom Left + if (found) + { + // Found the Top Left vertex + found = false; + for (int i = 0; i < height; i++) + { + int[] loc = findGuardPattern(matrix, halfWidth, i, halfWidth, true, START_PATTERN_REVERSE); + if (loc != null) + { + result[1] = new ResultPoint(loc[1], i); + result[5] = new ResultPoint(loc[0], i); + found = true; + break; + } + } + } + // Top Right + if (found) + { + // Found the Bottom Left vertex + found = false; + for (int i = height - 1; i > 0; i--) + { + int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); + if (loc != null) + { + result[2] = new ResultPoint(loc[0], i); + result[6] = new ResultPoint(loc[1], i); + found = true; + break; + } + } + } + // Bottom Right + if (found) + { + // Found the Top Right vertex + found = false; + for (int i = 0; i < height; i++) + { + int[] loc = findGuardPattern(matrix, 0, i, halfWidth, false, STOP_PATTERN_REVERSE); + if (loc != null) + { + result[3] = new ResultPoint(loc[0], i); + result[7] = new ResultPoint(loc[1], i); + found = true; + break; + } + } + } + return found?result:null; + } + + /// Because we scan horizontally to detect the start and stop patterns, the vertical component of + /// the codeword coordinates will be slightly wrong if there is any skew or rotation in the image. + /// This method moves those points back onto the edges of the theoretically perfect bounding + /// quadrilateral if needed. + /// + /// + /// The eight vertices located by findVertices(). + /// + private static void correctCodeWordVertices(ResultPoint[] vertices, bool upsideDown) + { + float skew = vertices[4].Y - vertices[6].Y; + if (upsideDown) + { + skew = - skew; + } + if (skew > SKEW_THRESHOLD) + { + // Fix v4 + float length = vertices[4].X - vertices[0].X; + float deltax = vertices[6].X - vertices[0].X; + float deltay = vertices[6].Y - vertices[0].Y; + float correction = length * deltay / deltax; + vertices[4] = new ResultPoint(vertices[4].X, vertices[4].Y + correction); + } + else if (- skew > SKEW_THRESHOLD) + { + // Fix v6 + float length = vertices[2].X - vertices[6].X; + float deltax = vertices[2].X - vertices[4].X; + float deltay = vertices[2].Y - vertices[4].Y; + float correction = length * deltay / deltax; + vertices[6] = new ResultPoint(vertices[6].X, vertices[6].Y - correction); + } + + skew = vertices[7].Y - vertices[5].Y; + if (upsideDown) + { + skew = - skew; + } + if (skew > SKEW_THRESHOLD) + { + // Fix v5 + float length = vertices[5].X - vertices[1].X; + float deltax = vertices[7].X - vertices[1].X; + float deltay = vertices[7].Y - vertices[1].Y; + float correction = length * deltay / deltax; + vertices[5] = new ResultPoint(vertices[5].X, vertices[5].Y + correction); + } + else if (- skew > SKEW_THRESHOLD) + { + // Fix v7 + float length = vertices[3].X - vertices[7].X; + float deltax = vertices[3].X - vertices[5].X; + float deltay = vertices[3].Y - vertices[5].Y; + float correction = length * deltay / deltax; + vertices[7] = new ResultPoint(vertices[7].X, vertices[7].Y - correction); + } + } + + ///

Estimates module size (pixels in a module) based on the Start and End + /// finder patterns.

+ /// + ///
+ /// an array of vertices: + /// vertices[0] x, y top left barcode + /// vertices[1] x, y bottom left barcode + /// vertices[2] x, y top right barcode + /// vertices[3] x, y bottom right barcode + /// vertices[4] x, y top left codeword area + /// vertices[5] x, y bottom left codeword area + /// vertices[6] x, y top right codeword area + /// vertices[7] x, y bottom right codeword area + /// + /// the module size. + /// + private static float computeModuleWidth(ResultPoint[] vertices) + { + float pixels1 = ResultPoint.distance(vertices[0], vertices[4]); + float pixels2 = ResultPoint.distance(vertices[1], vertices[5]); + float moduleWidth1 = (pixels1 + pixels2) / (17 * 2.0f); + float pixels3 = ResultPoint.distance(vertices[6], vertices[2]); + float pixels4 = ResultPoint.distance(vertices[7], vertices[3]); + float moduleWidth2 = (pixels3 + pixels4) / (18 * 2.0f); + return (moduleWidth1 + moduleWidth2) / 2.0f; + } + + /// Computes the dimension (number of modules in a row) of the PDF417 Code + /// based on vertices of the codeword area and estimated module size. + /// + /// + /// of codeword area + /// + /// of codeword area + /// + /// of codeword area + /// + /// of codeword are + /// + /// estimated module size + /// + /// the number of modules in a row. + /// + private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) + { + int topRowDimension = round(ResultPoint.distance(topLeft, topRight) / moduleWidth); + int bottomRowDimension = round(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth); + return ((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17; + /* + * int topRowDimension = round(ResultPoint.distance(topLeft, + * topRight)); //moduleWidth); int bottomRowDimension = + * round(ResultPoint.distance(bottomLeft, bottomRight)); // + * moduleWidth); int dimension = ((topRowDimension + bottomRowDimension) + * >> 1); // Round up to nearest 17 modules i.e. there are 17 modules per + * codeword //int dimension = ((((topRowDimension + bottomRowDimension) >> + * 1) + 8) / 17) * 17; return dimension; + */ + } + + private static BitMatrix sampleGrid(BitMatrix matrix, ResultPoint topLeft, ResultPoint bottomLeft, ResultPoint topRight, ResultPoint bottomRight, int dimension) + { + + // Note that unlike the QR Code sampler, we didn't find the center of modules, but the + // very corners. So there is no 0.5f here; 0.0f is right. + GridSampler sampler = GridSampler.Instance; + + return sampler.sampleGrid(matrix, dimension, 0.0f, 0.0f, dimension, 0.0f, dimension, dimension, 0.0f, dimension, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRight.X, bottomRight.Y, bottomLeft.X, bottomLeft.Y); // p4FromY + } + + /// Ends up being a bit faster than Math.round(). This merely rounds its + /// argument to the nearest int, where x.5 rounds up. + /// + private static int round(float d) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (int) (d + 0.5f); + } + + /// row of black/white values to search + /// + /// x position to start search + /// + /// y position to start search + /// + /// the number of pixels to search on this row + /// + /// pattern of counts of number of black and white pixels that are + /// being searched for as a pattern + /// + /// start/end horizontal offset of guard pattern, as an array of two ints. + /// + private static int[] findGuardPattern(BitMatrix matrix, int column, int row, int width, bool whiteFirst, int[] pattern) + { + int patternLength = pattern.Length; + // TODO: Find a way to cache this array, as this method is called hundreds of times + // per image, and we want to allocate as seldom as possible. + int[] counters = new int[patternLength]; + bool isWhite = whiteFirst; + + int counterPosition = 0; + int patternStart = column; + for (int x = column; x < column + width; x++) + { + bool pixel = matrix.get_Renamed(x, row); + if (pixel ^ isWhite) + { + counters[counterPosition]++; + } + else + { + if (counterPosition == patternLength - 1) + { + if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) + { + return new int[]{patternStart, x}; + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) + { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } + else + { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + return null; + } + + /// Determines how closely a set of observed counts of runs of black/white + /// values matches a given target pattern. This is reported as the ratio of + /// the total variance from the expected pattern proportions across all + /// pattern elements, to the length of the pattern. + /// + /// + /// observed counters + /// + /// expected pattern + /// + /// The most any counter can differ before we give up + /// + /// ratio of total variance between counters and pattern compared to + /// total pattern size, where the ratio has been multiplied by 256. + /// So, 0 means no variance (perfect match); 256 means the total + /// variance between counters and patterns equals the pattern length, + /// higher values mean even more variance + /// + private static int patternMatchVariance(int[] counters, int[] pattern, int maxIndividualVariance) + { + int numCounters = counters.Length; + int total = 0; + int patternLength = 0; + for (int i = 0; i < numCounters; i++) + { + total += counters[i]; + patternLength += pattern[i]; + } + if (total < patternLength) + { + // If we don't even have one pixel per unit of bar width, assume this + // is too small to reliably match, so fail: + return System.Int32.MaxValue; + } + // We're going to fake floating-point math in integers. We just need to use more bits. + // Scale up patternLength so that intermediate values below like scaledCounter will have + // more "significant digits". + int unitBarWidth = (total << 8) / patternLength; + maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> 8; + + int totalVariance = 0; + for (int x = 0; x < numCounters; x++) + { + int counter = counters[x] << 8; + int scaledPattern = pattern[x] * unitBarWidth; + int variance = counter > scaledPattern?counter - scaledPattern:scaledPattern - counter; + if (variance > maxIndividualVariance) + { + return System.Int32.MaxValue; + } + totalVariance += variance; + } + return totalVariance / total; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/QRCodeReader.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/QRCodeReader.cs new file mode 100644 index 0000000..eb144be --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/QRCodeReader.cs @@ -0,0 +1,181 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using DecodeHintType = com.google.zxing.DecodeHintType; +using Reader = com.google.zxing.Reader; +using ReaderException = com.google.zxing.ReaderException; +using Result = com.google.zxing.Result; +using ResultPoint = com.google.zxing.ResultPoint; +using ResultMetadataType = com.google.zxing.ResultMetadataType; +using BinaryBitmap = com.google.zxing.BinaryBitmap; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DecoderResult = com.google.zxing.common.DecoderResult; +using DetectorResult = com.google.zxing.common.DetectorResult; +using Decoder = com.google.zxing.qrcode.decoder.Decoder; +using Detector = com.google.zxing.qrcode.detector.Detector; +namespace com.google.zxing.qrcode +{ + + /// This implementation can detect and decode QR Codes in an image. + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public class QRCodeReader : Reader + { + virtual protected internal Decoder Decoder + { + get + { + return decoder; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'NO_POINTS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly ResultPoint[] NO_POINTS = new ResultPoint[0]; + + //UPGRADE_NOTE: Final was removed from the declaration of 'decoder '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private Decoder decoder = new Decoder(); + + /// Locates and decodes a QR code in an image. + /// + /// + /// a String representing the content encoded by the QR code + /// + /// ReaderException if a QR code cannot be found, or cannot be decoded + public virtual Result decode(BinaryBitmap image) + { + return decode(image, null); + } + + // public virtual Result decode(BinaryBitmap image, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public virtual Result decode(BinaryBitmap image, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + DecoderResult decoderResult; + ResultPoint[] points; + if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE)) + { + BitMatrix bits = extractPureBits(image.BlackMatrix); + decoderResult = decoder.decode(bits); + points = NO_POINTS; + } + else + { + DetectorResult detectorResult = new Detector(image.BlackMatrix).detect(hints); + decoderResult = decoder.decode(detectorResult.Bits); + points = detectorResult.Points; + } + + Result result = new Result(decoderResult.Text, decoderResult.RawBytes, points, BarcodeFormat.QR_CODE); + if (decoderResult.ByteSegments != null) + { + result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.ByteSegments); + } + if (decoderResult.ECLevel != null) + { + result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.ECLevel.ToString()); + } + return result; + } + + /// This method detects a barcode in a "pure" image -- that is, pure monochrome image + /// which contains only an unrotated, unskewed, image of a barcode, with some white border + /// around it. This is a specialized method that works exceptionally fast in this special + /// case. + /// + private static BitMatrix extractPureBits(BitMatrix image) + { + // Now need to determine module size in pixels + + int height = image.Height; + int width = image.Width; + int minDimension = System.Math.Min(height, width); + + // First, skip white border by tracking diagonally from the top left down and to the right: + int borderWidth = 0; + while (borderWidth < minDimension && !image.get_Renamed(borderWidth, borderWidth)) + { + borderWidth++; + } + if (borderWidth == minDimension) + { + throw ReaderException.Instance; + } + + // And then keep tracking across the top-left black module to determine module size + int moduleEnd = borderWidth; + while (moduleEnd < minDimension && image.get_Renamed(moduleEnd, moduleEnd)) + { + moduleEnd++; + } + if (moduleEnd == minDimension) + { + throw ReaderException.Instance; + } + + int moduleSize = moduleEnd - borderWidth; + + // And now find where the rightmost black module on the first row ends + int rowEndOfSymbol = width - 1; + while (rowEndOfSymbol >= 0 && !image.get_Renamed(rowEndOfSymbol, borderWidth)) + { + rowEndOfSymbol--; + } + if (rowEndOfSymbol < 0) + { + throw ReaderException.Instance; + } + rowEndOfSymbol++; + + // Make sure width of barcode is a multiple of module size + if ((rowEndOfSymbol - borderWidth) % moduleSize != 0) + { + throw ReaderException.Instance; + } + int dimension = (rowEndOfSymbol - borderWidth) / moduleSize; + + // Push in the "border" by half the module width so that we start + // sampling in the middle of the module. Just in case the image is a + // little off, this will help recover. + borderWidth += (moduleSize >> 1); + + int sampleDimension = borderWidth + (dimension - 1) * moduleSize; + if (sampleDimension >= width || sampleDimension >= height) + { + throw ReaderException.Instance; + } + + // Now just read off the bits + BitMatrix bits = new BitMatrix(dimension); + for (int i = 0; i < dimension; i++) + { + int iOffset = borderWidth + i * moduleSize; + for (int j = 0; j < dimension; j++) + { + if (image.get_Renamed(borderWidth + j * moduleSize, iOffset)) + { + bits.set_Renamed(j, i); + } + } + } + return bits; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/QRCodeWriter.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/QRCodeWriter.cs new file mode 100644 index 0000000..59c14e5 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/QRCodeWriter.cs @@ -0,0 +1,172 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using BarcodeFormat = com.google.zxing.BarcodeFormat; +using EncodeHintType = com.google.zxing.EncodeHintType; +using Writer = com.google.zxing.Writer; +using WriterException = com.google.zxing.WriterException; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +using Encoder = com.google.zxing.qrcode.encoder.Encoder; +using QRCode = com.google.zxing.qrcode.encoder.QRCode; +using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +namespace com.google.zxing.qrcode +{ + + /// This object renders a QR Code as a ByteMatrix 2D array of greyscale values. + /// + /// + /// dswitkin@google.com (Daniel Switkin) + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class QRCodeWriter : Writer + { + + private const int QUIET_ZONE_SIZE = 4; + + public ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height) + { + + return encode(contents, format, width, height, null); + } + + // public ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public ByteMatrix encode(System.String contents, BarcodeFormat format, int width, int height, System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + if (contents == null || contents.Length == 0) + { + throw new System.ArgumentException("Found empty contents"); + } + + if (format != BarcodeFormat.QR_CODE) + { + throw new System.ArgumentException("Can only encode QR_CODE, but got " + format); + } + + if (width < 0 || height < 0) + { + throw new System.ArgumentException("Requested dimensions are too small: " + width + 'x' + height); + } + + ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L; + if (hints != null) + { + // ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints[EncodeHintType.ERROR_CORRECTION]; // commented by .net follower (http://dotnetfollower.com) + ErrorCorrectionLevel requestedECLevel = hints.ContainsKey(EncodeHintType.ERROR_CORRECTION) ? (ErrorCorrectionLevel)hints[EncodeHintType.ERROR_CORRECTION] : null; // added by .net follower (http://dotnetfollower.com) + if (requestedECLevel != null) + { + errorCorrectionLevel = requestedECLevel; + } + } + + QRCode code = new QRCode(); + Encoder.encode(contents, errorCorrectionLevel, hints, code); + return renderResult(code, width, height); + } + + // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses + // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap). + private static ByteMatrix renderResult(QRCode code, int width, int height) + { + ByteMatrix input = code.Matrix; + int inputWidth = input.Width; + int inputHeight = input.Height; + int qrWidth = inputWidth + (QUIET_ZONE_SIZE << 1); + int qrHeight = inputHeight + (QUIET_ZONE_SIZE << 1); + int outputWidth = System.Math.Max(width, qrWidth); + int outputHeight = System.Math.Max(height, qrHeight); + + int multiple = System.Math.Min(outputWidth / qrWidth, outputHeight / qrHeight); + // Padding includes both the quiet zone and the extra white pixels to accommodate the requested + // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone. + // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will + // handle all the padding from 100x100 (the actual QR) up to 200x160. + int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; + int topPadding = (outputHeight - (inputHeight * multiple)) / 2; + + ByteMatrix output = new ByteMatrix(outputWidth, outputHeight); + sbyte[][] outputArray = output.Array; + + // We could be tricky and use the first row in each set of multiple as the temporary storage, + // instead of allocating this separate array. + sbyte[] row = new sbyte[outputWidth]; + + // 1. Write the white lines at the top + for (int y = 0; y < topPadding; y++) + { + setRowColor(outputArray[y], (sbyte) SupportClass.Identity(255)); + } + + // 2. Expand the QR image to the multiple + sbyte[][] inputArray = input.Array; + for (int y = 0; y < inputHeight; y++) + { + // a. Write the white pixels at the left of each row + for (int x = 0; x < leftPadding; x++) + { + row[x] = (sbyte) SupportClass.Identity(255); + } + + // b. Write the contents of this row of the barcode + int offset = leftPadding; + for (int x = 0; x < inputWidth; x++) + { + // Redivivus.in Java to c# Porting update - Type cased sbyte + // 30/01/2010 + // sbyte value_Renamed = (inputArray[y][x] == 1)?0:(sbyte) SupportClass.Identity(255); + sbyte value_Renamed = (sbyte)((inputArray[y][x] == 1) ? 0 : SupportClass.Identity(255)); + for (int z = 0; z < multiple; z++) + { + row[offset + z] = value_Renamed; + } + offset += multiple; + } + + // c. Write the white pixels at the right of each row + offset = leftPadding + (inputWidth * multiple); + for (int x = offset; x < outputWidth; x++) + { + row[x] = (sbyte) SupportClass.Identity(255); + } + + // d. Write the completed row multiple times + offset = topPadding + (y * multiple); + for (int z = 0; z < multiple; z++) + { + Array.Copy(row, 0, outputArray[offset + z], 0, outputWidth); + } + } + + // 3. Write the white lines at the bottom + int offset2 = topPadding + (inputHeight * multiple); + for (int y = offset2; y < outputHeight; y++) + { + setRowColor(outputArray[y], (sbyte) SupportClass.Identity(255)); + } + + return output; + } + + private static void setRowColor(sbyte[] row, sbyte value_Renamed) + { + for (int x = 0; x < row.Length; x++) + { + row[x] = value_Renamed; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/BitMatrixParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/BitMatrixParser.cs new file mode 100644 index 0000000..505b1f8 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/BitMatrixParser.cs @@ -0,0 +1,241 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.qrcode.decoder +{ + + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class BitMatrixParser + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'bitMatrix '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix bitMatrix; + private Version parsedVersion; + private FormatInformation parsedFormatInfo; + + /// {@link BitMatrix} to parse + /// + /// ReaderException if dimension is not >= 21 and 1 mod 4 + internal BitMatrixParser(BitMatrix bitMatrix) + { + int dimension = bitMatrix.Dimension; + if (dimension < 21 || (dimension & 0x03) != 1) + { + throw ReaderException.Instance; + } + this.bitMatrix = bitMatrix; + } + + ///

Reads format information from one of its two locations within the QR Code.

+ /// + ///
+ /// {@link FormatInformation} encapsulating the QR Code's format info + /// + /// ReaderException if both format information locations cannot be parsed as + /// the valid encoding of format information + /// + internal FormatInformation readFormatInformation() + { + + if (parsedFormatInfo != null) + { + return parsedFormatInfo; + } + + // Read top-left format info bits + int formatInfoBits = 0; + for (int i = 0; i < 6; i++) + { + formatInfoBits = copyBit(i, 8, formatInfoBits); + } + // .. and skip a bit in the timing pattern ... + formatInfoBits = copyBit(7, 8, formatInfoBits); + formatInfoBits = copyBit(8, 8, formatInfoBits); + formatInfoBits = copyBit(8, 7, formatInfoBits); + // .. and skip a bit in the timing pattern ... + for (int j = 5; j >= 0; j--) + { + formatInfoBits = copyBit(8, j, formatInfoBits); + } + + parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); + if (parsedFormatInfo != null) + { + return parsedFormatInfo; + } + + // Hmm, failed. Try the top-right/bottom-left pattern + int dimension = bitMatrix.Dimension; + formatInfoBits = 0; + int iMin = dimension - 8; + for (int i = dimension - 1; i >= iMin; i--) + { + formatInfoBits = copyBit(i, 8, formatInfoBits); + } + for (int j = dimension - 7; j < dimension; j++) + { + formatInfoBits = copyBit(8, j, formatInfoBits); + } + + parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); + if (parsedFormatInfo != null) + { + return parsedFormatInfo; + } + throw ReaderException.Instance; + } + + ///

Reads version information from one of its two locations within the QR Code.

+ /// + ///
+ /// {@link Version} encapsulating the QR Code's version + /// + /// ReaderException if both version information locations cannot be parsed as + /// the valid encoding of version information + /// + internal Version readVersion() + { + + if (parsedVersion != null) + { + return parsedVersion; + } + + int dimension = bitMatrix.Dimension; + + int provisionalVersion = (dimension - 17) >> 2; + if (provisionalVersion <= 6) + { + return Version.getVersionForNumber(provisionalVersion); + } + + // Read top-right version info: 3 wide by 6 tall + int versionBits = 0; + int ijMin = dimension - 11; + for (int j = 5; j >= 0; j--) + { + for (int i = dimension - 9; i >= ijMin; i--) + { + versionBits = copyBit(i, j, versionBits); + } + } + + parsedVersion = Version.decodeVersionInformation(versionBits); + if (parsedVersion != null && parsedVersion.DimensionForVersion == dimension) + { + return parsedVersion; + } + + // Hmm, failed. Try bottom left: 6 wide by 3 tall + versionBits = 0; + for (int i = 5; i >= 0; i--) + { + for (int j = dimension - 9; j >= ijMin; j--) + { + versionBits = copyBit(i, j, versionBits); + } + } + + parsedVersion = Version.decodeVersionInformation(versionBits); + if (parsedVersion != null && parsedVersion.DimensionForVersion == dimension) + { + return parsedVersion; + } + throw ReaderException.Instance; + } + + private int copyBit(int i, int j, int versionBits) + { + return bitMatrix.get_Renamed(i, j)?(versionBits << 1) | 0x1:versionBits << 1; + } + + ///

Reads the bits in the {@link BitMatrix} representing the finder pattern in the + /// correct order in order to reconstitute the codewords bytes contained within the + /// QR Code.

+ /// + ///
+ /// bytes encoded within the QR Code + /// + /// ReaderException if the exact number of bytes expected is not read + internal sbyte[] readCodewords() + { + + FormatInformation formatInfo = readFormatInformation(); + Version version = readVersion(); + + // Get the data mask for the format used in this QR Code. This will exclude + // some bits from reading as we wind through the bit matrix. + DataMask dataMask = DataMask.forReference((int) formatInfo.DataMask); + int dimension = bitMatrix.Dimension; + dataMask.unmaskBitMatrix(bitMatrix, dimension); + + BitMatrix functionPattern = version.buildFunctionPattern(); + + bool readingUp = true; + sbyte[] result = new sbyte[version.TotalCodewords]; + int resultOffset = 0; + int currentByte = 0; + int bitsRead = 0; + // Read columns in pairs, from right to left + for (int j = dimension - 1; j > 0; j -= 2) + { + if (j == 6) + { + // Skip whole column with vertical alignment pattern; + // saves time and makes the other code proceed more cleanly + j--; + } + // Read alternatingly from bottom to top then top to bottom + for (int count = 0; count < dimension; count++) + { + int i = readingUp?dimension - 1 - count:count; + for (int col = 0; col < 2; col++) + { + // Ignore bits covered by the function pattern + if (!functionPattern.get_Renamed(j - col, i)) + { + // Read a bit + bitsRead++; + currentByte <<= 1; + if (bitMatrix.get_Renamed(j - col, i)) + { + currentByte |= 1; + } + // If we've made a whole byte, save it off + if (bitsRead == 8) + { + result[resultOffset++] = (sbyte) currentByte; + bitsRead = 0; + currentByte = 0; + } + } + } + } + readingUp ^= true; // readingUp = !readingUp; // switch directions + } + if (resultOffset != version.TotalCodewords) + { + throw ReaderException.Instance; + } + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DataBlock.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DataBlock.cs new file mode 100644 index 0000000..91925f2 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DataBlock.cs @@ -0,0 +1,151 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.qrcode.decoder +{ + + ///

Encapsulates a block of data within a QR Code. QR Codes may split their data into + /// multiple blocks, each of which is a unit of data and error-correction codewords. Each + /// is represented by an instance of this class.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class DataBlock + { + internal int NumDataCodewords + { + get + { + return numDataCodewords; + } + + } + internal sbyte[] Codewords + { + get + { + return codewords; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'numDataCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int numDataCodewords; + //UPGRADE_NOTE: Final was removed from the declaration of 'codewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte[] codewords; + + private DataBlock(int numDataCodewords, sbyte[] codewords) + { + this.numDataCodewords = numDataCodewords; + this.codewords = codewords; + } + + ///

When QR Codes use multiple data blocks, they are actually interleaved. + /// That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This + /// method will separate the data into original blocks.

+ /// + ///
+ /// bytes as read directly from the QR Code + /// + /// version of the QR Code + /// + /// error-correction level of the QR Code + /// + /// {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the + /// QR Code + /// + internal static DataBlock[] getDataBlocks(sbyte[] rawCodewords, Version version, ErrorCorrectionLevel ecLevel) + { + + if (rawCodewords.Length != version.TotalCodewords) + { + throw new System.ArgumentException(); + } + + // Figure out the number and size of data blocks used by this version and + // error correction level + Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); + + // First count the total number of data blocks + int totalBlocks = 0; + Version.ECB[] ecBlockArray = ecBlocks.getECBlocks(); + for (int i = 0; i < ecBlockArray.Length; i++) + { + totalBlocks += ecBlockArray[i].Count; + } + + // Now establish DataBlocks of the appropriate size and number of data codewords + DataBlock[] result = new DataBlock[totalBlocks]; + int numResultBlocks = 0; + for (int j = 0; j < ecBlockArray.Length; j++) + { + Version.ECB ecBlock = ecBlockArray[j]; + for (int i = 0; i < ecBlock.Count; i++) + { + int numDataCodewords = ecBlock.DataCodewords; + int numBlockCodewords = ecBlocks.ECCodewordsPerBlock + numDataCodewords; + result[numResultBlocks++] = new DataBlock(numDataCodewords, new sbyte[numBlockCodewords]); + } + } + + // All blocks have the same amount of data, except that the last n + // (where n may be 0) have 1 more byte. Figure out where these start. + int shorterBlocksTotalCodewords = result[0].codewords.Length; + int longerBlocksStartAt = result.Length - 1; + while (longerBlocksStartAt >= 0) + { + int numCodewords = result[longerBlocksStartAt].codewords.Length; + if (numCodewords == shorterBlocksTotalCodewords) + { + break; + } + longerBlocksStartAt--; + } + longerBlocksStartAt++; + + int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock; + // The last elements of result may be 1 element longer; + // first fill out as many elements as all of them have + int rawCodewordsOffset = 0; + for (int i = 0; i < shorterBlocksNumDataCodewords; i++) + { + for (int j = 0; j < numResultBlocks; j++) + { + result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; + } + } + // Fill out the last data block in the longer ones + for (int j = longerBlocksStartAt; j < numResultBlocks; j++) + { + result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; + } + // Now add in error correction blocks + int max = result[0].codewords.Length; + for (int i = shorterBlocksNumDataCodewords; i < max; i++) + { + for (int j = 0; j < numResultBlocks; j++) + { + int iOffset = j < longerBlocksStartAt?i:i + 1; + result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; + } + } + return result; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DataMask.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DataMask.cs new file mode 100644 index 0000000..e2ecaa0 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DataMask.cs @@ -0,0 +1,157 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.qrcode.decoder +{ + + ///

Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations + /// of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix, + /// including areas used for finder patterns, timing patterns, etc. These areas should be unused + /// after the point they are unmasked anyway.

+ /// + ///

Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position + /// and j is row position. In fact, as the text says, i is row position and j is column position.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + abstract class DataMask + { + + /// See ISO 18004:2006 6.8.1 + //UPGRADE_NOTE: Final was removed from the declaration of 'DATA_MASKS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly DataMask[] DATA_MASKS = new DataMask[]{new DataMask000(), new DataMask001(), new DataMask010(), new DataMask011(), new DataMask100(), new DataMask101(), new DataMask110(), new DataMask111()}; + + private DataMask() + { + } + + ///

Implementations of this method reverse the data masking process applied to a QR Code and + /// make its bits ready to read.

+ /// + ///
+ /// representation of QR Code bits + /// + /// dimension of QR Code, represented by bits, being unmasked + /// + internal void unmaskBitMatrix(BitMatrix bits, int dimension) + { + for (int i = 0; i < dimension; i++) + { + for (int j = 0; j < dimension; j++) + { + if (isMasked(i, j)) + { + bits.flip(j, i); + } + } + } + } + + internal abstract bool isMasked(int i, int j); + + /// a value between 0 and 7 indicating one of the eight possible + /// data mask patterns a QR Code may use + /// + /// {@link DataMask} encapsulating the data mask pattern + /// + internal static DataMask forReference(int reference) + { + if (reference < 0 || reference > 7) + { + throw new System.ArgumentException(); + } + return DATA_MASKS[reference]; + } + + /// 000: mask bits for which (x + y) mod 2 == 0 + private class DataMask000:DataMask + { + internal override bool isMasked(int i, int j) + { + return ((i + j) & 0x01) == 0; + } + } + + /// 001: mask bits for which x mod 2 == 0 + private class DataMask001:DataMask + { + internal override bool isMasked(int i, int j) + { + return (i & 0x01) == 0; + } + } + + /// 010: mask bits for which y mod 3 == 0 + private class DataMask010:DataMask + { + internal override bool isMasked(int i, int j) + { + return j % 3 == 0; + } + } + + /// 011: mask bits for which (x + y) mod 3 == 0 + private class DataMask011:DataMask + { + internal override bool isMasked(int i, int j) + { + return (i + j) % 3 == 0; + } + } + + /// 100: mask bits for which (x/2 + y/3) mod 2 == 0 + private class DataMask100:DataMask + { + internal override bool isMasked(int i, int j) + { + return (((SupportClass.URShift(i, 1)) + (j / 3)) & 0x01) == 0; + } + } + + /// 101: mask bits for which xy mod 2 + xy mod 3 == 0 + private class DataMask101:DataMask + { + internal override bool isMasked(int i, int j) + { + int temp = i * j; + return (temp & 0x01) + (temp % 3) == 0; + } + } + + /// 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0 + private class DataMask110:DataMask + { + internal override bool isMasked(int i, int j) + { + int temp = i * j; + return (((temp & 0x01) + (temp % 3)) & 0x01) == 0; + } + } + + /// 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0 + private class DataMask111:DataMask + { + internal override bool isMasked(int i, int j) + { + return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DecodedBitStreamParser.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DecodedBitStreamParser.cs new file mode 100644 index 0000000..3db0ee3 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/DecodedBitStreamParser.cs @@ -0,0 +1,443 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitSource = com.google.zxing.common.BitSource; +using CharacterSetECI = com.google.zxing.common.CharacterSetECI; +using DecoderResult = com.google.zxing.common.DecoderResult; +namespace com.google.zxing.qrcode.decoder +{ + + ///

QR Codes can encode text as bits in one of several modes, and can use multiple modes + /// in one QR Code. This class decodes the bits back into text.

+ /// + ///

See ISO 18004:2006, 6.4.3 - 6.4.7

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class DecodedBitStreamParser + { + + /// See ISO 18004:2006, 6.4.4 Table 5 + //UPGRADE_NOTE: Final was removed from the declaration of 'ALPHANUMERIC_CHARS'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly char[] ALPHANUMERIC_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'}; + private const System.String SHIFT_JIS = "SJIS"; + private const System.String EUC_JP = "EUC_JP"; + private static bool ASSUME_SHIFT_JIS; + private const System.String UTF8 = "UTF8"; + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // Commented & Added + private const System.String ISO88591 = "ISO-8859-1"; + + private DecodedBitStreamParser() + { + } + + internal static DecoderResult decode(sbyte[] bytes, Version version, ErrorCorrectionLevel ecLevel) + { + BitSource bits = new BitSource(bytes); + System.Text.StringBuilder result = new System.Text.StringBuilder(50); + CharacterSetECI currentCharacterSetECI = null; + bool fc1InEffect = false; + // System.Collections.ArrayList byteSegments = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(1)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List byteSegments = new System.Collections.Generic.List(1); // added by .net follower (http://dotnetfollower.com) + Mode mode; + do + { + // While still another segment to read... + if (bits.available() < 4) + { + // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here + mode = Mode.TERMINATOR; + } + else + { + try + { + mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits + } + catch (System.ArgumentException iae) + { + throw ReaderException.Instance; + } + } + if (!mode.Equals(Mode.TERMINATOR)) + { + if (mode.Equals(Mode.FNC1_FIRST_POSITION) || mode.Equals(Mode.FNC1_SECOND_POSITION)) + { + // We do little with FNC1 except alter the parsed result a bit according to the spec + fc1InEffect = true; + } + else if (mode.Equals(Mode.STRUCTURED_APPEND)) + { + // not really supported; all we do is ignore it + // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue + bits.readBits(16); + } + else if (mode.Equals(Mode.ECI)) + { + // Count doesn't apply to ECI + int value_Renamed = parseECIValue(bits); + currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value_Renamed); + if (currentCharacterSetECI == null) + { + throw ReaderException.Instance; + } + } + else + { + // How many characters will follow, encoded in this mode? + int count = bits.readBits(mode.getCharacterCountBits(version)); + if (mode.Equals(Mode.NUMERIC)) + { + decodeNumericSegment(bits, result, count); + } + else if (mode.Equals(Mode.ALPHANUMERIC)) + { + decodeAlphanumericSegment(bits, result, count, fc1InEffect); + } + else if (mode.Equals(Mode.BYTE)) + { + decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments); + } + else if (mode.Equals(Mode.KANJI)) + { + decodeKanjiSegment(bits, result, count); + } + else + { + throw ReaderException.Instance; + } + } + } + } + while (!mode.Equals(Mode.TERMINATOR)); + + return new DecoderResult(bytes, result.ToString(), (byteSegments.Count == 0)?null:byteSegments, ecLevel); + } + + private static void decodeKanjiSegment(BitSource bits, System.Text.StringBuilder result, int count) + { + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as Shift_JIS afterwards + sbyte[] buffer = new sbyte[2 * count]; + int offset = 0; + while (count > 0) + { + // Each 13 bits encodes a 2-byte character + int twoBytes = bits.readBits(13); + int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); + if (assembledTwoBytes < 0x01F00) + { + // In the 0x8140 to 0x9FFC range + assembledTwoBytes += 0x08140; + } + else + { + // In the 0xE040 to 0xEBBF range + assembledTwoBytes += 0x0C140; + } + buffer[offset] = (sbyte) (assembledTwoBytes >> 8); + buffer[offset + 1] = (sbyte) assembledTwoBytes; + offset += 2; + count--; + } + // Shift_JIS may not be supported in some environments: + try + { + //UPGRADE_TODO: The differences in the Format of parameters for constructor 'java.lang.String.String' may cause compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'" + // result.Append(System.Text.Encoding.GetEncoding(SHIFT_JIS).GetString(SupportClass.ToByteArray(buffer))); // commented by .net follower (http://dotnetfollower.com) + byte[] inputBytes = SupportClass.ToByteArray(buffer); // added by .net follower (http://dotnetfollower.com) + result.Append(System.Text.Encoding.GetEncoding(SHIFT_JIS).GetString(inputBytes, 0, inputBytes.Length)); // added by .net follower (http://dotnetfollower.com) + } + catch (System.IO.IOException uee) + { + throw ReaderException.Instance; + } + } + + // private static void decodeByteSegment(BitSource bits, System.Text.StringBuilder result, int count, CharacterSetECI currentCharacterSetECI, System.Collections.ArrayList byteSegments) // commented by .net follower (http://dotnetfollower.com) + private static void decodeByteSegment(BitSource bits, System.Text.StringBuilder result, int count, CharacterSetECI currentCharacterSetECI, System.Collections.Generic.List byteSegments) // added by .net follower (http://dotnetfollower.com) + { + sbyte[] readBytes = new sbyte[count]; + if (count << 3 > bits.available()) + { + throw ReaderException.Instance; + } + for (int i = 0; i < count; i++) + { + readBytes[i] = (sbyte) bits.readBits(8); + } + System.String encoding; + if (currentCharacterSetECI == null) + { + // The spec isn't clear on this mode; see + // section 6.4.5: t does not say which encoding to assuming + // upon decoding. I have seen ISO-8859-1 used as well as + // Shift_JIS -- without anything like an ECI designator to + // give a hint. + encoding = guessEncoding(readBytes); + } + else + { + encoding = currentCharacterSetECI.EncodingName; + } + try + { + //UPGRADE_TODO: The differences in the Format of parameters for constructor 'java.lang.String.String' may cause compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'" + // result.Append(System.Text.Encoding.GetEncoding(encoding).GetString(SupportClass.ToByteArray(readBytes))); // commented by .net follower (http://dotnetfollower.com) + byte[] inputBytes = SupportClass.ToByteArray(readBytes); // added by .net follower (http://dotnetfollower.com) + result.Append(System.Text.Encoding.GetEncoding(encoding).GetString(inputBytes, 0, inputBytes.Length)); // added by .net follower (http://dotnetfollower.com) + } + catch (System.IO.IOException uce) + { + throw ReaderException.Instance; + } + byteSegments.Add(SupportClass.ToByteArray(readBytes)); + } + + private static void decodeAlphanumericSegment(BitSource bits, System.Text.StringBuilder result, int count, bool fc1InEffect) + { + // Read two characters at a time + int start = result.Length; + while (count > 1) + { + int nextTwoCharsBits = bits.readBits(11); + result.Append(ALPHANUMERIC_CHARS[nextTwoCharsBits / 45]); + result.Append(ALPHANUMERIC_CHARS[nextTwoCharsBits % 45]); + count -= 2; + } + if (count == 1) + { + // special case: one character left + result.Append(ALPHANUMERIC_CHARS[bits.readBits(6)]); + } + // See section 6.4.8.1, 6.4.8.2 + if (fc1InEffect) + { + // We need to massage the result a bit if in an FNC1 mode: + for (int i = start; i < result.Length; i++) + { + if (result[i] == '%') + { + if (i < result.Length - 1 && result[i + 1] == '%') + { + // %% is rendered as % + result.Remove(i + 1, 1); + } + else + { + // In alpha mode, % should be converted to FNC1 separator 0x1D + result[i] = (char) 0x1D; + } + } + } + } + } + + private static void decodeNumericSegment(BitSource bits, System.Text.StringBuilder result, int count) + { + // Read three digits at a time + while (count >= 3) + { + // Each 10 bits encodes three digits + int threeDigitsBits = bits.readBits(10); + if (threeDigitsBits >= 1000) + { + throw ReaderException.Instance; + } + result.Append(ALPHANUMERIC_CHARS[threeDigitsBits / 100]); + result.Append(ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10]); + result.Append(ALPHANUMERIC_CHARS[threeDigitsBits % 10]); + count -= 3; + } + if (count == 2) + { + // Two digits left over to read, encoded in 7 bits + int twoDigitsBits = bits.readBits(7); + if (twoDigitsBits >= 100) + { + throw ReaderException.Instance; + } + result.Append(ALPHANUMERIC_CHARS[twoDigitsBits / 10]); + result.Append(ALPHANUMERIC_CHARS[twoDigitsBits % 10]); + } + else if (count == 1) + { + // One digit left over to read + int digitBits = bits.readBits(4); + if (digitBits >= 10) + { + throw ReaderException.Instance; + } + result.Append(ALPHANUMERIC_CHARS[digitBits]); + } + } + + private static System.String guessEncoding(sbyte[] bytes) + { + if (ASSUME_SHIFT_JIS) + { + return SHIFT_JIS; + } + // Does it start with the UTF-8 byte order mark? then guess it's UTF-8 + if (bytes.Length > 3 && bytes[0] == (sbyte) SupportClass.Identity(0xEF) && bytes[1] == (sbyte) SupportClass.Identity(0xBB) && bytes[2] == (sbyte) SupportClass.Identity(0xBF)) + { + return UTF8; + } + // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, + // which should be by far the most common encodings. ISO-8859-1 + // should not have bytes in the 0x80 - 0x9F range, while Shift_JIS + // uses this as a first byte of a two-byte character. If we see this + // followed by a valid second byte in Shift_JIS, assume it is Shift_JIS. + // If we see something else in that second byte, we'll make the risky guess + // that it's UTF-8. + int length = bytes.Length; + bool canBeISO88591 = true; + bool canBeShiftJIS = true; + int maybeDoubleByteCount = 0; + int maybeSingleByteKatakanaCount = 0; + bool sawLatin1Supplement = false; + bool lastWasPossibleDoubleByteStart = false; + for (int i = 0; i < length && (canBeISO88591 || canBeShiftJIS); i++) + { + int value_Renamed = bytes[i] & 0xFF; + if ((value_Renamed == 0xC2 || value_Renamed == 0xC3) && i < length - 1) + { + // This is really a poor hack. The slightly more exotic characters people might want to put in + // a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings + // that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF]. + int nextValue = bytes[i + 1] & 0xFF; + if (nextValue <= 0xBF && ((value_Renamed == 0xC2 && nextValue >= 0xA0) || (value_Renamed == 0xC3 && nextValue >= 0x80))) + { + sawLatin1Supplement = true; + } + } + if (value_Renamed >= 0x7F && value_Renamed <= 0x9F) + { + canBeISO88591 = false; + } + if (value_Renamed >= 0xA1 && value_Renamed <= 0xDF) + { + // count the number of characters that might be a Shift_JIS single-byte Katakana character + if (!lastWasPossibleDoubleByteStart) + { + maybeSingleByteKatakanaCount++; + } + } + if (!lastWasPossibleDoubleByteStart && ((value_Renamed >= 0xF0 && value_Renamed <= 0xFF) || value_Renamed == 0x80 || value_Renamed == 0xA0)) + { + canBeShiftJIS = false; + } + if (((value_Renamed >= 0x81 && value_Renamed <= 0x9F) || (value_Renamed >= 0xE0 && value_Renamed <= 0xEF))) + { + // These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid + // second byte. + if (lastWasPossibleDoubleByteStart) + { + // If we just checked this and the last byte for being a valid double-byte + // char, don't check starting on this byte. If this and the last byte + // formed a valid pair, then this shouldn't be checked to see if it starts + // a double byte pair of course. + lastWasPossibleDoubleByteStart = false; + } + else + { + // ... otherwise do check to see if this plus the next byte form a valid + // double byte pair encoding a character. + lastWasPossibleDoubleByteStart = true; + if (i >= bytes.Length - 1) + { + canBeShiftJIS = false; + } + else + { + int nextValue = bytes[i + 1] & 0xFF; + if (nextValue < 0x40 || nextValue > 0xFC) + { + canBeShiftJIS = false; + } + else + { + maybeDoubleByteCount++; + } + // There is some conflicting information out there about which bytes can follow which in + // double-byte Shift_JIS characters. The rule above seems to be the one that matches practice. + } + } + } + else + { + lastWasPossibleDoubleByteStart = false; + } + } + // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is: + // - If we saw + // - at least three byte that starts a double-byte value (bytes that are rare in ISO-8859-1), or + // - over 5% of bytes that could be single-byte Katakana (also rare in ISO-8859-1), + // - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS + if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) + { + return SHIFT_JIS; + } + // Otherwise, we default to ISO-8859-1 unless we know it can't be + if (!sawLatin1Supplement && canBeISO88591) + { + return ISO88591; + } + // Otherwise, we take a wild guess with UTF-8 + return UTF8; + } + + private static int parseECIValue(BitSource bits) + { + int firstByte = bits.readBits(8); + if ((firstByte & 0x80) == 0) + { + // just one byte + return firstByte & 0x7F; + } + else if ((firstByte & 0xC0) == 0x80) + { + // two bytes + int secondByte = bits.readBits(8); + return ((firstByte & 0x3F) << 8) | secondByte; + } + else if ((firstByte & 0xE0) == 0xC0) + { + // three bytes + int secondThirdBytes = bits.readBits(16); + return ((firstByte & 0x1F) << 16) | secondThirdBytes; + } + throw new System.ArgumentException("Bad ECI bits starting with byte " + firstByte); + } + static DecodedBitStreamParser() + { + { + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // Commented & Added + //System.String platformDefault = System_Renamed.getProperty("file.encoding"); + //ASSUME_SHIFT_JIS = SHIFT_JIS.ToUpper().Equals(platformDefault.ToUpper()) || EUC_JP.ToUpper().Equals(platformDefault.ToUpper()); + ASSUME_SHIFT_JIS = false; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Decoder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Decoder.cs new file mode 100644 index 0000000..9762202 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Decoder.cs @@ -0,0 +1,153 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DecoderResult = com.google.zxing.common.DecoderResult; +using GF256 = com.google.zxing.common.reedsolomon.GF256; +using ReedSolomonDecoder = com.google.zxing.common.reedsolomon.ReedSolomonDecoder; +using ReedSolomonException = com.google.zxing.common.reedsolomon.ReedSolomonException; +namespace com.google.zxing.qrcode.decoder +{ + + ///

The main class which implements QR Code decoding -- as opposed to locating and extracting + /// the QR Code from an image.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Decoder + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'rsDecoder '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ReedSolomonDecoder rsDecoder; + + public Decoder() + { + rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD); + } + + ///

Convenience method that can decode a QR Code represented as a 2D array of booleans. + /// "true" is taken to mean a black module.

+ /// + ///
+ /// booleans representing white/black QR Code modules + /// + /// text and bytes encoded within the QR Code + /// + /// ReaderException if the QR Code cannot be decoded + public DecoderResult decode(bool[][] image) + { + int dimension = image.Length; + BitMatrix bits = new BitMatrix(dimension); + for (int i = 0; i < dimension; i++) + { + for (int j = 0; j < dimension; j++) + { + if (image[i][j]) + { + bits.set_Renamed(j, i); + } + } + } + return decode(bits); + } + + ///

Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.

+ /// + ///
+ /// booleans representing white/black QR Code modules + /// + /// text and bytes encoded within the QR Code + /// + /// ReaderException if the QR Code cannot be decoded + public DecoderResult decode(BitMatrix bits) + { + + // Construct a parser and read version, error-correction level + BitMatrixParser parser = new BitMatrixParser(bits); + Version version = parser.readVersion(); + ErrorCorrectionLevel ecLevel = parser.readFormatInformation().ErrorCorrectionLevel; + + // Read codewords + sbyte[] codewords = parser.readCodewords(); + // Separate into data blocks + DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel); + + // Count total number of data bytes + int totalBytes = 0; + for (int i = 0; i < dataBlocks.Length; i++) + { + totalBytes += dataBlocks[i].NumDataCodewords; + } + sbyte[] resultBytes = new sbyte[totalBytes]; + int resultOffset = 0; + + // Error-correct and copy data blocks together into a stream of bytes + for (int j = 0; j < dataBlocks.Length; j++) + { + DataBlock dataBlock = dataBlocks[j]; + sbyte[] codewordBytes = dataBlock.Codewords; + int numDataCodewords = dataBlock.NumDataCodewords; + correctErrors(codewordBytes, numDataCodewords); + for (int i = 0; i < numDataCodewords; i++) + { + resultBytes[resultOffset++] = codewordBytes[i]; + } + } + + // Decode the contents of that stream of bytes + return DecodedBitStreamParser.decode(resultBytes, version, ecLevel); + } + + ///

Given data and error-correction codewords received, possibly corrupted by errors, attempts to + /// correct the errors in-place using Reed-Solomon error correction.

+ /// + ///
+ /// data and error correction codewords + /// + /// number of codewords that are data bytes + /// + /// ReaderException if error correction fails + private void correctErrors(sbyte[] codewordBytes, int numDataCodewords) + { + int numCodewords = codewordBytes.Length; + // First read into an array of ints + int[] codewordsInts = new int[numCodewords]; + for (int i = 0; i < numCodewords; i++) + { + codewordsInts[i] = codewordBytes[i] & 0xFF; + } + int numECCodewords = codewordBytes.Length - numDataCodewords; + try + { + rsDecoder.decode(codewordsInts, numECCodewords); + } + catch (ReedSolomonException rse) + { + throw ReaderException.Instance; + } + // Copy back into array of bytes -- only need to worry about the bytes that were data + // We don't care about errors in the error-correction codewords + for (int i = 0; i < numDataCodewords; i++) + { + codewordBytes[i] = (sbyte) codewordsInts[i]; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/ErrorCorrectionLevel.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/ErrorCorrectionLevel.cs new file mode 100644 index 0000000..1b65cc9 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/ErrorCorrectionLevel.cs @@ -0,0 +1,102 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.qrcode.decoder +{ + + ///

See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels + /// defined by the QR code standard.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class ErrorCorrectionLevel + { + public int Bits + { + get + { + return bits; + } + + } + public System.String Name + { + get + { + return name; + } + + } + + // No, we can't use an enum here. J2ME doesn't support it. + + /// L = ~7% correction + //UPGRADE_NOTE: Final was removed from the declaration of 'L '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ErrorCorrectionLevel L = new ErrorCorrectionLevel(0, 0x01, "L"); + /// M = ~15% correction + //UPGRADE_NOTE: Final was removed from the declaration of 'M '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ErrorCorrectionLevel M = new ErrorCorrectionLevel(1, 0x00, "M"); + /// Q = ~25% correction + //UPGRADE_NOTE: Final was removed from the declaration of 'Q '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2, 0x03, "Q"); + /// H = ~30% correction + //UPGRADE_NOTE: Final was removed from the declaration of 'H '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly ErrorCorrectionLevel H = new ErrorCorrectionLevel(3, 0x02, "H"); + + //UPGRADE_NOTE: Final was removed from the declaration of 'FOR_BITS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly ErrorCorrectionLevel[] FOR_BITS = new ErrorCorrectionLevel[]{M, L, H, Q}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'ordinal '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int ordinal_Renamed_Field; + //UPGRADE_NOTE: Final was removed from the declaration of 'bits '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int bits; + //UPGRADE_NOTE: Final was removed from the declaration of 'name '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String name; + + private ErrorCorrectionLevel(int ordinal, int bits, System.String name) + { + this.ordinal_Renamed_Field = ordinal; + this.bits = bits; + this.name = name; + } + + public int ordinal() + { + return ordinal_Renamed_Field; + } + + public override System.String ToString() + { + return name; + } + + /// int containing the two bits encoding a QR Code's error correction level + /// + /// {@link ErrorCorrectionLevel} representing the encoded error correction level + /// + public static ErrorCorrectionLevel forBits(int bits) + { + if (bits < 0 || bits >= FOR_BITS.Length) + { + throw new System.ArgumentException(); + } + return FOR_BITS[bits]; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/FormatInformation.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/FormatInformation.cs new file mode 100644 index 0000000..cf820d6 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/FormatInformation.cs @@ -0,0 +1,144 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.qrcode.decoder +{ + + ///

Encapsulates a QR Code's format information, including the data mask used and + /// error correction level.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + /// + /// + /// + /// + sealed class FormatInformation + { + internal ErrorCorrectionLevel ErrorCorrectionLevel + { + get + { + return errorCorrectionLevel; + } + + } + internal sbyte DataMask + { + get + { + return dataMask; + } + + } + + private const int FORMAT_INFO_MASK_QR = 0x5412; + + /// See ISO 18004:2006, Annex C, Table C.1 + //UPGRADE_NOTE: Final was removed from the declaration of 'FORMAT_INFO_DECODE_LOOKUP'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] FORMAT_INFO_DECODE_LOOKUP = new int[][]{new int[]{0x5412, 0x00}, new int[]{0x5125, 0x01}, new int[]{0x5E7C, 0x02}, new int[]{0x5B4B, 0x03}, new int[]{0x45F9, 0x04}, new int[]{0x40CE, 0x05}, new int[]{0x4F97, 0x06}, new int[]{0x4AA0, 0x07}, new int[]{0x77C4, 0x08}, new int[]{0x72F3, 0x09}, new int[]{0x7DAA, 0x0A}, new int[]{0x789D, 0x0B}, new int[]{0x662F, 0x0C}, new int[]{0x6318, 0x0D}, new int[]{0x6C41, 0x0E}, new int[]{0x6976, 0x0F}, new int[]{0x1689, 0x10}, new int[]{0x13BE, 0x11}, new int[]{0x1CE7, 0x12}, new int[]{0x19D0, 0x13}, new int[]{0x0762, 0x14}, new int[]{0x0255, 0x15}, new int[]{0x0D0C, 0x16}, new int[]{0x083B, 0x17}, new int[]{0x355F, 0x18}, new int[]{0x3068, 0x19}, new int[]{0x3F31, 0x1A}, new int[]{0x3A06, 0x1B}, new int[]{0x24B4, 0x1C}, new int[]{0x2183, 0x1D}, new int[]{0x2EDA, 0x1E}, new int[]{0x2BED, 0x1F}}; + + /// Offset i holds the number of 1 bits in the binary representation of i + //UPGRADE_NOTE: Final was removed from the declaration of 'BITS_SET_IN_HALF_BYTE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] BITS_SET_IN_HALF_BYTE = new int[]{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'errorCorrectionLevel '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ErrorCorrectionLevel errorCorrectionLevel; + //UPGRADE_NOTE: Final was removed from the declaration of 'dataMask '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private sbyte dataMask; + + private FormatInformation(int formatInfo) + { + // Bits 3,4 + errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03); + // Bottom 3 bits + dataMask = (sbyte) (formatInfo & 0x07); + } + + internal static int numBitsDiffering(int a, int b) + { + a ^= b; // a now has a 1 bit exactly where its bit differs with b's + // Count bits set quickly with a series of lookups: + return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 4) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 8) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 12) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 16) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 20) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 24) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(SupportClass.URShift(a, 28) & 0x0F)]; + } + + /// format info indicator, with mask still applied + /// + /// information about the format it specifies, or null + /// if doesn't seem to match any known pattern + /// + internal static FormatInformation decodeFormatInformation(int maskedFormatInfo) + { + FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo); + if (formatInfo != null) + { + return formatInfo; + } + // Should return null, but, some QR codes apparently + // do not mask this info. Try again by actually masking the pattern + // first + return doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR); + } + + private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo) + { + // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing + int bestDifference = System.Int32.MaxValue; + int bestFormatInfo = 0; + for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.Length; i++) + { + int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i]; + int targetInfo = decodeInfo[0]; + if (targetInfo == maskedFormatInfo) + { + // Found an exact match + return new FormatInformation(decodeInfo[1]); + } + int bitsDifference = numBitsDiffering(maskedFormatInfo, targetInfo); + if (bitsDifference < bestDifference) + { + bestFormatInfo = decodeInfo[1]; + bestDifference = bitsDifference; + } + } + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits + // differing means we found a match + if (bestDifference <= 3) + { + return new FormatInformation(bestFormatInfo); + } + return null; + } + + public override int GetHashCode() + { + return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask; + } + + public override bool Equals(System.Object o) + { + if (!(o is FormatInformation)) + { + return false; + } + FormatInformation other = (FormatInformation) o; + return this.errorCorrectionLevel == other.errorCorrectionLevel && this.dataMask == other.dataMask; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Mode.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Mode.cs new file mode 100644 index 0000000..86843f8 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Mode.cs @@ -0,0 +1,158 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.qrcode.decoder +{ + + ///

See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which + /// data can be encoded to bits in the QR code standard.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Mode + { + public int Bits + { + get + { + return bits; + } + + } + public System.String Name + { + get + { + return name; + } + + } + + // No, we can't use an enum here. J2ME doesn't support it. + + //UPGRADE_NOTE: Final was removed from the declaration of 'TERMINATOR '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode TERMINATOR = new Mode(new int[]{0, 0, 0}, 0x00, "TERMINATOR"); // Not really a mode... + //UPGRADE_NOTE: Final was removed from the declaration of 'NUMERIC '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode NUMERIC = new Mode(new int[]{10, 12, 14}, 0x01, "NUMERIC"); + //UPGRADE_NOTE: Final was removed from the declaration of 'ALPHANUMERIC '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode ALPHANUMERIC = new Mode(new int[]{9, 11, 13}, 0x02, "ALPHANUMERIC"); + //UPGRADE_NOTE: Final was removed from the declaration of 'STRUCTURED_APPEND '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode STRUCTURED_APPEND = new Mode(new int[]{0, 0, 0}, 0x03, "STRUCTURED_APPEND"); // Not supported + //UPGRADE_NOTE: Final was removed from the declaration of 'BYTE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode BYTE = new Mode(new int[]{8, 16, 16}, 0x04, "BYTE"); + //UPGRADE_NOTE: Final was removed from the declaration of 'ECI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode ECI = new Mode(null, 0x07, "ECI"); // character counts don't apply + //UPGRADE_NOTE: Final was removed from the declaration of 'KANJI '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode KANJI = new Mode(new int[]{8, 10, 12}, 0x08, "KANJI"); + //UPGRADE_NOTE: Final was removed from the declaration of 'FNC1_FIRST_POSITION '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode FNC1_FIRST_POSITION = new Mode(null, 0x05, "FNC1_FIRST_POSITION"); + //UPGRADE_NOTE: Final was removed from the declaration of 'FNC1_SECOND_POSITION '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly Mode FNC1_SECOND_POSITION = new Mode(null, 0x09, "FNC1_SECOND_POSITION"); + + //UPGRADE_NOTE: Final was removed from the declaration of 'characterCountBitsForVersions '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] characterCountBitsForVersions; + //UPGRADE_NOTE: Final was removed from the declaration of 'bits '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int bits; + //UPGRADE_NOTE: Final was removed from the declaration of 'name '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private System.String name; + + private Mode(int[] characterCountBitsForVersions, int bits, System.String name) + { + this.characterCountBitsForVersions = characterCountBitsForVersions; + this.bits = bits; + this.name = name; + } + + /// four bits encoding a QR Code data mode + /// + /// {@link Mode} encoded by these bits + /// + /// IllegalArgumentException if bits do not correspond to a known mode + public static Mode forBits(int bits) + { + switch (bits) + { + + case 0x0: + return TERMINATOR; + + case 0x1: + return NUMERIC; + + case 0x2: + return ALPHANUMERIC; + + case 0x3: + return STRUCTURED_APPEND; + + case 0x4: + return BYTE; + + case 0x5: + return FNC1_FIRST_POSITION; + + case 0x7: + return ECI; + + case 0x8: + return KANJI; + + case 0x9: + return FNC1_SECOND_POSITION; + + default: + throw new System.ArgumentException(); + + } + } + + /// version in question + /// + /// number of bits used, in this QR Code symbol {@link Version}, to encode the + /// count of characters that will follow encoded in this {@link Mode} + /// + public int getCharacterCountBits(Version version) + { + if (characterCountBitsForVersions == null) + { + throw new System.ArgumentException("Character count doesn't apply to this mode"); + } + int number = version.VersionNumber; + int offset; + if (number <= 9) + { + offset = 0; + } + else if (number <= 26) + { + offset = 1; + } + else + { + offset = 2; + } + return characterCountBitsForVersions[offset]; + } + + public override System.String ToString() + { + return name; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Version.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Version.cs new file mode 100644 index 0000000..c28be0c --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/decoder/Version.cs @@ -0,0 +1,320 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.qrcode.decoder +{ + + /// See ISO 18004:2006 Annex D + /// + /// + /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Version + { + public int VersionNumber + { + get + { + return versionNumber; + } + + } + public int[] AlignmentPatternCenters + { + get + { + return alignmentPatternCenters; + } + + } + public int TotalCodewords + { + get + { + return totalCodewords; + } + + } + public int DimensionForVersion + { + get + { + return 17 + 4 * versionNumber; + } + + } + + /// See ISO 18004:2006 Annex D. + /// Element i represents the raw version bits that specify version i + 7 + /// + //UPGRADE_NOTE: Final was removed from the declaration of 'VERSION_DECODE_INFO'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] VERSION_DECODE_INFO = new int[]{0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x2542E, 0x26A64, 0x27541, 0x28C69}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'VERSIONS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly Version[] VERSIONS = buildVersions(); + + //UPGRADE_NOTE: Final was removed from the declaration of 'versionNumber '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int versionNumber; + //UPGRADE_NOTE: Final was removed from the declaration of 'alignmentPatternCenters '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] alignmentPatternCenters; + //UPGRADE_NOTE: Final was removed from the declaration of 'ecBlocks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ECBlocks[] ecBlocks; + //UPGRADE_NOTE: Final was removed from the declaration of 'totalCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int totalCodewords; + + private Version(int versionNumber, int[] alignmentPatternCenters, ECBlocks ecBlocks1, ECBlocks ecBlocks2, ECBlocks ecBlocks3, ECBlocks ecBlocks4) + { + this.versionNumber = versionNumber; + this.alignmentPatternCenters = alignmentPatternCenters; + this.ecBlocks = new ECBlocks[]{ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4}; + int total = 0; + int ecCodewords = ecBlocks1.ECCodewordsPerBlock; + ECB[] ecbArray = ecBlocks1.getECBlocks(); + for (int i = 0; i < ecbArray.Length; i++) + { + ECB ecBlock = ecbArray[i]; + total += ecBlock.Count * (ecBlock.DataCodewords + ecCodewords); + } + this.totalCodewords = total; + } + + public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) + { + return ecBlocks[ecLevel.ordinal()]; + } + + ///

Deduces version information purely from QR Code dimensions.

+ /// + ///
+ /// dimension in modules + /// + /// {@link Version} for a QR Code of that dimension + /// + /// ReaderException if dimension is not 1 mod 4 + public static Version getProvisionalVersionForDimension(int dimension) + { + if (dimension % 4 != 1) + { + throw ReaderException.Instance; + } + try + { + return getVersionForNumber((dimension - 17) >> 2); + } + catch (System.ArgumentException iae) + { + throw ReaderException.Instance; + } + } + + public static Version getVersionForNumber(int versionNumber) + { + if (versionNumber < 1 || versionNumber > 40) + { + throw new System.ArgumentException(); + } + return VERSIONS[versionNumber - 1]; + } + + internal static Version decodeVersionInformation(int versionBits) + { + int bestDifference = System.Int32.MaxValue; + int bestVersion = 0; + for (int i = 0; i < VERSION_DECODE_INFO.Length; i++) + { + int targetVersion = VERSION_DECODE_INFO[i]; + // Do the version info bits match exactly? done. + if (targetVersion == versionBits) + { + return getVersionForNumber(i + 7); + } + // Otherwise see if this is the closest to a real version info bit string + // we have seen so far + int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); + if (bitsDifference < bestDifference) + { + bestVersion = i + 7; + bestDifference = bitsDifference; + } + } + // We can tolerate up to 3 bits of error since no two version info codewords will + // differ in less than 4 bits. + if (bestDifference <= 3) + { + return getVersionForNumber(bestVersion); + } + // If we didn't find a close enough match, fail + return null; + } + + /// See ISO 18004:2006 Annex E + internal BitMatrix buildFunctionPattern() + { + int dimension = DimensionForVersion; + BitMatrix bitMatrix = new BitMatrix(dimension); + + // Top left finder pattern + separator + format + bitMatrix.setRegion(0, 0, 9, 9); + // Top right finder pattern + separator + format + bitMatrix.setRegion(dimension - 8, 0, 8, 9); + // Bottom left finder pattern + separator + format + bitMatrix.setRegion(0, dimension - 8, 9, 8); + + // Alignment patterns + int max = alignmentPatternCenters.Length; + for (int x = 0; x < max; x++) + { + int i = alignmentPatternCenters[x] - 2; + for (int y = 0; y < max; y++) + { + if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) + { + // No alignment patterns near the three finder paterns + continue; + } + bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5); + } + } + + // Vertical timing pattern + bitMatrix.setRegion(6, 9, 1, dimension - 17); + // Horizontal timing pattern + bitMatrix.setRegion(9, 6, dimension - 17, 1); + + if (versionNumber > 6) + { + // Version info, top right + bitMatrix.setRegion(dimension - 11, 0, 3, 6); + // Version info, bottom left + bitMatrix.setRegion(0, dimension - 11, 6, 3); + } + + return bitMatrix; + } + + ///

Encapsulates a set of error-correction blocks in one symbol version. Most versions will + /// use blocks of differing sizes within one version, so, this encapsulates the parameters for + /// each set of blocks. It also holds the number of error-correction codewords per block since it + /// will be the same across all blocks within one version.

+ ///
+ public sealed class ECBlocks + { + public int ECCodewordsPerBlock + { + get + { + return ecCodewordsPerBlock; + } + + } + public int NumBlocks + { + get + { + int total = 0; + for (int i = 0; i < ecBlocks.Length; i++) + { + total += ecBlocks[i].Count; + } + return total; + } + + } + public int TotalECCodewords + { + get + { + return ecCodewordsPerBlock * NumBlocks; + } + + } + //UPGRADE_NOTE: Final was removed from the declaration of 'ecCodewordsPerBlock '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int ecCodewordsPerBlock; + //UPGRADE_NOTE: Final was removed from the declaration of 'ecBlocks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ECB[] ecBlocks; + + internal ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks) + { + this.ecCodewordsPerBlock = ecCodewordsPerBlock; + this.ecBlocks = new ECB[]{ecBlocks}; + } + + internal ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks1, ECB ecBlocks2) + { + this.ecCodewordsPerBlock = ecCodewordsPerBlock; + this.ecBlocks = new ECB[]{ecBlocks1, ecBlocks2}; + } + + public ECB[] getECBlocks() + { + return ecBlocks; + } + } + + ///

Encapsualtes the parameters for one error-correction block in one symbol version. + /// This includes the number of data codewords, and the number of times a block with these + /// parameters is used consecutively in the QR code version's format.

+ ///
+ public sealed class ECB + { + public int Count + { + get + { + return count; + } + + } + public int DataCodewords + { + get + { + return dataCodewords; + } + + } + //UPGRADE_NOTE: Final was removed from the declaration of 'count '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int count; + //UPGRADE_NOTE: Final was removed from the declaration of 'dataCodewords '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int dataCodewords; + + internal ECB(int count, int dataCodewords) + { + this.count = count; + this.dataCodewords = dataCodewords; + } + } + + public override System.String ToString() + { + return System.Convert.ToString(versionNumber); + } + + /// See ISO 18004:2006 6.5.1 Table 9 + private static Version[] buildVersions() + { + return new Version[]{new Version(1, new int[]{}, new ECBlocks(7, new ECB(1, 19)), new ECBlocks(10, new ECB(1, 16)), new ECBlocks(13, new ECB(1, 13)), new ECBlocks(17, new ECB(1, 9))), new Version(2, new int[]{6, 18}, new ECBlocks(10, new ECB(1, 34)), new ECBlocks(16, new ECB(1, 28)), new ECBlocks(22, new ECB(1, 22)), new ECBlocks(28, new ECB(1, 16))), new Version(3, new int[]{6, 22}, new ECBlocks(15, new ECB(1, 55)), new ECBlocks(26, new ECB(1, 44)), new ECBlocks(18, new ECB(2, 17)), new ECBlocks(22, new ECB(2, 13))), new Version(4, new int[]{6, 26}, new ECBlocks(20, new ECB(1, 80)), new ECBlocks(18, new ECB(2, 32)), new ECBlocks(26, new ECB(2, 24)), new ECBlocks(16, new ECB(4, 9))), new Version(5, new int[]{6, 30}, new ECBlocks(26, new ECB(1, 108)), new ECBlocks(24, new ECB(2, 43)), new ECBlocks(18, new ECB(2, 15), new ECB(2, 16)), new ECBlocks(22, new ECB(2, 11), new ECB(2, 12))), new Version(6, new int[]{6, 34}, new ECBlocks(18, new ECB(2, 68)), new ECBlocks(16, new ECB(4, 27)), new ECBlocks(24, new ECB(4, 19)), new ECBlocks(28, new ECB(4, 15))), new Version(7, new int[]{6, 22, 38}, new ECBlocks(20, new ECB(2, 78)), new ECBlocks(18, new ECB(4, 31)), new ECBlocks(18, new ECB(2, 14), new ECB(4, 15)), new ECBlocks(26, new ECB(4, 13), new ECB(1, 14))), new Version(8, new int[]{6, 24, 42}, new ECBlocks(24, new ECB(2, 97)), new ECBlocks(22, new ECB(2, 38), new ECB(2, 39)), new ECBlocks(22, new ECB(4, 18), new ECB(2, 19)), new ECBlocks(26, new ECB(4, 14), new ECB(2, 15))), new Version(9, new int[]{6, 26, 46}, new ECBlocks(30, new ECB(2, 116)), new ECBlocks(22, new ECB(3, 36), new ECB(2, 37)), new ECBlocks(20, new ECB(4, 16), new ECB(4, 17)), new ECBlocks(24, new ECB(4, 12), new ECB(4, 13))), new Version(10, new int[]{6, 28, 50}, new ECBlocks(18, new ECB(2, 68), new ECB(2, 69)), new ECBlocks(26, new ECB(4, 43), new ECB(1, 44)), new ECBlocks(24, new ECB(6, 19), new ECB(2, 20)), new ECBlocks(28, new ECB(6, 15), new ECB(2, 16))), new Version(11, new int[]{6, 30, 54}, new ECBlocks(20, new ECB(4, 81)), + new ECBlocks(30, new ECB(1, 50), new ECB(4, 51)), new ECBlocks(28, new ECB(4, 22), new ECB(4, 23)), new ECBlocks(24, new ECB(3, 12), new ECB(8, 13))), new Version(12, new int[]{6, 32, 58}, new ECBlocks(24, new ECB(2, 92), new ECB(2, 93)), new ECBlocks(22, new ECB(6, 36), new ECB(2, 37)), new ECBlocks(26, new ECB(4, 20), new ECB(6, 21)), new ECBlocks(28, new ECB(7, 14), new ECB(4, 15))), new Version(13, new int[]{6, 34, 62}, new ECBlocks(26, new ECB(4, 107)), new ECBlocks(22, new ECB(8, 37), new ECB(1, 38)), new ECBlocks(24, new ECB(8, 20), new ECB(4, 21)), new ECBlocks(22, new ECB(12, 11), new ECB(4, 12))), new Version(14, new int[]{6, 26, 46, 66}, new ECBlocks(30, new ECB(3, 115), new ECB(1, 116)), new ECBlocks(24, new ECB(4, 40), new ECB(5, 41)), new ECBlocks(20, new ECB(11, 16), new ECB(5, 17)), new ECBlocks(24, new ECB(11, 12), new ECB(5, 13))), new Version(15, new int[]{6, 26, 48, 70}, new ECBlocks(22, new ECB(5, 87), new ECB(1, 88)), new ECBlocks(24, new ECB(5, 41), new ECB(5, 42)), new ECBlocks(30, new ECB(5, 24), new ECB(7, 25)), new ECBlocks(24, new ECB(11, 12), new ECB(7, 13))), new Version(16, new int[]{6, 26, 50, 74}, new ECBlocks(24, new ECB(5, 98), new ECB(1, 99)), new ECBlocks(28, new ECB(7, 45), new ECB(3, 46)), new ECBlocks(24, new ECB(15, 19), new ECB(2, 20)), new ECBlocks(30, new ECB(3, 15), new ECB(13, 16))), new Version(17, new int[]{6, 30, 54, 78}, new ECBlocks(28, new ECB(1, 107), new ECB(5, 108)), new ECBlocks(28, new ECB(10, 46), new ECB(1, 47)), new ECBlocks(28, new ECB(1, 22), new ECB(15, 23)), new ECBlocks(28, new ECB(2, 14), new ECB(17, 15))), new Version(18, new int[]{6, 30, 56, 82}, new ECBlocks(30, new ECB(5, 120), new ECB(1, 121)), new ECBlocks(26, new ECB(9, 43), new ECB(4, 44)), new ECBlocks(28, new ECB(17, 22), new ECB(1, 23)), new ECBlocks(28, new ECB(2, 14), new ECB(19, 15))), new Version(19, new int[]{6, 30, 58, 86}, new ECBlocks(28, new ECB(3, 113), new ECB(4, 114)), new ECBlocks(26, new ECB(3, 44), new ECB(11, 45)), new ECBlocks(26, new ECB(17, 21), + new ECB(4, 22)), new ECBlocks(26, new ECB(9, 13), new ECB(16, 14))), new Version(20, new int[]{6, 34, 62, 90}, new ECBlocks(28, new ECB(3, 107), new ECB(5, 108)), new ECBlocks(26, new ECB(3, 41), new ECB(13, 42)), new ECBlocks(30, new ECB(15, 24), new ECB(5, 25)), new ECBlocks(28, new ECB(15, 15), new ECB(10, 16))), new Version(21, new int[]{6, 28, 50, 72, 94}, new ECBlocks(28, new ECB(4, 116), new ECB(4, 117)), new ECBlocks(26, new ECB(17, 42)), new ECBlocks(28, new ECB(17, 22), new ECB(6, 23)), new ECBlocks(30, new ECB(19, 16), new ECB(6, 17))), new Version(22, new int[]{6, 26, 50, 74, 98}, new ECBlocks(28, new ECB(2, 111), new ECB(7, 112)), new ECBlocks(28, new ECB(17, 46)), new ECBlocks(30, new ECB(7, 24), new ECB(16, 25)), new ECBlocks(24, new ECB(34, 13))), new Version(23, new int[]{6, 30, 54, 74, 102}, new ECBlocks(30, new ECB(4, 121), new ECB(5, 122)), new ECBlocks(28, new ECB(4, 47), new ECB(14, 48)), new ECBlocks(30, new ECB(11, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(16, 15), new ECB(14, 16))), new Version(24, new int[]{6, 28, 54, 80, 106}, new ECBlocks(30, new ECB(6, 117), new ECB(4, 118)), new ECBlocks(28, new ECB(6, 45), new ECB(14, 46)), new ECBlocks(30, new ECB(11, 24), new ECB(16, 25)), new ECBlocks(30, new ECB(30, 16), new ECB(2, 17))), new Version(25, new int[]{6, 32, 58, 84, 110}, new ECBlocks(26, new ECB(8, 106), new ECB(4, 107)), new ECBlocks(28, new ECB(8, 47), new ECB(13, 48)), new ECBlocks(30, new ECB(7, 24), new ECB(22, 25)), new ECBlocks(30, new ECB(22, 15), new ECB(13, 16))), new Version(26, new int[]{6, 30, 58, 86, 114}, new ECBlocks(28, new ECB(10, 114), new ECB(2, 115)), new ECBlocks(28, new ECB(19, 46), new ECB(4, 47)), new ECBlocks(28, new ECB(28, 22), new ECB(6, 23)), new ECBlocks(30, new ECB(33, 16), new ECB(4, 17))), new Version(27, new int[]{6, 34, 62, 90, 118}, new ECBlocks(30, new ECB(8, 122), new ECB(4, 123)), new ECBlocks(28, new ECB(22, 45), new ECB(3, 46)), new ECBlocks(30, new ECB(8, 23), new ECB(26, 24)), new ECBlocks(30, new ECB(12, 15), + new ECB(28, 16))), new Version(28, new int[]{6, 26, 50, 74, 98, 122}, new ECBlocks(30, new ECB(3, 117), new ECB(10, 118)), new ECBlocks(28, new ECB(3, 45), new ECB(23, 46)), new ECBlocks(30, new ECB(4, 24), new ECB(31, 25)), new ECBlocks(30, new ECB(11, 15), new ECB(31, 16))), new Version(29, new int[]{6, 30, 54, 78, 102, 126}, new ECBlocks(30, new ECB(7, 116), new ECB(7, 117)), new ECBlocks(28, new ECB(21, 45), new ECB(7, 46)), new ECBlocks(30, new ECB(1, 23), new ECB(37, 24)), new ECBlocks(30, new ECB(19, 15), new ECB(26, 16))), new Version(30, new int[]{6, 26, 52, 78, 104, 130}, new ECBlocks(30, new ECB(5, 115), new ECB(10, 116)), new ECBlocks(28, new ECB(19, 47), new ECB(10, 48)), new ECBlocks(30, new ECB(15, 24), new ECB(25, 25)), new ECBlocks(30, new ECB(23, 15), new ECB(25, 16))), new Version(31, new int[]{6, 30, 56, 82, 108, 134}, new ECBlocks(30, new ECB(13, 115), new ECB(3, 116)), new ECBlocks(28, new ECB(2, 46), new ECB(29, 47)), new ECBlocks(30, new ECB(42, 24), new ECB(1, 25)), new ECBlocks(30, new ECB(23, 15), new ECB(28, 16))), new Version(32, new int[]{6, 34, 60, 86, 112, 138}, new ECBlocks(30, new ECB(17, 115)), new ECBlocks(28, new ECB(10, 46), new ECB(23, 47)), new ECBlocks(30, new ECB(10, 24), new ECB(35, 25)), new ECBlocks(30, new ECB(19, 15), new ECB(35, 16))), new Version(33, new int[]{6, 30, 58, 86, 114, 142}, new ECBlocks(30, new ECB(17, 115), new ECB(1, 116)), new ECBlocks(28, new ECB(14, 46), new ECB(21, 47)), new ECBlocks(30, new ECB(29, 24), new ECB(19, 25)), new ECBlocks(30, new ECB(11, 15), new ECB(46, 16))), new Version(34, new int[]{6, 34, 62, 90, 118, 146}, new ECBlocks(30, new ECB(13, 115), new ECB(6, 116)), new ECBlocks(28, new ECB(14, 46), new ECB(23, 47)), new ECBlocks(30, new ECB(44, 24), new ECB(7, 25)), new ECBlocks(30, new ECB(59, 16), new ECB(1, 17))), new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150}, new ECBlocks(30, new ECB(12, 121), new ECB(7, 122)), new ECBlocks(28, new ECB(12, 47), new ECB(26, 48)), new ECBlocks(30, new ECB(39, 24), new + ECB(14, 25)), new ECBlocks(30, new ECB(22, 15), new ECB(41, 16))), new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154}, new ECBlocks(30, new ECB(6, 121), new ECB(14, 122)), new ECBlocks(28, new ECB(6, 47), new ECB(34, 48)), new ECBlocks(30, new ECB(46, 24), new ECB(10, 25)), new ECBlocks(30, new ECB(2, 15), new ECB(64, 16))), new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158}, new ECBlocks(30, new ECB(17, 122), new ECB(4, 123)), new ECBlocks(28, new ECB(29, 46), new ECB(14, 47)), new ECBlocks(30, new ECB(49, 24), new ECB(10, 25)), new ECBlocks(30, new ECB(24, 15), new ECB(46, 16))), new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162}, new ECBlocks(30, new ECB(4, 122), new ECB(18, 123)), new ECBlocks(28, new ECB(13, 46), new ECB(32, 47)), new ECBlocks(30, new ECB(48, 24), new ECB(14, 25)), new ECBlocks(30, new ECB(42, 15), new ECB(32, 16))), new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166}, new ECBlocks(30, new ECB(20, 117), new ECB(4, 118)), new ECBlocks(28, new ECB(40, 47), new ECB(7, 48)), new ECBlocks(30, new ECB(43, 24), new ECB(22, 25)), new ECBlocks(30, new ECB(10, 15), new ECB(67, 16))), new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170}, new ECBlocks(30, new ECB(19, 118), new ECB(6, 119)), new ECBlocks(28, new ECB(18, 47), new ECB(31, 48)), new ECBlocks(30, new ECB(34, 24), new ECB(34, 25)), new ECBlocks(30, new ECB(20, 15), new ECB(61, 16)))}; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/AlignmentPattern.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/AlignmentPattern.cs new file mode 100644 index 0000000..e406657 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/AlignmentPattern.cs @@ -0,0 +1,53 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ResultPoint = com.google.zxing.ResultPoint; +namespace com.google.zxing.qrcode.detector +{ + + ///

Encapsulates an alignment pattern, which are the smaller square patterns found in + /// all but the simplest QR Codes.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class AlignmentPattern:ResultPoint + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'estimatedModuleSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private float estimatedModuleSize; + + internal AlignmentPattern(float posX, float posY, float estimatedModuleSize):base(posX, posY) + { + this.estimatedModuleSize = estimatedModuleSize; + } + + ///

Determines if this alignment pattern "about equals" an alignment pattern at the stated + /// position and size -- meaning, it is at nearly the same center with nearly the same size.

+ ///
+ internal bool aboutEquals(float moduleSize, float i, float j) + { + if (System.Math.Abs(i - Y) <= moduleSize && System.Math.Abs(j - X) <= moduleSize) + { + float moduleSizeDiff = System.Math.Abs(moduleSize - estimatedModuleSize); + return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f; + } + return false; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/AlignmentPatternFinder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/AlignmentPatternFinder.cs new file mode 100644 index 0000000..b34cac6 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/AlignmentPatternFinder.cs @@ -0,0 +1,341 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using ResultPointCallback = com.google.zxing.ResultPointCallback; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.qrcode.detector +{ + + ///

This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder + /// patterns but are smaller and appear at regular intervals throughout the image.

+ /// + ///

At the moment this only looks for the bottom-right alignment pattern.

+ /// + ///

This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied, + /// pasted and stripped down here for maximum performance but does unfortunately duplicate + /// some code.

+ /// + ///

This class is thread-safe but not reentrant. Each thread must allocate its own object. + /// + ///

+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + sealed class AlignmentPatternFinder + { + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix image; + //UPGRADE_NOTE: Final was removed from the declaration of 'possibleCenters '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + // private System.Collections.ArrayList possibleCenters; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.List possibleCenters; // added by .net follower (http://dotnetfollower.com) + //UPGRADE_NOTE: Final was removed from the declaration of 'startX '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int startX; + //UPGRADE_NOTE: Final was removed from the declaration of 'startY '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int startY; + //UPGRADE_NOTE: Final was removed from the declaration of 'width '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int width; + //UPGRADE_NOTE: Final was removed from the declaration of 'height '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int height; + //UPGRADE_NOTE: Final was removed from the declaration of 'moduleSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private float moduleSize; + //UPGRADE_NOTE: Final was removed from the declaration of 'crossCheckStateCount '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] crossCheckStateCount; + //UPGRADE_NOTE: Final was removed from the declaration of 'resultPointCallback '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPointCallback resultPointCallback; + + ///

Creates a finder that will look in a portion of the whole image.

+ /// + ///
+ /// image to search + /// + /// left column from which to start searching + /// + /// top row from which to start searching + /// + /// width of region to search + /// + /// height of region to search + /// + /// estimated module size so far + /// + internal AlignmentPatternFinder(BitMatrix image, int startX, int startY, int width, int height, float moduleSize, ResultPointCallback resultPointCallback) + { + this.image = image; + // this.possibleCenters = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(5)); // commented by .net follower (http://dotnetfollower.com) + this.possibleCenters = new System.Collections.Generic.List(5); // added by .net follower (http://dotnetfollower.com) + this.startX = startX; + this.startY = startY; + this.width = width; + this.height = height; + this.moduleSize = moduleSize; + this.crossCheckStateCount = new int[3]; + this.resultPointCallback = resultPointCallback; + } + + ///

This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since + /// it's pretty performance-critical and so is written to be fast foremost.

+ /// + ///
+ /// {@link AlignmentPattern} if found + /// + /// ReaderException if not found + internal AlignmentPattern find() + { + int startX = this.startX; + int height = this.height; + int maxJ = startX + width; + int middleI = startY + (height >> 1); + // We are looking for black/white/black modules in 1:1:1 ratio; + // this tracks the number of black/white/black modules seen so far + int[] stateCount = new int[3]; + for (int iGen = 0; iGen < height; iGen++) + { + // Search from middle outwards + int i = middleI + ((iGen & 0x01) == 0?((iGen + 1) >> 1):- ((iGen + 1) >> 1)); + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + int j = startX; + // Burn off leading white pixels before anything else; if we start in the middle of + // a white run, it doesn't make sense to count its length, since we don't know if the + // white run continued to the left of the start point + while (j < maxJ && !image.get_Renamed(j, i)) + { + j++; + } + int currentState = 0; + while (j < maxJ) + { + if (image.get_Renamed(j, i)) + { + // Black pixel + if (currentState == 1) + { + // Counting black pixels + stateCount[currentState]++; + } + else + { + // Counting white pixels + if (currentState == 2) + { + // A winner? + if (foundPatternCross(stateCount)) + { + // Yes + AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j); + if (confirmed != null) + { + return confirmed; + } + } + stateCount[0] = stateCount[2]; + stateCount[1] = 1; + stateCount[2] = 0; + currentState = 1; + } + else + { + stateCount[++currentState]++; + } + } + } + else + { + // White pixel + if (currentState == 1) + { + // Counting black pixels + currentState++; + } + stateCount[currentState]++; + } + j++; + } + if (foundPatternCross(stateCount)) + { + AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ); + if (confirmed != null) + { + return confirmed; + } + } + } + + // Hmm, nothing we saw was observed and confirmed twice. If we had + // any guess at all, return it. + if (!(possibleCenters.Count == 0)) + { + return (AlignmentPattern) possibleCenters[0]; + } + + throw ReaderException.Instance; + } + + /// Given a count of black/white/black pixels just seen and an end position, + /// figures the location of the center of this black/white/black run. + /// + private static float centerFromEnd(int[] stateCount, int end) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (float) (end - stateCount[2]) - stateCount[1] / 2.0f; + } + + /// count of black/white/black pixels just read + /// + /// true iff the proportions of the counts is close enough to the 1/1/1 ratios + /// used by alignment patterns to be considered a match + /// + private bool foundPatternCross(int[] stateCount) + { + float moduleSize = this.moduleSize; + float maxVariance = moduleSize / 2.0f; + for (int i = 0; i < 3; i++) + { + if (System.Math.Abs(moduleSize - stateCount[i]) >= maxVariance) + { + return false; + } + } + return true; + } + + ///

After a horizontal scan finds a potential alignment pattern, this method + /// "cross-checks" by scanning down vertically through the center of the possible + /// alignment pattern to see if the same proportion is detected.

+ /// + ///
+ /// row where an alignment pattern was detected + /// + /// center of the section that appears to cross an alignment pattern + /// + /// maximum reasonable number of modules that should be + /// observed in any reading state, based on the results of the horizontal scan + /// + /// vertical center of alignment pattern, or {@link Float#NaN} if not found + /// + private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) + { + BitMatrix image = this.image; + + int maxI = image.Height; + int[] stateCount = crossCheckStateCount; + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + + // Start counting up from center + int i = startI; + while (i >= 0 && image.get_Renamed(centerJ, i) && stateCount[1] <= maxCount) + { + stateCount[1]++; + i--; + } + // If already too many modules in this state or ran off the edge: + if (i < 0 || stateCount[1] > maxCount) + { + return System.Single.NaN; + } + while (i >= 0 && !image.get_Renamed(centerJ, i) && stateCount[0] <= maxCount) + { + stateCount[0]++; + i--; + } + if (stateCount[0] > maxCount) + { + return System.Single.NaN; + } + + // Now also count down from center + i = startI + 1; + while (i < maxI && image.get_Renamed(centerJ, i) && stateCount[1] <= maxCount) + { + stateCount[1]++; + i++; + } + if (i == maxI || stateCount[1] > maxCount) + { + return System.Single.NaN; + } + while (i < maxI && !image.get_Renamed(centerJ, i) && stateCount[2] <= maxCount) + { + stateCount[2]++; + i++; + } + if (stateCount[2] > maxCount) + { + return System.Single.NaN; + } + + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; + if (5 * System.Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) + { + return System.Single.NaN; + } + + return foundPatternCross(stateCount)?centerFromEnd(stateCount, i):System.Single.NaN; + } + + ///

This is called when a horizontal scan finds a possible alignment pattern. It will + /// cross check with a vertical scan, and if successful, will see if this pattern had been + /// found on a previous horizontal scan. If so, we consider it confirmed and conclude we have + /// found the alignment pattern.

+ /// + ///
+ /// reading state module counts from horizontal scan + /// + /// row where alignment pattern may be found + /// + /// end of possible alignment pattern in row + /// + /// {@link AlignmentPattern} if we have found the same pattern twice, or null if not + /// + private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) + { + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; + float centerJ = centerFromEnd(stateCount, j); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal); + if (!System.Single.IsNaN(centerI)) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float estimatedModuleSize = (float) (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f; + int max = possibleCenters.Count; + for (int index = 0; index < max; index++) + { + AlignmentPattern center = (AlignmentPattern) possibleCenters[index]; + // Look for about the same center and module size: + if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) + { + return new AlignmentPattern(centerJ, centerI, estimatedModuleSize); + } + } + // Hadn't found this before; save it + ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); + possibleCenters.Add(point); + if (resultPointCallback != null) + { + resultPointCallback.foundPossibleResultPoint(point); + } + } + return null; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/Detector.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/Detector.cs new file mode 100644 index 0000000..e98ff54 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/Detector.cs @@ -0,0 +1,424 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using ResultPointCallback = com.google.zxing.ResultPointCallback; +using BitMatrix = com.google.zxing.common.BitMatrix; +using DetectorResult = com.google.zxing.common.DetectorResult; +using GridSampler = com.google.zxing.common.GridSampler; +using PerspectiveTransform = com.google.zxing.common.PerspectiveTransform; +using Version = com.google.zxing.qrcode.decoder.Version; +namespace com.google.zxing.qrcode.detector +{ + + ///

Encapsulates logic that can detect a QR Code in an image, even if the QR Code + /// is rotated or skewed, or partially obscured.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public class Detector + { + virtual protected internal BitMatrix Image + { + get + { + return image; + } + + } + virtual protected internal ResultPointCallback ResultPointCallback + { + get + { + return resultPointCallback; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix image; + private ResultPointCallback resultPointCallback; + + public Detector(BitMatrix image) + { + this.image = image; + } + + ///

Detects a QR Code in an image, simply.

+ /// + ///
+ /// {@link DetectorResult} encapsulating results of detecting a QR Code + /// + /// ReaderException if no QR Code can be found + public virtual DetectorResult detect() + { + return detect(null); + } + + ///

Detects a QR Code in an image, simply.

+ /// + ///
+ /// optional hints to detector + /// + /// {@link DetectorResult} encapsulating results of detecting a QR Code + /// + /// ReaderException if no QR Code can be found + // public virtual DetectorResult detect(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + public virtual DetectorResult detect(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + + // resultPointCallback = hints == null?null:(ResultPointCallback) hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]; // commented by .net follower (http://dotnetfollower.com) + resultPointCallback = null; // added by .net follower (http://dotnetfollower.com) + if (hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) // added by .net follower (http://dotnetfollower.com) + resultPointCallback = (ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]; // added by .net follower (http://dotnetfollower.com) + + FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback); + FinderPatternInfo info = finder.find(hints); + + return processFinderPatternInfo(info); + } + + protected internal virtual DetectorResult processFinderPatternInfo(FinderPatternInfo info) + { + + FinderPattern topLeft = info.TopLeft; + FinderPattern topRight = info.TopRight; + FinderPattern bottomLeft = info.BottomLeft; + + float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); + if (moduleSize < 1.0f) + { + throw ReaderException.Instance; + } + int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); + Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension); + int modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7; + + AlignmentPattern alignmentPattern = null; + // Anything above version 1 has an alignment pattern + if (provisionalVersion.AlignmentPatternCenters.Length > 0) + { + + // Guess where a "bottom right" finder pattern would have been + float bottomRightX = topRight.X - topLeft.X + bottomLeft.X; + float bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y; + + // Estimate that alignment pattern is closer by 3 modules + // from "bottom right" to known top left location + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int estAlignmentX = (int) (topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X)); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int estAlignmentY = (int) (topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y)); + + // Kind of arbitrary -- expand search radius before giving up + for (int i = 4; i <= 16; i <<= 1) + { + try + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float) i); + break; + } + catch (ReaderException re) + { + // try next round + } + } + // If we didn't find alignment pattern... well try anyway without it + } + + PerspectiveTransform transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); + + BitMatrix bits = sampleGrid(image, transform, dimension); + + ResultPoint[] points; + if (alignmentPattern == null) + { + points = new ResultPoint[]{bottomLeft, topLeft, topRight}; + } + else + { + points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern}; + } + return new DetectorResult(bits, points); + } + + public virtual PerspectiveTransform createTransform(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint alignmentPattern, int dimension) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float dimMinusThree = (float) dimension - 3.5f; + float bottomRightX; + float bottomRightY; + float sourceBottomRightX; + float sourceBottomRightY; + if (alignmentPattern != null) + { + bottomRightX = alignmentPattern.X; + bottomRightY = alignmentPattern.Y; + sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f; + } + else + { + // Don't have an alignment pattern, just make up the bottom-right point + bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X; + bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y; + sourceBottomRightX = sourceBottomRightY = dimMinusThree; + } + + PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, sourceBottomRightY, 3.5f, dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX, bottomRightY, bottomLeft.X, bottomLeft.Y); + + return transform; + } + + private static BitMatrix sampleGrid(BitMatrix image, PerspectiveTransform transform, int dimension) + { + + GridSampler sampler = GridSampler.Instance; + return sampler.sampleGrid(image, dimension, transform); + } + + ///

Computes the dimension (number of modules on a size) of the QR Code based on the position + /// of the finder patterns and estimated module size.

+ ///
+ protected internal static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize) + { + int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize); + int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); + int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; + switch (dimension & 0x03) + { + + // mod 4 + case 0: + dimension++; + break; + // 1? do nothing + + case 2: + dimension--; + break; + + case 3: + throw ReaderException.Instance; + } + return dimension; + } + + ///

Computes an average estimated module size based on estimated derived from the positions + /// of the three finder patterns.

+ ///
+ protected internal virtual float calculateModuleSize(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft) + { + // Take the average + return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; + } + + ///

Estimates module size based on two finder patterns -- it uses + /// {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the + /// width of each, measuring along the axis between their centers.

+ ///
+ private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.X, (int) pattern.Y, (int) otherPattern.X, (int) otherPattern.Y); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.X, (int) otherPattern.Y, (int) pattern.X, (int) pattern.Y); + if (System.Single.IsNaN(moduleSizeEst1)) + { + return moduleSizeEst2 / 7.0f; + } + if (System.Single.IsNaN(moduleSizeEst2)) + { + return moduleSizeEst1 / 7.0f; + } + // Average them, and divide by 7 since we've counted the width of 3 black modules, + // and 1 white and 1 black module on either side. Ergo, divide sum by 14. + return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; + } + + /// See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of + /// a finder pattern by looking for a black-white-black run from the center in the direction + /// of another point (another finder pattern center), and in the opposite direction too.

+ ///
+ private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) + { + + float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); + + // Now count other way -- don't run off image though of course + float scale = 1.0f; + int otherToX = fromX - (toX - fromX); + if (otherToX < 0) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + scale = (float) fromX / (float) (fromX - otherToX); + otherToX = 0; + } + else if (otherToX >= image.Width) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + scale = (float) (image.Width - 1 - fromX) / (float) (otherToX - fromX); + otherToX = image.Width - 1; + } + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int otherToY = (int) (fromY - (toY - fromY) * scale); + + scale = 1.0f; + if (otherToY < 0) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + scale = (float) fromY / (float) (fromY - otherToY); + otherToY = 0; + } + else if (otherToY >= image.Height) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + scale = (float) (image.Height - 1 - fromY) / (float) (otherToY - fromY); + otherToY = image.Height - 1; + } + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + otherToX = (int) (fromX + (otherToX - fromX) * scale); + + result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); + return result - 1.0f; // -1 because we counted the middle pixel twice + } + + ///

This method traces a line from a point in the image, in the direction towards another point. + /// It begins in a black region, and keeps going until it finds white, then black, then white again. + /// It reports the distance from the start to this point.

+ /// + ///

This is used when figuring out how wide a finder pattern is, when the finder pattern + /// may be skewed or rotated.

+ ///
+ private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) + { + // Mild variant of Bresenham's algorithm; + // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm + bool steep = System.Math.Abs(toY - fromY) > System.Math.Abs(toX - fromX); + if (steep) + { + int temp = fromX; + fromX = fromY; + fromY = temp; + temp = toX; + toX = toY; + toY = temp; + } + + int dx = System.Math.Abs(toX - fromX); + int dy = System.Math.Abs(toY - fromY); + int error = - dx >> 1; + int ystep = fromY < toY?1:- 1; + int xstep = fromX < toX?1:- 1; + int state = 0; // In black pixels, looking for white, first or second time + for (int x = fromX, y = fromY; x != toX; x += xstep) + { + + int realX = steep?y:x; + int realY = steep?x:y; + if (state == 1) + { + // In white pixels, looking for black + if (image.get_Renamed(realX, realY)) + { + state++; + } + } + else + { + if (!image.get_Renamed(realX, realY)) + { + state++; + } + } + + if (state == 3) + { + // Found black, white, black, and stumbled back onto white; done + int diffX = x - fromX; + int diffY = y - fromY; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (float) System.Math.Sqrt((double) (diffX * diffX + diffY * diffY)); + } + error += dy; + if (error > 0) + { + if (y == toY) + { + break; + } + y += ystep; + error -= dx; + } + } + int diffX2 = toX - fromX; + int diffY2 = toY - fromY; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (float) System.Math.Sqrt((double) (diffX2 * diffX2 + diffY2 * diffY2)); + } + + ///

Attempts to locate an alignment pattern in a limited region of the image, which is + /// guessed to contain it. This method uses {@link AlignmentPattern}.

+ /// + ///
+ /// estimated module size so far + /// + /// x coordinate of center of area probably containing alignment pattern + /// + /// y coordinate of above + /// + /// number of pixels in all directions to search from the center + /// + /// {@link AlignmentPattern} if found, or null otherwise + /// + /// ReaderException if an unexpected error occurs during detection + protected internal virtual AlignmentPattern findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, float allowanceFactor) + { + // Look for an alignment pattern (3 modules in size) around where it + // should be + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + int allowance = (int) (allowanceFactor * overallEstModuleSize); + int alignmentAreaLeftX = System.Math.Max(0, estAlignmentX - allowance); + int alignmentAreaRightX = System.Math.Min(image.Width - 1, estAlignmentX + allowance); + if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) + { + throw ReaderException.Instance; + } + + int alignmentAreaTopY = System.Math.Max(0, estAlignmentY - allowance); + int alignmentAreaBottomY = System.Math.Min(image.Height - 1, estAlignmentY + allowance); + + AlignmentPatternFinder alignmentFinder = new AlignmentPatternFinder(image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, resultPointCallback); + return alignmentFinder.find(); + } + + /// Ends up being a bit faster than Math.round(). This merely rounds its argument to the nearest int, + /// where x.5 rounds up. + /// + private static int round(float d) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (int) (d + 0.5f); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPattern.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPattern.cs new file mode 100644 index 0000000..8b01a47 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPattern.cs @@ -0,0 +1,77 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using ResultPoint = com.google.zxing.ResultPoint; +namespace com.google.zxing.qrcode.detector +{ + + ///

Encapsulates a finder pattern, which are the three square patterns found in + /// the corners of QR Codes. It also encapsulates a count of similar finder patterns, + /// as a convenience to the finder's bookkeeping.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class FinderPattern:ResultPoint + { + public float EstimatedModuleSize + { + get + { + return estimatedModuleSize; + } + + } + internal int Count + { + get + { + return count; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'estimatedModuleSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private float estimatedModuleSize; + private int count; + + internal FinderPattern(float posX, float posY, float estimatedModuleSize):base(posX, posY) + { + this.estimatedModuleSize = estimatedModuleSize; + this.count = 1; + } + + internal void incrementCount() + { + this.count++; + } + + ///

Determines if this finder pattern "about equals" a finder pattern at the stated + /// position and size -- meaning, it is at nearly the same center with nearly the same size.

+ ///
+ internal bool aboutEquals(float moduleSize, float i, float j) + { + if (System.Math.Abs(i - Y) <= moduleSize && System.Math.Abs(j - X) <= moduleSize) + { + float moduleSizeDiff = System.Math.Abs(moduleSize - estimatedModuleSize); + return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f; + } + return false; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPatternFinder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPatternFinder.cs new file mode 100644 index 0000000..54f68dd --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPatternFinder.cs @@ -0,0 +1,650 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +using DecodeHintType = com.google.zxing.DecodeHintType; +using ReaderException = com.google.zxing.ReaderException; +using ResultPoint = com.google.zxing.ResultPoint; +using ResultPointCallback = com.google.zxing.ResultPointCallback; +using Collections = com.google.zxing.common.Collections; +using Comparator = com.google.zxing.common.Comparator; +using BitMatrix = com.google.zxing.common.BitMatrix; +namespace com.google.zxing.qrcode.detector +{ + + ///

This class attempts to find finder patterns in a QR Code. Finder patterns are the square + /// markers at three corners of a QR Code.

+ /// + ///

This class is thread-safe but not reentrant. Each thread must allocate its own object. + /// + ///

+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public class FinderPatternFinder + { + virtual protected internal BitMatrix Image + { + get + { + return image; + } + + } + // virtual protected internal System.Collections.ArrayList PossibleCenters // commented by .net follower (http://dotnetfollower.com) + virtual protected internal System.Collections.Generic.List PossibleCenters // added by .net follower (http://dotnetfollower.com) + { + get + { + return possibleCenters; + } + + } + private int[] CrossCheckStateCount + { + get + { + crossCheckStateCount[0] = 0; + crossCheckStateCount[1] = 0; + crossCheckStateCount[2] = 0; + crossCheckStateCount[3] = 0; + crossCheckStateCount[4] = 0; + return crossCheckStateCount; + } + + } + + private const int CENTER_QUORUM = 2; + protected internal const int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center + protected internal const int MAX_MODULES = 57; // support up to version 10 for mobile clients + private const int INTEGER_MATH_SHIFT = 8; + + //UPGRADE_NOTE: Final was removed from the declaration of 'image '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private BitMatrix image; + //UPGRADE_NOTE: Final was removed from the declaration of 'possibleCenters '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + // private System.Collections.ArrayList possibleCenters; // commented by .net follower (http://dotnetfollower.com) + private System.Collections.Generic.List possibleCenters; // added by .net follower (http://dotnetfollower.com) + private bool hasSkipped; + //UPGRADE_NOTE: Final was removed from the declaration of 'crossCheckStateCount '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private int[] crossCheckStateCount; + //UPGRADE_NOTE: Final was removed from the declaration of 'resultPointCallback '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ResultPointCallback resultPointCallback; + + ///

Creates a finder that will search the image for three finder patterns.

+ /// + ///
+ /// image to search + /// + public FinderPatternFinder(BitMatrix image):this(image, null) + { + } + + public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) + { + this.image = image; + // this.possibleCenters = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // commented by .net follower (http://dotnetfollower.com) + this.possibleCenters = new System.Collections.Generic.List(10); // added by .net follower (http://dotnetfollower.com) + this.crossCheckStateCount = new int[5]; + this.resultPointCallback = resultPointCallback; + } + + // internal virtual FinderPatternInfo find(System.Collections.Hashtable hints) // commented by .net follower (http://dotnetfollower.com) + internal virtual FinderPatternInfo find(System.Collections.Generic.Dictionary hints) // added by .net follower (http://dotnetfollower.com) + { + bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER); + int maxI = image.Height; + int maxJ = image.Width; + // We are looking for black/white/black/white/black modules in + // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far + + // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the + // image, and then account for the center being 3 modules in size. This gives the smallest + // number of pixels the center could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + int iSkip = (3 * maxI) / (4 * MAX_MODULES); + if (iSkip < MIN_SKIP || tryHarder) + { + iSkip = MIN_SKIP; + } + + bool done = false; + int[] stateCount = new int[5]; + for (int i = iSkip - 1; i < maxI && !done; i += iSkip) + { + // Get a row of black/white values + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + int currentState = 0; + for (int j = 0; j < maxJ; j++) + { + if (image.get_Renamed(j, i)) + { + // Black pixel + if ((currentState & 1) == 1) + { + // Counting white pixels + currentState++; + } + stateCount[currentState]++; + } + else + { + // White pixel + if ((currentState & 1) == 0) + { + // Counting black pixels + if (currentState == 4) + { + // A winner? + if (foundPatternCross(stateCount)) + { + // Yes + bool confirmed = handlePossibleCenter(stateCount, i, j); + if (confirmed) + { + // Start examining every other line. Checking each line turned out to be too + // expensive and didn't improve performance. + iSkip = 2; + if (hasSkipped) + { + done = haveMultiplyConfirmedCenters(); + } + else + { + int rowSkip = findRowSkip(); + if (rowSkip > stateCount[2]) + { + // Skip rows between row of lower confirmed center + // and top of presumed third confirmed center + // but back up a bit to get a full chance of detecting + // it, entire width of center of finder pattern + + // Skip by rowSkip, but back off by stateCount[2] (size of last center + // of pattern we saw) to be conservative, and also back off by iSkip which + // is about to be re-added + i += rowSkip - stateCount[2] - iSkip; + j = maxJ - 1; + } + } + } + else + { + // Advance to next black pixel + do + { + j++; + } + while (j < maxJ && !image.get_Renamed(j, i)); + j--; // back up to that last white pixel + } + // Clear state to start looking again + currentState = 0; + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + } + else + { + // No, shift counts back by two + stateCount[0] = stateCount[2]; + stateCount[1] = stateCount[3]; + stateCount[2] = stateCount[4]; + stateCount[3] = 1; + stateCount[4] = 0; + currentState = 3; + } + } + else + { + stateCount[++currentState]++; + } + } + else + { + // Counting white pixels + stateCount[currentState]++; + } + } + } + if (foundPatternCross(stateCount)) + { + bool confirmed = handlePossibleCenter(stateCount, i, maxJ); + if (confirmed) + { + iSkip = stateCount[0]; + if (hasSkipped) + { + // Found a third one + done = haveMultiplyConfirmedCenters(); + } + } + } + } + + FinderPattern[] patternInfo = selectBestPatterns(); + ResultPoint.orderBestPatterns(patternInfo); + + return new FinderPatternInfo(patternInfo); + } + + /// Given a count of black/white/black/white/black pixels just seen and an end position, + /// figures the location of the center of this run. + /// + private static float centerFromEnd(int[] stateCount, int end) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (float) (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f; + } + + /// count of black/white/black/white/black pixels just read + /// + /// true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios + /// used by finder patterns to be considered a match + /// + protected internal static bool foundPatternCross(int[] stateCount) + { + int totalModuleSize = 0; + for (int i = 0; i < 5; i++) + { + int count = stateCount[i]; + if (count == 0) + { + return false; + } + totalModuleSize += count; + } + if (totalModuleSize < 7) + { + return false; + } + int moduleSize = (totalModuleSize << INTEGER_MATH_SHIFT) / 7; + int maxVariance = moduleSize / 2; + // Allow less than 50% variance from 1-1-3-1-1 proportions + return System.Math.Abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance && System.Math.Abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance && System.Math.Abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance && System.Math.Abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance && System.Math.Abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance; + } + + ///

After a horizontal scan finds a potential finder pattern, this method + /// "cross-checks" by scanning down vertically through the center of the possible + /// finder pattern to see if the same proportion is detected.

+ /// + ///
+ /// row where a finder pattern was detected + /// + /// center of the section that appears to cross a finder pattern + /// + /// maximum reasonable number of modules that should be + /// observed in any reading state, based on the results of the horizontal scan + /// + /// vertical center of finder pattern, or {@link Float#NaN} if not found + /// + private float crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal) + { + BitMatrix image = this.image; + + int maxI = image.Height; + int[] stateCount = CrossCheckStateCount; + + // Start counting up from center + int i = startI; + while (i >= 0 && image.get_Renamed(centerJ, i)) + { + stateCount[2]++; + i--; + } + if (i < 0) + { + return System.Single.NaN; + } + while (i >= 0 && !image.get_Renamed(centerJ, i) && stateCount[1] <= maxCount) + { + stateCount[1]++; + i--; + } + // If already too many modules in this state or ran off the edge: + if (i < 0 || stateCount[1] > maxCount) + { + return System.Single.NaN; + } + while (i >= 0 && image.get_Renamed(centerJ, i) && stateCount[0] <= maxCount) + { + stateCount[0]++; + i--; + } + if (stateCount[0] > maxCount) + { + return System.Single.NaN; + } + + // Now also count down from center + i = startI + 1; + while (i < maxI && image.get_Renamed(centerJ, i)) + { + stateCount[2]++; + i++; + } + if (i == maxI) + { + return System.Single.NaN; + } + while (i < maxI && !image.get_Renamed(centerJ, i) && stateCount[3] < maxCount) + { + stateCount[3]++; + i++; + } + if (i == maxI || stateCount[3] >= maxCount) + { + return System.Single.NaN; + } + while (i < maxI && image.get_Renamed(centerJ, i) && stateCount[4] < maxCount) + { + stateCount[4]++; + i++; + } + if (stateCount[4] >= maxCount) + { + return System.Single.NaN; + } + + // If we found a finder-pattern-like section, but its size is more than 40% different than + // the original, assume it's a false positive + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + if (5 * System.Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) + { + return System.Single.NaN; + } + + return foundPatternCross(stateCount)?centerFromEnd(stateCount, i):System.Single.NaN; + } + + ///

Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, + /// except it reads horizontally instead of vertically. This is used to cross-cross + /// check a vertical cross check and locate the real center of the alignment pattern.

+ ///
+ private float crossCheckHorizontal(int startJ, int centerI, int maxCount, int originalStateCountTotal) + { + BitMatrix image = this.image; + + int maxJ = image.Width; + int[] stateCount = CrossCheckStateCount; + + int j = startJ; + while (j >= 0 && image.get_Renamed(j, centerI)) + { + stateCount[2]++; + j--; + } + if (j < 0) + { + return System.Single.NaN; + } + while (j >= 0 && !image.get_Renamed(j, centerI) && stateCount[1] <= maxCount) + { + stateCount[1]++; + j--; + } + if (j < 0 || stateCount[1] > maxCount) + { + return System.Single.NaN; + } + while (j >= 0 && image.get_Renamed(j, centerI) && stateCount[0] <= maxCount) + { + stateCount[0]++; + j--; + } + if (stateCount[0] > maxCount) + { + return System.Single.NaN; + } + + j = startJ + 1; + while (j < maxJ && image.get_Renamed(j, centerI)) + { + stateCount[2]++; + j++; + } + if (j == maxJ) + { + return System.Single.NaN; + } + while (j < maxJ && !image.get_Renamed(j, centerI) && stateCount[3] < maxCount) + { + stateCount[3]++; + j++; + } + if (j == maxJ || stateCount[3] >= maxCount) + { + return System.Single.NaN; + } + while (j < maxJ && image.get_Renamed(j, centerI) && stateCount[4] < maxCount) + { + stateCount[4]++; + j++; + } + if (stateCount[4] >= maxCount) + { + return System.Single.NaN; + } + + // If we found a finder-pattern-like section, but its size is significantly different than + // the original, assume it's a false positive + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + if (5 * System.Math.Abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) + { + return System.Single.NaN; + } + + return foundPatternCross(stateCount)?centerFromEnd(stateCount, j):System.Single.NaN; + } + + ///

This is called when a horizontal scan finds a possible alignment pattern. It will + /// cross check with a vertical scan, and if successful, will, ah, cross-cross-check + /// with another horizontal scan. This is needed primarily to locate the real horizontal + /// center of the pattern in cases of extreme skew.

+ /// + ///

If that succeeds the finder pattern location is added to a list that tracks + /// the number of times each location has been nearly-matched as a finder pattern. + /// Each additional find is more evidence that the location is in fact a finder + /// pattern center + /// + ///

+ /// reading state module counts from horizontal scan + /// + /// row where finder pattern may be found + /// + /// end of possible finder pattern in row + /// + /// true if a finder pattern candidate was found this time + /// + protected internal virtual bool handlePossibleCenter(int[] stateCount, int i, int j) + { + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + float centerJ = centerFromEnd(stateCount, j); + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal); + if (!System.Single.IsNaN(centerI)) + { + // Re-cross check + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal); + if (!System.Single.IsNaN(centerJ)) + { + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float estimatedModuleSize = (float) stateCountTotal / 7.0f; + bool found = false; + int max = possibleCenters.Count; + for (int index = 0; index < max; index++) + { + FinderPattern center = (FinderPattern) possibleCenters[index]; + // Look for about the same center and module size: + if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) + { + center.incrementCount(); + found = true; + break; + } + } + if (!found) + { + ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize); + possibleCenters.Add(point); + if (resultPointCallback != null) + { + resultPointCallback.foundPossibleResultPoint(point); + } + } + return true; + } + } + return false; + } + + /// number of rows we could safely skip during scanning, based on the first + /// two finder patterns that have been located. In some cases their position will + /// allow us to infer that the third pattern must lie below a certain point farther + /// down in the image. + /// + private int findRowSkip() + { + int max = possibleCenters.Count; + if (max <= 1) + { + return 0; + } + FinderPattern firstConfirmedCenter = null; + for (int i = 0; i < max; i++) + { + FinderPattern center = (FinderPattern) possibleCenters[i]; + if (center.Count >= CENTER_QUORUM) + { + if (firstConfirmedCenter == null) + { + firstConfirmedCenter = center; + } + else + { + // We have two confirmed centers + // How far down can we skip before resuming looking for the next + // pattern? In the worst case, only the difference between the + // difference in the x / y coordinates of the two centers. + // This is the case where you find top left last. + hasSkipped = true; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return (int) (System.Math.Abs(firstConfirmedCenter.X - center.X) - System.Math.Abs(firstConfirmedCenter.Y - center.Y)) / 2; + } + } + } + return 0; + } + + /// true iff we have found at least 3 finder patterns that have been detected + /// at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the + /// candidates is "pretty similar" + /// + private bool haveMultiplyConfirmedCenters() + { + int confirmedCount = 0; + float totalModuleSize = 0.0f; + int max = possibleCenters.Count; + for (int i = 0; i < max; i++) + { + FinderPattern pattern = (FinderPattern) possibleCenters[i]; + if (pattern.Count >= CENTER_QUORUM) + { + confirmedCount++; + totalModuleSize += pattern.EstimatedModuleSize; + } + } + if (confirmedCount < 3) + { + return false; + } + // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" + // and that we need to keep looking. We detect this by asking if the estimated module sizes + // vary too much. We arbitrarily say that when the total deviation from average exceeds + // 5% of the total module size estimates, it's too much. + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float average = totalModuleSize / (float) max; + float totalDeviation = 0.0f; + for (int i = 0; i < max; i++) + { + FinderPattern pattern = (FinderPattern) possibleCenters[i]; + totalDeviation += System.Math.Abs(pattern.EstimatedModuleSize - average); + } + return totalDeviation <= 0.05f * totalModuleSize; + } + + /// the 3 best {@link FinderPattern}s from our list of candidates. The "best" are + /// those that have been detected at least {@link #CENTER_QUORUM} times, and whose module + /// size differs from the average among those patterns the least + /// + /// ReaderException if 3 such finder patterns do not exist + private FinderPattern[] selectBestPatterns() + { + + int startSize = possibleCenters.Count; + if (startSize < 3) + { + // Couldn't find enough finder patterns + throw ReaderException.Instance; + } + + // Filter outlier possibilities whose module size is too different + if (startSize > 3) + { + // But we can only afford to do so if we have at least 4 possibilities to choose from + float totalModuleSize = 0.0f; + for (int i = 0; i < startSize; i++) + { + totalModuleSize += ((FinderPattern) possibleCenters[i]).EstimatedModuleSize; + } + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + float average = totalModuleSize / (float) startSize; + for (int i = 0; i < possibleCenters.Count && possibleCenters.Count > 3; i++) + { + FinderPattern pattern = (FinderPattern) possibleCenters[i]; + if (System.Math.Abs(pattern.EstimatedModuleSize - average) > 0.2f * average) + { + possibleCenters.RemoveAt(i); + i--; + } + } + } + + if (possibleCenters.Count > 3) + { + // Throw away all but those first size candidate points we found. + Collections.insertionSort(possibleCenters, new CenterComparator()); + SupportClass.SetCapacity(possibleCenters, 3); + } + + return new FinderPattern[]{(FinderPattern) possibleCenters[0], (FinderPattern) possibleCenters[1], (FinderPattern) possibleCenters[2]}; + } + + ///

Orders by {@link FinderPattern#getCount()}, descending.

+ private class CenterComparator : Comparator + { + public virtual int compare(System.Object center1, System.Object center2) + { + return ((FinderPattern) center2).Count - ((FinderPattern) center1).Count; + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPatternInfo.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPatternInfo.cs new file mode 100644 index 0000000..eaa8dfa --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/detector/FinderPatternInfo.cs @@ -0,0 +1,69 @@ +/* +* Copyright 2007 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.qrcode.detector +{ + + ///

Encapsulates information about finder patterns in an image, including the location of + /// the three finder patterns, and their estimated module size.

+ /// + ///
+ /// Sean Owen + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class FinderPatternInfo + { + public FinderPattern BottomLeft + { + get + { + return bottomLeft; + } + + } + public FinderPattern TopLeft + { + get + { + return topLeft; + } + + } + public FinderPattern TopRight + { + get + { + return topRight; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'bottomLeft '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private FinderPattern bottomLeft; + //UPGRADE_NOTE: Final was removed from the declaration of 'topLeft '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private FinderPattern topLeft; + //UPGRADE_NOTE: Final was removed from the declaration of 'topRight '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private FinderPattern topRight; + + public FinderPatternInfo(FinderPattern[] patternCenters) + { + this.bottomLeft = patternCenters[0]; + this.topLeft = patternCenters[1]; + this.topRight = patternCenters[2]; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/BitVector.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/BitVector.cs new file mode 100644 index 0000000..60ac22d --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/BitVector.cs @@ -0,0 +1,196 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +namespace com.google.zxing.qrcode.encoder +{ + + /// JAVAPORT: This should be combined with BitArray in the future, although that class is not yet + /// dynamically resizeable. This implementation is reasonable but there is a lot of function calling + /// in loops I'd like to get rid of. + /// + /// + /// satorux@google.com (Satoru Takabayashi) - creator + /// + /// dswitkin@google.com (Daniel Switkin) - ported from C++ + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class BitVector + { + public sbyte[] Array + { + // Callers should not assume that array.length is the exact number of bytes needed to hold + // sizeInBits - it will typically be larger for efficiency. + + get + { + return array; + } + + } + + private int sizeInBits; + private sbyte[] array; + + // For efficiency, start out with some room to work. + private const int DEFAULT_SIZE_IN_BYTES = 32; + + public BitVector() + { + sizeInBits = 0; + array = new sbyte[DEFAULT_SIZE_IN_BYTES]; + } + + // Return the bit value at "index". + public int at(int index) + { + if (index < 0 || index >= sizeInBits) + { + throw new System.ArgumentException("Bad index: " + index); + } + int value_Renamed = array[index >> 3] & 0xff; + return (value_Renamed >> (7 - (index & 0x7))) & 1; + } + + // Return the number of bits in the bit vector. + public int size() + { + return sizeInBits; + } + + // Return the number of bytes in the bit vector. + public int sizeInBytes() + { + return (sizeInBits + 7) >> 3; + } + + // Append one bit to the bit vector. + public void appendBit(int bit) + { + if (!(bit == 0 || bit == 1)) + { + throw new System.ArgumentException("Bad bit"); + } + int numBitsInLastByte = sizeInBits & 0x7; + // We'll expand array if we don't have bits in the last byte. + if (numBitsInLastByte == 0) + { + appendByte(0); + sizeInBits -= 8; + } + // Modify the last byte. + array[sizeInBits >> 3] |= (sbyte) ((bit << (7 - numBitsInLastByte))); + ++sizeInBits; + } + + // Append "numBits" bits in "value" to the bit vector. + // REQUIRES: 0<= numBits <= 32. + // + // Examples: + // - appendBits(0x00, 1) adds 0. + // - appendBits(0x00, 4) adds 0000. + // - appendBits(0xff, 8) adds 11111111. + public void appendBits(int value_Renamed, int numBits) + { + if (numBits < 0 || numBits > 32) + { + throw new System.ArgumentException("Num bits must be between 0 and 32"); + } + int numBitsLeft = numBits; + while (numBitsLeft > 0) + { + // Optimization for byte-oriented appending. + if ((sizeInBits & 0x7) == 0 && numBitsLeft >= 8) + { + int newByte = (value_Renamed >> (numBitsLeft - 8)) & 0xff; + appendByte(newByte); + numBitsLeft -= 8; + } + else + { + int bit = (value_Renamed >> (numBitsLeft - 1)) & 1; + appendBit(bit); + --numBitsLeft; + } + } + } + + // Append "bits". + public void appendBitVector(BitVector bits) + { + int size = bits.size(); + for (int i = 0; i < size; ++i) + { + appendBit(bits.at(i)); + } + } + + // Modify the bit vector by XOR'ing with "other" + public void xor(BitVector other) + { + if (sizeInBits != other.size()) + { + throw new System.ArgumentException("BitVector sizes don't match"); + } + int sizeInBytes = (sizeInBits + 7) >> 3; + for (int i = 0; i < sizeInBytes; ++i) + { + // The last byte could be incomplete (i.e. not have 8 bits in + // it) but there is no problem since 0 XOR 0 == 0. + array[i] ^= other.array[i]; + } + } + + // Return String like "01110111" for debugging. + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(sizeInBits); + for (int i = 0; i < sizeInBits; ++i) + { + if (at(i) == 0) + { + result.Append('0'); + } + else if (at(i) == 1) + { + result.Append('1'); + } + else + { + throw new System.ArgumentException("Byte isn't 0 or 1"); + } + } + return result.ToString(); + } + + // Add a new byte to the end, possibly reallocating and doubling the size of the array if we've + // run out of room. + private void appendByte(int value_Renamed) + { + if ((sizeInBits >> 3) == array.Length) + { + sbyte[] newArray = new sbyte[(array.Length << 1)]; + // Redivivus.in Java to c# Porting update + // 30/01/2010 + // added namespace system + System.Array.Copy(array, 0, newArray, 0, array.Length); + array = newArray; + } + array[sizeInBits >> 3] = (sbyte) value_Renamed; + sizeInBits += 8; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/BlockPair.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/BlockPair.cs new file mode 100644 index 0000000..6f3a298 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/BlockPair.cs @@ -0,0 +1,51 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ByteArray = com.google.zxing.common.ByteArray; +namespace com.google.zxing.qrcode.encoder +{ + + sealed class BlockPair + { + public ByteArray DataBytes + { + get + { + return dataBytes; + } + + } + public ByteArray ErrorCorrectionBytes + { + get + { + return errorCorrectionBytes; + } + + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'dataBytes '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ByteArray dataBytes; + //UPGRADE_NOTE: Final was removed from the declaration of 'errorCorrectionBytes '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private ByteArray errorCorrectionBytes; + + internal BlockPair(ByteArray data, ByteArray errorCorrection) + { + dataBytes = data; + errorCorrectionBytes = errorCorrection; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/Encoder.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/Encoder.cs new file mode 100644 index 0000000..3e55abb --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/Encoder.cs @@ -0,0 +1,648 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using WriterException = com.google.zxing.WriterException; +using EncodeHintType = com.google.zxing.EncodeHintType; +using ByteArray = com.google.zxing.common.ByteArray; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +using CharacterSetECI = com.google.zxing.common.CharacterSetECI; +using GF256 = com.google.zxing.common.reedsolomon.GF256; +using ReedSolomonEncoder = com.google.zxing.common.reedsolomon.ReedSolomonEncoder; +using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +using Mode = com.google.zxing.qrcode.decoder.Mode; +using Version = com.google.zxing.qrcode.decoder.Version; +namespace com.google.zxing.qrcode.encoder +{ + + /// satorux@google.com (Satoru Takabayashi) - creator + /// + /// dswitkin@google.com (Daniel Switkin) - ported from C++ + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class Encoder + { + + // The original table is defined in the table 5 of JISX0510:2004 (p.19). + //UPGRADE_NOTE: Final was removed from the declaration of 'ALPHANUMERIC_TABLE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[] ALPHANUMERIC_TABLE = new int[]{- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, 36, - 1, - 1, - 1, 37, 38, - 1, - 1, - 1, - 1, 39, 40, - 1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, - 1, - 1, - 1, - 1, - 1, - 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 1, - 1, - 1, - 1, - 1}; + + internal const System.String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; + + private Encoder() + { + } + + // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. + // Basically it applies four rules and summate all penalties. + private static int calculateMaskPenalty(ByteMatrix matrix) + { + int penalty = 0; + penalty += MaskUtil.applyMaskPenaltyRule1(matrix); + penalty += MaskUtil.applyMaskPenaltyRule2(matrix); + penalty += MaskUtil.applyMaskPenaltyRule3(matrix); + penalty += MaskUtil.applyMaskPenaltyRule4(matrix); + return penalty; + } + + /// Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen + /// internally by chooseMode(). On success, store the result in "qrCode". + /// + /// We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for + /// "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very + /// strong error correction for this purpose. + /// + /// Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode() + /// with which clients can specify the encoding mode. For now, we don't need the functionality. + /// + public static void encode(System.String content, ErrorCorrectionLevel ecLevel, QRCode qrCode) + { + encode(content, ecLevel, null, qrCode); + } + + // public static void encode(System.String content, ErrorCorrectionLevel ecLevel, System.Collections.Hashtable hints, QRCode qrCode) // commented by .net follower (http://dotnetfollower.com) + public static void encode(System.String content, ErrorCorrectionLevel ecLevel, System.Collections.Generic.Dictionary hints, QRCode qrCode) // added by .net follower (http://dotnetfollower.com) + { + + // System.String encoding = hints == null?null:(System.String) hints[EncodeHintType.CHARACTER_SET]; // commented by .net follower (http://dotnetfollower.com) + System.String encoding = null; // added by .net follower (http://dotnetfollower.com) + if (hints != null && hints.ContainsKey(EncodeHintType.CHARACTER_SET)) // added by .net follower (http://dotnetfollower.com) + encoding = (System.String)hints[EncodeHintType.CHARACTER_SET]; // added by .net follower (http://dotnetfollower.com) + + if (encoding == null) + { + encoding = DEFAULT_BYTE_MODE_ENCODING; + } + + // Step 1: Choose the mode (encoding). + Mode mode = chooseMode(content, encoding); + + // Step 2: Append "bytes" into "dataBits" in appropriate encoding. + BitVector dataBits = new BitVector(); + appendBytes(content, mode, dataBits, encoding); + // Step 3: Initialize QR code that can contain "dataBits". + int numInputBytes = dataBits.sizeInBytes(); + initQRCode(numInputBytes, ecLevel, mode, qrCode); + + // Step 4: Build another bit vector that contains header and data. + BitVector headerAndDataBits = new BitVector(); + + // Step 4.5: Append ECI message if applicable + if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding)) + { + CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding); + if (eci != null) + { + appendECI(eci, headerAndDataBits); + } + } + + appendModeInfo(mode, headerAndDataBits); + + int numLetters = mode.Equals(Mode.BYTE)?dataBits.sizeInBytes():content.Length; + appendLengthInfo(numLetters, qrCode.Version, mode, headerAndDataBits); + headerAndDataBits.appendBitVector(dataBits); + + // Step 5: Terminate the bits properly. + terminateBits(qrCode.NumDataBytes, headerAndDataBits); + + // Step 6: Interleave data bits with error correction code. + BitVector finalBits = new BitVector(); + interleaveWithECBytes(headerAndDataBits, qrCode.NumTotalBytes, qrCode.NumDataBytes, qrCode.NumRSBlocks, finalBits); + + // Step 7: Choose the mask pattern and set to "qrCode". + ByteMatrix matrix = new ByteMatrix(qrCode.MatrixWidth, qrCode.MatrixWidth); + qrCode.MaskPattern = chooseMaskPattern(finalBits, qrCode.ECLevel, qrCode.Version, matrix); + + // Step 8. Build the matrix and set it to "qrCode". + MatrixUtil.buildMatrix(finalBits, qrCode.ECLevel, qrCode.Version, qrCode.MaskPattern, matrix); + qrCode.Matrix = matrix; + // Step 9. Make sure we have a valid QR Code. + if (!qrCode.Valid) + { + throw new WriterException("Invalid QR code: " + qrCode.ToString()); + } + } + + /// the code point of the table used in alphanumeric mode or + /// -1 if there is no corresponding code in the table. + /// + internal static int getAlphanumericCode(int code) + { + if (code < ALPHANUMERIC_TABLE.Length) + { + return ALPHANUMERIC_TABLE[code]; + } + return - 1; + } + + public static Mode chooseMode(System.String content) + { + return chooseMode(content, null); + } + + /// Choose the best mode by examining the content. Note that 'encoding' is used as a hint; + /// if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. + /// + public static Mode chooseMode(System.String content, System.String encoding) + { + if ("Shift_JIS".Equals(encoding)) + { + // Choose Kanji mode if all input are double-byte characters + return isOnlyDoubleByteKanji(content)?Mode.KANJI:Mode.BYTE; + } + bool hasNumeric = false; + bool hasAlphanumeric = false; + for (int i = 0; i < content.Length; ++i) + { + char c = content[i]; + if (c >= '0' && c <= '9') + { + hasNumeric = true; + } + else if (getAlphanumericCode(c) != - 1) + { + hasAlphanumeric = true; + } + else + { + return Mode.BYTE; + } + } + if (hasAlphanumeric) + { + return Mode.ALPHANUMERIC; + } + else if (hasNumeric) + { + return Mode.NUMERIC; + } + return Mode.BYTE; + } + + private static bool isOnlyDoubleByteKanji(System.String content) + { + sbyte[] bytes; + try + { + //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'" + bytes = SupportClass.ToSByteArray(System.Text.Encoding.GetEncoding("Shift_JIS").GetBytes(content)); + } + catch (System.IO.IOException uee) + { + return false; + } + int length = bytes.Length; + if (length % 2 != 0) + { + return false; + } + for (int i = 0; i < length; i += 2) + { + int byte1 = bytes[i] & 0xFF; + if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) + { + return false; + } + } + return true; + } + + private static int chooseMaskPattern(BitVector bits, ErrorCorrectionLevel ecLevel, int version, ByteMatrix matrix) + { + + int minPenalty = System.Int32.MaxValue; // Lower penalty is better. + int bestMaskPattern = - 1; + // We try all mask patterns to choose the best one. + for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) + { + MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix); + int penalty = calculateMaskPenalty(matrix); + if (penalty < minPenalty) + { + minPenalty = penalty; + bestMaskPattern = maskPattern; + } + } + return bestMaskPattern; + } + + /// Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success, + /// modify "qrCode". + /// + private static void initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode, QRCode qrCode) + { + qrCode.ECLevel = ecLevel; + qrCode.Mode = mode; + + // In the following comments, we use numbers of Version 7-H. + for (int versionNum = 1; versionNum <= 40; versionNum++) + { + Version version = Version.getVersionForNumber(versionNum); + // numBytes = 196 + int numBytes = version.TotalCodewords; + // getNumECBytes = 130 + Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); + int numEcBytes = ecBlocks.TotalECCodewords; + // getNumRSBlocks = 5 + int numRSBlocks = ecBlocks.NumBlocks; + // getNumDataBytes = 196 - 130 = 66 + int numDataBytes = numBytes - numEcBytes; + // We want to choose the smallest version which can contain data of "numInputBytes" + some + // extra bits for the header (mode info and length info). The header can be three bytes + // (precisely 4 + 16 bits) at most. Hence we do +3 here. + if (numDataBytes >= numInputBytes + 3) + { + // Yay, we found the proper rs block info! + qrCode.Version = versionNum; + qrCode.NumTotalBytes = numBytes; + qrCode.NumDataBytes = numDataBytes; + qrCode.NumRSBlocks = numRSBlocks; + // getNumECBytes = 196 - 66 = 130 + qrCode.NumECBytes = numEcBytes; + // matrix width = 21 + 6 * 4 = 45 + qrCode.MatrixWidth = version.DimensionForVersion; + return ; + } + } + throw new WriterException("Cannot find proper rs block info (input data too big?)"); + } + + /// Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). + internal static void terminateBits(int numDataBytes, BitVector bits) + { + int capacity = numDataBytes << 3; + if (bits.size() > capacity) + { + throw new WriterException("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity); + } + // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. + // TODO: srowen says we can remove this for loop, since the 4 terminator bits are optional if + // the last byte has less than 4 bits left. So it amounts to padding the last byte with zeroes + // either way. + for (int i = 0; i < 4 && bits.size() < capacity; ++i) + { + bits.appendBit(0); + } + int numBitsInLastByte = bits.size() % 8; + // If the last byte isn't 8-bit aligned, we'll add padding bits. + if (numBitsInLastByte > 0) + { + int numPaddingBits = 8 - numBitsInLastByte; + for (int i = 0; i < numPaddingBits; ++i) + { + bits.appendBit(0); + } + } + // Should be 8-bit aligned here. + if (bits.size() % 8 != 0) + { + throw new WriterException("Number of bits is not a multiple of 8"); + } + // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). + int numPaddingBytes = numDataBytes - bits.sizeInBytes(); + for (int i = 0; i < numPaddingBytes; ++i) + { + if (i % 2 == 0) + { + bits.appendBits(0xec, 8); + } + else + { + bits.appendBits(0x11, 8); + } + } + if (bits.size() != capacity) + { + throw new WriterException("Bits size does not equal capacity"); + } + } + + /// Get number of data bytes and number of error correction bytes for block id "blockID". Store + /// the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of + /// JISX0510:2004 (p.30) + /// + internal static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, int numRSBlocks, int blockID, int[] numDataBytesInBlock, int[] numECBytesInBlock) + { + if (blockID >= numRSBlocks) + { + throw new WriterException("Block ID too large"); + } + // numRsBlocksInGroup2 = 196 % 5 = 1 + int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; + // numRsBlocksInGroup1 = 5 - 1 = 4 + int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; + // numTotalBytesInGroup1 = 196 / 5 = 39 + int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; + // numTotalBytesInGroup2 = 39 + 1 = 40 + int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; + // numDataBytesInGroup1 = 66 / 5 = 13 + int numDataBytesInGroup1 = numDataBytes / numRSBlocks; + // numDataBytesInGroup2 = 13 + 1 = 14 + int numDataBytesInGroup2 = numDataBytesInGroup1 + 1; + // numEcBytesInGroup1 = 39 - 13 = 26 + int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; + // numEcBytesInGroup2 = 40 - 14 = 26 + int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; + // Sanity checks. + // 26 = 26 + if (numEcBytesInGroup1 != numEcBytesInGroup2) + { + throw new WriterException("EC bytes mismatch"); + } + // 5 = 4 + 1. + if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) + { + throw new WriterException("RS blocks mismatch"); + } + // 196 = (13 + 26) * 4 + (14 + 26) * 1 + if (numTotalBytes != ((numDataBytesInGroup1 + numEcBytesInGroup1) * numRsBlocksInGroup1) + ((numDataBytesInGroup2 + numEcBytesInGroup2) * numRsBlocksInGroup2)) + { + throw new WriterException("Total bytes mismatch"); + } + + if (blockID < numRsBlocksInGroup1) + { + numDataBytesInBlock[0] = numDataBytesInGroup1; + numECBytesInBlock[0] = numEcBytesInGroup1; + } + else + { + numDataBytesInBlock[0] = numDataBytesInGroup2; + numECBytesInBlock[0] = numEcBytesInGroup2; + } + } + + /// Interleave "bits" with corresponding error correction bytes. On success, store the result in + /// "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. + /// + internal static void interleaveWithECBytes(BitVector bits, int numTotalBytes, int numDataBytes, int numRSBlocks, BitVector result) + { + + // "bits" must have "getNumDataBytes" bytes of data. + if (bits.sizeInBytes() != numDataBytes) + { + throw new WriterException("Number of bits and data bytes does not match"); + } + + // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll + // store the divided data bytes blocks and error correction bytes blocks into "blocks". + int dataBytesOffset = 0; + int maxNumDataBytes = 0; + int maxNumEcBytes = 0; + + // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. + // System.Collections.ArrayList blocks = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(numRSBlocks)); // commented by .net follower (http://dotnetfollower.com) + System.Collections.Generic.List blocks = new System.Collections.Generic.List(numRSBlocks); // added by .net follower (http://dotnetfollower.com) + + for (int i = 0; i < numRSBlocks; ++i) + { + int[] numDataBytesInBlock = new int[1]; + int[] numEcBytesInBlock = new int[1]; + getNumDataBytesAndNumECBytesForBlockID(numTotalBytes, numDataBytes, numRSBlocks, i, numDataBytesInBlock, numEcBytesInBlock); + + ByteArray dataBytes = new ByteArray(); + dataBytes.set_Renamed(bits.Array, dataBytesOffset, numDataBytesInBlock[0]); + ByteArray ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); + blocks.Add(new BlockPair(dataBytes, ecBytes)); + + maxNumDataBytes = System.Math.Max(maxNumDataBytes, dataBytes.size()); + maxNumEcBytes = System.Math.Max(maxNumEcBytes, ecBytes.size()); + dataBytesOffset += numDataBytesInBlock[0]; + } + if (numDataBytes != dataBytesOffset) + { + throw new WriterException("Data bytes does not match offset"); + } + + // First, place data blocks. + for (int i = 0; i < maxNumDataBytes; ++i) + { + for (int j = 0; j < blocks.Count; ++j) + { + ByteArray dataBytes = ((BlockPair) blocks[j]).DataBytes; + if (i < dataBytes.size()) + { + result.appendBits(dataBytes.at(i), 8); + } + } + } + // Then, place error correction blocks. + for (int i = 0; i < maxNumEcBytes; ++i) + { + for (int j = 0; j < blocks.Count; ++j) + { + ByteArray ecBytes = ((BlockPair) blocks[j]).ErrorCorrectionBytes; + if (i < ecBytes.size()) + { + result.appendBits(ecBytes.at(i), 8); + } + } + } + if (numTotalBytes != result.sizeInBytes()) + { + // Should be same. + throw new WriterException("Interleaving error: " + numTotalBytes + " and " + result.sizeInBytes() + " differ."); + } + } + + internal static ByteArray generateECBytes(ByteArray dataBytes, int numEcBytesInBlock) + { + int numDataBytes = dataBytes.size(); + int[] toEncode = new int[numDataBytes + numEcBytesInBlock]; + for (int i = 0; i < numDataBytes; i++) + { + toEncode[i] = dataBytes.at(i); + } + new ReedSolomonEncoder(GF256.QR_CODE_FIELD).encode(toEncode, numEcBytesInBlock); + + ByteArray ecBytes = new ByteArray(numEcBytesInBlock); + for (int i = 0; i < numEcBytesInBlock; i++) + { + ecBytes.set_Renamed(i, toEncode[numDataBytes + i]); + } + return ecBytes; + } + + /// Append mode info. On success, store the result in "bits". + internal static void appendModeInfo(Mode mode, BitVector bits) + { + bits.appendBits(mode.Bits, 4); + } + + + /// Append length info. On success, store the result in "bits". + internal static void appendLengthInfo(int numLetters, int version, Mode mode, BitVector bits) + { + int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version)); + if (numLetters > ((1 << numBits) - 1)) + { + throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1)); + } + bits.appendBits(numLetters, numBits); + } + + /// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". + internal static void appendBytes(System.String content, Mode mode, BitVector bits, System.String encoding) + { + if (mode.Equals(Mode.NUMERIC)) + { + appendNumericBytes(content, bits); + } + else if (mode.Equals(Mode.ALPHANUMERIC)) + { + appendAlphanumericBytes(content, bits); + } + else if (mode.Equals(Mode.BYTE)) + { + append8BitBytes(content, bits, encoding); + } + else if (mode.Equals(Mode.KANJI)) + { + appendKanjiBytes(content, bits); + } + else + { + throw new WriterException("Invalid mode: " + mode); + } + } + + internal static void appendNumericBytes(System.String content, BitVector bits) + { + int length = content.Length; + int i = 0; + while (i < length) + { + int num1 = content[i] - '0'; + if (i + 2 < length) + { + // Encode three numeric letters in ten bits. + int num2 = content[i + 1] - '0'; + int num3 = content[i + 2] - '0'; + bits.appendBits(num1 * 100 + num2 * 10 + num3, 10); + i += 3; + } + else if (i + 1 < length) + { + // Encode two numeric letters in seven bits. + int num2 = content[i + 1] - '0'; + bits.appendBits(num1 * 10 + num2, 7); + i += 2; + } + else + { + // Encode one numeric letter in four bits. + bits.appendBits(num1, 4); + i++; + } + } + } + + internal static void appendAlphanumericBytes(System.String content, BitVector bits) + { + int length = content.Length; + int i = 0; + while (i < length) + { + int code1 = getAlphanumericCode(content[i]); + if (code1 == - 1) + { + throw new WriterException(); + } + if (i + 1 < length) + { + int code2 = getAlphanumericCode(content[i + 1]); + if (code2 == - 1) + { + throw new WriterException(); + } + // Encode two alphanumeric letters in 11 bits. + bits.appendBits(code1 * 45 + code2, 11); + i += 2; + } + else + { + // Encode one alphanumeric letter in six bits. + bits.appendBits(code1, 6); + i++; + } + } + } + + internal static void append8BitBytes(System.String content, BitVector bits, System.String encoding) + { + sbyte[] bytes; + try + { + //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'" + bytes = SupportClass.ToSByteArray(System.Text.Encoding.GetEncoding(encoding).GetBytes(content)); + } + catch (System.IO.IOException uee) + { + //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" + throw new WriterException(uee.ToString()); + } + for (int i = 0; i < bytes.Length; ++i) + { + bits.appendBits(bytes[i], 8); + } + } + + internal static void appendKanjiBytes(System.String content, BitVector bits) + { + sbyte[] bytes; + try + { + //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'" + bytes = SupportClass.ToSByteArray(System.Text.Encoding.GetEncoding("Shift_JIS").GetBytes(content)); + } + catch (System.IO.IOException uee) + { + //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" + throw new WriterException(uee.ToString()); + } + int length = bytes.Length; + for (int i = 0; i < length; i += 2) + { + int byte1 = bytes[i] & 0xFF; + int byte2 = bytes[i + 1] & 0xFF; + int code = (byte1 << 8) | byte2; + int subtracted = - 1; + if (code >= 0x8140 && code <= 0x9ffc) + { + subtracted = code - 0x8140; + } + else if (code >= 0xe040 && code <= 0xebbf) + { + subtracted = code - 0xc140; + } + if (subtracted == - 1) + { + throw new WriterException("Invalid byte sequence"); + } + int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); + bits.appendBits(encoded, 13); + } + } + + private static void appendECI(CharacterSetECI eci, BitVector bits) + { + bits.appendBits(Mode.ECI.Bits, 4); + // This is correct for values up to 127, which is all we need now. + bits.appendBits(eci.Value, 8); + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/MaskUtil.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/MaskUtil.cs new file mode 100644 index 0000000..e561171 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/MaskUtil.cs @@ -0,0 +1,226 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +namespace com.google.zxing.qrcode.encoder +{ + + /// satorux@google.com (Satoru Takabayashi) - creator + /// + /// dswitkin@google.com (Daniel Switkin) - ported from C++ + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class MaskUtil + { + + private MaskUtil() + { + // do nothing + } + + // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and + // give penalty to them. Example: 00000 or 11111. + public static int applyMaskPenaltyRule1(ByteMatrix matrix) + { + return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false); + } + + // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give + // penalty to them. + public static int applyMaskPenaltyRule2(ByteMatrix matrix) + { + int penalty = 0; + sbyte[][] array = matrix.Array; + int width = matrix.Width; + int height = matrix.Height; + for (int y = 0; y < height - 1; ++y) + { + for (int x = 0; x < width - 1; ++x) + { + int value_Renamed = array[y][x]; + if (value_Renamed == array[y][x + 1] && value_Renamed == array[y + 1][x] && value_Renamed == array[y + 1][x + 1]) + { + penalty += 3; + } + } + } + return penalty; + } + + // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or + // 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give + // penalties twice (i.e. 40 * 2). + public static int applyMaskPenaltyRule3(ByteMatrix matrix) + { + int penalty = 0; + sbyte[][] array = matrix.Array; + int width = matrix.Width; + int height = matrix.Height; + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + // Tried to simplify following conditions but failed. + if (x + 6 < width && array[y][x] == 1 && array[y][x + 1] == 0 && array[y][x + 2] == 1 && array[y][x + 3] == 1 && array[y][x + 4] == 1 && array[y][x + 5] == 0 && array[y][x + 6] == 1 && ((x + 10 < width && array[y][x + 7] == 0 && array[y][x + 8] == 0 && array[y][x + 9] == 0 && array[y][x + 10] == 0) || (x - 4 >= 0 && array[y][x - 1] == 0 && array[y][x - 2] == 0 && array[y][x - 3] == 0 && array[y][x - 4] == 0))) + { + penalty += 40; + } + if (y + 6 < height && array[y][x] == 1 && array[y + 1][x] == 0 && array[y + 2][x] == 1 && array[y + 3][x] == 1 && array[y + 4][x] == 1 && array[y + 5][x] == 0 && array[y + 6][x] == 1 && ((y + 10 < height && array[y + 7][x] == 0 && array[y + 8][x] == 0 && array[y + 9][x] == 0 && array[y + 10][x] == 0) || (y - 4 >= 0 && array[y - 1][x] == 0 && array[y - 2][x] == 0 && array[y - 3][x] == 0 && array[y - 4][x] == 0))) + { + penalty += 40; + } + } + } + return penalty; + } + + // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give + // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples: + // - 0% => 100 + // - 40% => 20 + // - 45% => 10 + // - 50% => 0 + // - 55% => 10 + // - 55% => 20 + // - 100% => 100 + public static int applyMaskPenaltyRule4(ByteMatrix matrix) + { + int numDarkCells = 0; + sbyte[][] array = matrix.Array; + int width = matrix.Width; + int height = matrix.Height; + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + if (array[y][x] == 1) + { + numDarkCells += 1; + } + } + } + int numTotalCells = matrix.Height * matrix.Width; + double darkRatio = (double) numDarkCells / numTotalCells; + //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'" + return System.Math.Abs((int) (darkRatio * 100 - 50)) / 5 * 10; + } + + // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask + // pattern conditions. + public static bool getDataMaskBit(int maskPattern, int x, int y) + { + if (!QRCode.isValidMaskPattern(maskPattern)) + { + throw new System.ArgumentException("Invalid mask pattern"); + } + int intermediate, temp; + switch (maskPattern) + { + + case 0: + intermediate = (y + x) & 0x1; + break; + + case 1: + intermediate = y & 0x1; + break; + + case 2: + intermediate = x % 3; + break; + + case 3: + intermediate = (y + x) % 3; + break; + + case 4: + intermediate = ((SupportClass.URShift(y, 1)) + (x / 3)) & 0x1; + break; + + case 5: + temp = y * x; + intermediate = (temp & 0x1) + (temp % 3); + break; + + case 6: + temp = y * x; + intermediate = (((temp & 0x1) + (temp % 3)) & 0x1); + break; + + case 7: + temp = y * x; + intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1); + break; + + default: + throw new System.ArgumentException("Invalid mask pattern: " + maskPattern); + + } + return intermediate == 0; + } + + // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both + // vertical and horizontal orders respectively. + private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, bool isHorizontal) + { + int penalty = 0; + int numSameBitCells = 0; + int prevBit = - 1; + // Horizontal mode: + // for (int i = 0; i < matrix.height(); ++i) { + // for (int j = 0; j < matrix.width(); ++j) { + // int bit = matrix.get(i, j); + // Vertical mode: + // for (int i = 0; i < matrix.width(); ++i) { + // for (int j = 0; j < matrix.height(); ++j) { + // int bit = matrix.get(j, i); + int iLimit = isHorizontal?matrix.Height:matrix.Width; + int jLimit = isHorizontal?matrix.Width:matrix.Height; + sbyte[][] array = matrix.Array; + for (int i = 0; i < iLimit; ++i) + { + for (int j = 0; j < jLimit; ++j) + { + int bit = isHorizontal?array[i][j]:array[j][i]; + if (bit == prevBit) + { + numSameBitCells += 1; + // Found five repetitive cells with the same color (bit). + // We'll give penalty of 3. + if (numSameBitCells == 5) + { + penalty += 3; + } + else if (numSameBitCells > 5) + { + // After five repetitive cells, we'll add the penalty one + // by one. + penalty += 1; + } + } + else + { + numSameBitCells = 1; // Include the cell itself. + prevBit = bit; + } + } + numSameBitCells = 0; // Clear at each row/column. + } + return penalty; + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/MatrixUtil.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/MatrixUtil.cs new file mode 100644 index 0000000..b461a66 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/MatrixUtil.cs @@ -0,0 +1,518 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using WriterException = com.google.zxing.WriterException; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +namespace com.google.zxing.qrcode.encoder +{ + + /// satorux@google.com (Satoru Takabayashi) - creator + /// + /// dswitkin@google.com (Daniel Switkin) - ported from C++ + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class MatrixUtil + { + + private MatrixUtil() + { + // do nothing + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'POSITION_DETECTION_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] POSITION_DETECTION_PATTERN = new int[][]{new int[]{1, 1, 1, 1, 1, 1, 1}, new int[]{1, 0, 0, 0, 0, 0, 1}, new int[]{1, 0, 1, 1, 1, 0, 1}, new int[]{1, 0, 1, 1, 1, 0, 1}, new int[]{1, 0, 1, 1, 1, 0, 1}, new int[]{1, 0, 0, 0, 0, 0, 1}, new int[]{1, 1, 1, 1, 1, 1, 1}}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'HORIZONTAL_SEPARATION_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] HORIZONTAL_SEPARATION_PATTERN = new int[][]{new int[]{0, 0, 0, 0, 0, 0, 0, 0}}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'VERTICAL_SEPARATION_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] VERTICAL_SEPARATION_PATTERN = new int[][]{new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}, new int[]{0}}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'POSITION_ADJUSTMENT_PATTERN'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] POSITION_ADJUSTMENT_PATTERN = new int[][]{new int[]{1, 1, 1, 1, 1}, new int[]{1, 0, 0, 0, 1}, new int[]{1, 0, 1, 0, 1}, new int[]{1, 0, 0, 0, 1}, new int[]{1, 1, 1, 1, 1}}; + + // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu. + //UPGRADE_NOTE: Final was removed from the declaration of 'POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = new int[][]{new int[]{- 1, - 1, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 18, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 22, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 26, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 30, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 34, - 1, - 1, - 1, - 1, - 1}, new int[]{6, 22, 38, - 1, - 1, - 1, - 1}, new int[]{6, 24, 42, - 1, - 1, - 1, - 1}, new int[]{6, 26, 46, - 1, - 1, - 1, - 1}, new int[]{6, 28, 50, - 1, - 1, - 1, - 1}, new int[]{6, 30, 54, - 1, - 1, - 1, - 1}, new int[]{6, 32, 58, - 1, - 1, - 1, - 1}, new int[]{6, 34, 62, - 1, - 1, - 1, - 1}, new int[]{6, 26, 46, 66, - 1, - 1, - 1}, new int[]{6, 26, 48, 70, - 1, - 1, - 1}, new int[]{6, 26, 50, 74, - 1, - 1, - 1}, new int[]{6, 30, 54, 78, - 1, - 1, - 1}, new int[]{6, 30, 56, 82, - 1, - 1, - 1}, new int[]{6, 30, 58, 86, - 1, - 1, - 1}, new int[]{6, 34, 62, 90, - 1, - 1, - 1}, new int[]{6, 28, 50, 72, 94, - 1, - 1}, new int[]{6, 26, 50, 74, 98, - 1, - 1}, new int[]{6, 30, 54, 78, 102, - 1, - 1}, new int[]{6, 28, 54, 80, 106, - 1, - 1}, new int[]{6, 32, 58, 84, 110, - 1, - 1}, new int[]{6, 30, 58, 86, 114, - 1, - 1}, new int[]{6, 34, 62, 90, 118, - 1, - 1}, new int[]{6, 26, 50, 74, 98, 122, - 1}, new int[]{6, 30, 54, 78, 102, 126, - 1}, new int[]{6, 26, 52, 78, 104, 130, - 1}, new int[]{6, 30, 56, 82, 108, 134, - 1}, new int[]{6, 34, 60, 86, 112, 138, - 1}, new int[]{6, 30, 58, 86, 114, 142, - 1}, new int[]{6, 34, 62, 90, 118, 146, - 1}, new int[]{6, 30, 54, 78, 102, 126, 150}, new int[]{6, 24, 50, 76, 102, 128, 154}, new int[]{6, 28, 54, 80, 106, 132, 158}, new int[]{6, 32, 58, 84, 110, 136, 162}, new int[]{6, 26, 54, 82, 110, 138, 166}, new int[]{6, 30, 58, 86, 114, 142, 170}}; + + // Type info cells at the left top corner. + //UPGRADE_NOTE: Final was removed from the declaration of 'TYPE_INFO_COORDINATES'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private static readonly int[][] TYPE_INFO_COORDINATES = new int[][]{new int[]{8, 0}, new int[]{8, 1}, new int[]{8, 2}, new int[]{8, 3}, new int[]{8, 4}, new int[]{8, 5}, new int[]{8, 7}, new int[]{8, 8}, new int[]{7, 8}, new int[]{5, 8}, new int[]{4, 8}, new int[]{3, 8}, new int[]{2, 8}, new int[]{1, 8}, new int[]{0, 8}}; + + // From Appendix D in JISX0510:2004 (p. 67) + private const int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101 + + // From Appendix C in JISX0510:2004 (p.65). + private const int TYPE_INFO_POLY = 0x537; + private const int TYPE_INFO_MASK_PATTERN = 0x5412; + + // Set all cells to -1. -1 means that the cell is empty (not set yet). + // + // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding + // with the ByteMatrix initialized all to zero. + public static void clearMatrix(ByteMatrix matrix) + { + matrix.clear((sbyte) (- 1)); + } + + // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On + // success, store the result in "matrix" and return true. + public static void buildMatrix(BitVector dataBits, ErrorCorrectionLevel ecLevel, int version, int maskPattern, ByteMatrix matrix) + { + clearMatrix(matrix); + embedBasicPatterns(version, matrix); + // Type information appear with any version. + embedTypeInfo(ecLevel, maskPattern, matrix); + // Version info appear if version >= 7. + maybeEmbedVersionInfo(version, matrix); + // Data should be embedded at end. + embedDataBits(dataBits, maskPattern, matrix); + } + + // Embed basic patterns. On success, modify the matrix and return true. + // The basic patterns are: + // - Position detection patterns + // - Timing patterns + // - Dark dot at the left bottom corner + // - Position adjustment patterns, if need be + public static void embedBasicPatterns(int version, ByteMatrix matrix) + { + // Let's get started with embedding big squares at corners. + embedPositionDetectionPatternsAndSeparators(matrix); + // Then, embed the dark dot at the left bottom corner. + embedDarkDotAtLeftBottomCorner(matrix); + + // Position adjustment patterns appear if version >= 2. + maybeEmbedPositionAdjustmentPatterns(version, matrix); + // Timing patterns should be embedded after position adj. patterns. + embedTimingPatterns(matrix); + } + + // Embed type information. On success, modify the matrix. + public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix) + { + BitVector typeInfoBits = new BitVector(); + makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits); + + for (int i = 0; i < typeInfoBits.size(); ++i) + { + // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in + // "typeInfoBits". + int bit = typeInfoBits.at(typeInfoBits.size() - 1 - i); + + // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46). + int x1 = TYPE_INFO_COORDINATES[i][0]; + int y1 = TYPE_INFO_COORDINATES[i][1]; + matrix.set_Renamed(x1, y1, bit); + + if (i < 8) + { + // Right top corner. + int x2 = matrix.Width - i - 1; + int y2 = 8; + matrix.set_Renamed(x2, y2, bit); + } + else + { + // Left bottom corner. + int x2 = 8; + int y2 = matrix.Height - 7 + (i - 8); + matrix.set_Renamed(x2, y2, bit); + } + } + } + + // Embed version information if need be. On success, modify the matrix and return true. + // See 8.10 of JISX0510:2004 (p.47) for how to embed version information. + public static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) + { + if (version < 7) + { + // Version info is necessary if version >= 7. + return ; // Don't need version info. + } + BitVector versionInfoBits = new BitVector(); + makeVersionInfoBits(version, versionInfoBits); + + int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0. + for (int i = 0; i < 6; ++i) + { + for (int j = 0; j < 3; ++j) + { + // Place bits in LSB (least significant bit) to MSB order. + int bit = versionInfoBits.at(bitIndex); + bitIndex--; + // Left bottom corner. + matrix.set_Renamed(i, matrix.Height - 11 + j, bit); + // Right bottom corner. + matrix.set_Renamed(matrix.Height - 11 + j, i, bit); + } + } + } + + // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true. + // For debugging purposes, it skips masking process if "getMaskPattern" is -1. + // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. + public static void embedDataBits(BitVector dataBits, int maskPattern, ByteMatrix matrix) + { + int bitIndex = 0; + int direction = - 1; + // Start from the right bottom cell. + int x = matrix.Width - 1; + int y = matrix.Height - 1; + while (x > 0) + { + // Skip the vertical timing pattern. + if (x == 6) + { + x -= 1; + } + while (y >= 0 && y < matrix.Height) + { + for (int i = 0; i < 2; ++i) + { + int xx = x - i; + // Skip the cell if it's not empty. + if (!isEmpty(matrix.get_Renamed(xx, y))) + { + continue; + } + int bit; + if (bitIndex < dataBits.size()) + { + bit = dataBits.at(bitIndex); + ++bitIndex; + } + else + { + // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described + // in 8.4.9 of JISX0510:2004 (p. 24). + bit = 0; + } + + // Skip masking if mask_pattern is -1. + if (maskPattern != - 1) + { + if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) + { + bit ^= 0x1; + } + } + matrix.set_Renamed(xx, y, bit); + } + y += direction; + } + direction = - direction; // Reverse the direction. + y += direction; + x -= 2; // Move to the left. + } + // All bits should be consumed. + if (bitIndex != dataBits.size()) + { + throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.size()); + } + } + + // Return the position of the most significant bit set (to one) in the "value". The most + // significant bit is position 32. If there is no bit set, return 0. Examples: + // - findMSBSet(0) => 0 + // - findMSBSet(1) => 1 + // - findMSBSet(255) => 8 + public static int findMSBSet(int value_Renamed) + { + int numDigits = 0; + while (value_Renamed != 0) + { + value_Renamed = SupportClass.URShift(value_Renamed, 1); + ++numDigits; + } + return numDigits; + } + + // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH + // code is used for encoding type information and version information. + // Example: Calculation of version information of 7. + // f(x) is created from 7. + // - 7 = 000111 in 6 bits + // - f(x) = x^2 + x^2 + x^1 + // g(x) is given by the standard (p. 67) + // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1 + // Multiply f(x) by x^(18 - 6) + // - f'(x) = f(x) * x^(18 - 6) + // - f'(x) = x^14 + x^13 + x^12 + // Calculate the remainder of f'(x) / g(x) + // x^2 + // __________________________________________________ + // g(x) )x^14 + x^13 + x^12 + // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2 + // -------------------------------------------------- + // x^11 + x^10 + x^7 + x^4 + x^2 + // + // The remainder is x^11 + x^10 + x^7 + x^4 + x^2 + // Encode it in binary: 110010010100 + // The return value is 0xc94 (1100 1001 0100) + // + // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit + // operations. We don't care if cofficients are positive or negative. + public static int calculateBCHCode(int value_Renamed, int poly) + { + // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1 + // from 13 to make it 12. + int msbSetInPoly = findMSBSet(poly); + value_Renamed <<= msbSetInPoly - 1; + // Do the division business using exclusive-or operations. + while (findMSBSet(value_Renamed) >= msbSetInPoly) + { + value_Renamed ^= poly << (findMSBSet(value_Renamed) - msbSetInPoly); + } + // Now the "value" is the remainder (i.e. the BCH code) + return value_Renamed; + } + + // Make bit vector of type information. On success, store the result in "bits" and return true. + // Encode error correction level and mask pattern. See 8.9 of + // JISX0510:2004 (p.45) for details. + public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitVector bits) + { + if (!QRCode.isValidMaskPattern(maskPattern)) + { + throw new WriterException("Invalid mask pattern"); + } + int typeInfo = (ecLevel.Bits << 3) | maskPattern; + bits.appendBits(typeInfo, 5); + + int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY); + bits.appendBits(bchCode, 10); + + BitVector maskBits = new BitVector(); + maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15); + bits.xor(maskBits); + + if (bits.size() != 15) + { + // Just in case. + throw new WriterException("should not happen but we got: " + bits.size()); + } + } + + // Make bit vector of version information. On success, store the result in "bits" and return true. + // See 8.10 of JISX0510:2004 (p.45) for details. + public static void makeVersionInfoBits(int version, BitVector bits) + { + bits.appendBits(version, 6); + int bchCode = calculateBCHCode(version, VERSION_INFO_POLY); + bits.appendBits(bchCode, 12); + + if (bits.size() != 18) + { + // Just in case. + throw new WriterException("should not happen but we got: " + bits.size()); + } + } + + // Check if "value" is empty. + private static bool isEmpty(int value_Renamed) + { + return value_Renamed == - 1; + } + + // Check if "value" is valid. + private static bool isValidValue(int value_Renamed) + { + return (value_Renamed == - 1 || value_Renamed == 0 || value_Renamed == 1); // Dark (black). + } + + private static void embedTimingPatterns(ByteMatrix matrix) + { + // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical + // separation patterns (size 1). Thus, 8 = 7 + 1. + for (int i = 8; i < matrix.Width - 8; ++i) + { + int bit = (i + 1) % 2; + // Horizontal line. + if (!isValidValue(matrix.get_Renamed(i, 6))) + { + throw new WriterException(); + } + if (isEmpty(matrix.get_Renamed(i, 6))) + { + matrix.set_Renamed(i, 6, bit); + } + // Vertical line. + if (!isValidValue(matrix.get_Renamed(6, i))) + { + throw new WriterException(); + } + if (isEmpty(matrix.get_Renamed(6, i))) + { + matrix.set_Renamed(6, i, bit); + } + } + } + + // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46) + private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) + { + if (matrix.get_Renamed(8, matrix.Height - 8) == 0) + { + throw new WriterException(); + } + matrix.set_Renamed(8, matrix.Height - 8, 1); + } + + private static void embedHorizontalSeparationPattern(int xStart, int yStart, ByteMatrix matrix) + { + // We know the width and height. + if (HORIZONTAL_SEPARATION_PATTERN[0].Length != 8 || HORIZONTAL_SEPARATION_PATTERN.Length != 1) + { + throw new WriterException("Bad horizontal separation pattern"); + } + for (int x = 0; x < 8; ++x) + { + if (!isEmpty(matrix.get_Renamed(xStart + x, yStart))) + { + throw new WriterException(); + } + matrix.set_Renamed(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]); + } + } + + private static void embedVerticalSeparationPattern(int xStart, int yStart, ByteMatrix matrix) + { + // We know the width and height. + if (VERTICAL_SEPARATION_PATTERN[0].Length != 1 || VERTICAL_SEPARATION_PATTERN.Length != 7) + { + throw new WriterException("Bad vertical separation pattern"); + } + for (int y = 0; y < 7; ++y) + { + if (!isEmpty(matrix.get_Renamed(xStart, yStart + y))) + { + throw new WriterException(); + } + matrix.set_Renamed(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]); + } + } + + // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are + // almost identical, since we cannot write a function that takes 2D arrays in different sizes in + // C/C++. We should live with the fact. + private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) + { + // We know the width and height. + if (POSITION_ADJUSTMENT_PATTERN[0].Length != 5 || POSITION_ADJUSTMENT_PATTERN.Length != 5) + { + throw new WriterException("Bad position adjustment"); + } + for (int y = 0; y < 5; ++y) + { + for (int x = 0; x < 5; ++x) + { + if (!isEmpty(matrix.get_Renamed(xStart + x, yStart + y))) + { + throw new WriterException(); + } + matrix.set_Renamed(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]); + } + } + } + + private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) + { + // We know the width and height. + if (POSITION_DETECTION_PATTERN[0].Length != 7 || POSITION_DETECTION_PATTERN.Length != 7) + { + throw new WriterException("Bad position detection pattern"); + } + for (int y = 0; y < 7; ++y) + { + for (int x = 0; x < 7; ++x) + { + if (!isEmpty(matrix.get_Renamed(xStart + x, yStart + y))) + { + throw new WriterException(); + } + matrix.set_Renamed(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]); + } + } + } + + // Embed position detection patterns and surrounding vertical/horizontal separators. + private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) + { + // Embed three big squares at corners. + int pdpWidth = POSITION_DETECTION_PATTERN[0].Length; + // Left top corner. + embedPositionDetectionPattern(0, 0, matrix); + // Right top corner. + embedPositionDetectionPattern(matrix.Width - pdpWidth, 0, matrix); + // Left bottom corner. + embedPositionDetectionPattern(0, matrix.Width - pdpWidth, matrix); + + // Embed horizontal separation patterns around the squares. + int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].Length; + // Left top corner. + embedHorizontalSeparationPattern(0, hspWidth - 1, matrix); + // Right top corner. + embedHorizontalSeparationPattern(matrix.Width - hspWidth, hspWidth - 1, matrix); + // Left bottom corner. + embedHorizontalSeparationPattern(0, matrix.Width - hspWidth, matrix); + + // Embed vertical separation patterns around the squares. + int vspSize = VERTICAL_SEPARATION_PATTERN.Length; + // Left top corner. + embedVerticalSeparationPattern(vspSize, 0, matrix); + // Right top corner. + embedVerticalSeparationPattern(matrix.Height - vspSize - 1, 0, matrix); + // Left bottom corner. + embedVerticalSeparationPattern(vspSize, matrix.Height - vspSize, matrix); + } + + // Embed position adjustment patterns if need be. + private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix) + { + if (version < 2) + { + // The patterns appear if version >= 2 + return ; + } + int index = version - 1; + int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index]; + int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].Length; + for (int i = 0; i < numCoordinates; ++i) + { + for (int j = 0; j < numCoordinates; ++j) + { + int y = coordinates[i]; + int x = coordinates[j]; + if (x == - 1 || y == - 1) + { + continue; + } + // If the cell is unset, we embed the position adjustment pattern here. + if (isEmpty(matrix.get_Renamed(x, y))) + { + // -2 is necessary since the x/y coordinates point to the center of the pattern, not the + // left top corner. + embedPositionAdjustmentPattern(x - 2, y - 2, matrix); + } + } + } + } + } +} \ No newline at end of file diff --git a/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/QRCode.cs b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/QRCode.cs new file mode 100644 index 0000000..972fd02 --- /dev/null +++ b/WindowsPhone/BarcodeScanner/sources/ZXing7_1Port/qrcode/encoder/QRCode.cs @@ -0,0 +1,299 @@ +/* +* Copyright 2008 ZXing authors +* +* 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. +*/ +using System; +using ByteMatrix = com.google.zxing.common.ByteMatrix; +using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +using Mode = com.google.zxing.qrcode.decoder.Mode; +namespace com.google.zxing.qrcode.encoder +{ + + /// satorux@google.com (Satoru Takabayashi) - creator + /// + /// dswitkin@google.com (Daniel Switkin) - ported from C++ + /// + /// www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source + /// + public sealed class QRCode + { + public Mode Mode + { + // Mode of the QR Code. + + get + { + return mode; + } + + set + { + mode = value; + } + + } + public ErrorCorrectionLevel ECLevel + { + // Error correction level of the QR Code. + + get + { + return ecLevel; + } + + set + { + ecLevel = value; + } + + } + public int Version + { + // Version of the QR Code. The bigger size, the bigger version. + + get + { + return version; + } + + set + { + version = value; + } + + } + public int MatrixWidth + { + // ByteMatrix width of the QR Code. + + get + { + return matrixWidth; + } + + set + { + matrixWidth = value; + } + + } + public int MaskPattern + { + // Mask pattern of the QR Code. + + get + { + return maskPattern; + } + + set + { + maskPattern = value; + } + + } + public int NumTotalBytes + { + // Number of total bytes in the QR Code. + + get + { + return numTotalBytes; + } + + set + { + numTotalBytes = value; + } + + } + public int NumDataBytes + { + // Number of data bytes in the QR Code. + + get + { + return numDataBytes; + } + + set + { + numDataBytes = value; + } + + } + public int NumECBytes + { + // Number of error correction bytes in the QR Code. + + get + { + return numECBytes; + } + + set + { + numECBytes = value; + } + + } + public int NumRSBlocks + { + // Number of Reedsolomon blocks in the QR Code. + + get + { + return numRSBlocks; + } + + set + { + numRSBlocks = value; + } + + } + public ByteMatrix Matrix + { + // ByteMatrix data of the QR Code. + + get + { + return matrix; + } + + // This takes ownership of the 2D array. + + set + { + matrix = value; + } + + } + public bool Valid + { + // Checks all the member variables are set properly. Returns true on success. Otherwise, returns + // false. + + get + { + return mode != null && ecLevel != null && version != - 1 && matrixWidth != - 1 && maskPattern != - 1 && numTotalBytes != - 1 && numDataBytes != - 1 && numECBytes != - 1 && numRSBlocks != - 1 && isValidMaskPattern(maskPattern) && numTotalBytes == numDataBytes + numECBytes && matrix != null && matrixWidth == matrix.Width && matrix.Width == matrix.Height; // Must be square. + } + + } + + public const int NUM_MASK_PATTERNS = 8; + + private Mode mode; + private ErrorCorrectionLevel ecLevel; + private int version; + private int matrixWidth; + private int maskPattern; + private int numTotalBytes; + private int numDataBytes; + private int numECBytes; + private int numRSBlocks; + private ByteMatrix matrix; + + public QRCode() + { + mode = null; + ecLevel = null; + version = - 1; + matrixWidth = - 1; + maskPattern = - 1; + numTotalBytes = - 1; + numDataBytes = - 1; + numECBytes = - 1; + numRSBlocks = - 1; + matrix = null; + } + + + // Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They + // call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell. + public int at(int x, int y) + { + // The value must be zero or one. + int value_Renamed = matrix.get_Renamed(x, y); + if (!(value_Renamed == 0 || value_Renamed == 1)) + { + // this is really like an assert... not sure what better exception to use? + throw new System.SystemException("Bad value"); + } + return value_Renamed; + } + + // Return debug String. + public override System.String ToString() + { + System.Text.StringBuilder result = new System.Text.StringBuilder(200); + result.Append("<<\n"); + result.Append(" mode: "); + result.Append(mode); + result.Append("\n ecLevel: "); + result.Append(ecLevel); + result.Append("\n version: "); + result.Append(version); + result.Append("\n matrixWidth: "); + result.Append(matrixWidth); + result.Append("\n maskPattern: "); + result.Append(maskPattern); + result.Append("\n numTotalBytes: "); + result.Append(numTotalBytes); + result.Append("\n numDataBytes: "); + result.Append(numDataBytes); + result.Append("\n numECBytes: "); + result.Append(numECBytes); + result.Append("\n numRSBlocks: "); + result.Append(numRSBlocks); + if (matrix == null) + { + result.Append("\n matrix: null\n"); + } + else + { + result.Append("\n matrix:\n"); + result.Append(matrix.ToString()); + } + result.Append(">>\n"); + return result.ToString(); + } + + // Check if "mask_pattern" is valid. + public static bool isValidMaskPattern(int maskPattern) + { + return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS; + } + + // Return true if the all values in the matrix are binary numbers. + // + // JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in + // production. I'm leaving it because it may be useful for testing. It should be removed entirely + // if ByteMatrix is changed never to contain a -1. + /* + private static boolean EverythingIsBinary(final ByteMatrix matrix) { + for (int y = 0; y < matrix.height(); ++y) { + for (int x = 0; x < matrix.width(); ++x) { + int value = matrix.get(y, x); + if (!(value == 0 || value == 1)) { + // Found non zero/one value. + return false; + } + } + } + return true; + } + */ + } +} \ No newline at end of file diff --git a/WindowsPhone/ChildBrowser/ChildBrowserCommand.cs b/WindowsPhone/ChildBrowser/ChildBrowserCommand.cs index 85aeb9a..be2426f 100644 --- a/WindowsPhone/ChildBrowser/ChildBrowserCommand.cs +++ b/WindowsPhone/ChildBrowser/ChildBrowserCommand.cs @@ -10,10 +10,10 @@ using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using System.Diagnostics; -using System.Runtime.Serialization; -using WP7CordovaClassLib.Cordova; -using WP7CordovaClassLib.Cordova.Commands; -using WP7CordovaClassLib.Cordova.JSON; +using System.Runtime.Serialization; +using WP7CordovaClassLib.Cordova; +using WP7CordovaClassLib.Cordova.Commands; +using WP7CordovaClassLib.Cordova.JSON; using Microsoft.Phone.Shell; namespace WP7CordovaClassLib.Cordova.Commands diff --git a/WindowsPhone/LiveTiles/LiveTiles.cs b/WindowsPhone/LiveTiles/LiveTiles.cs index e99ea82..9aaca13 100644 --- a/WindowsPhone/LiveTiles/LiveTiles.cs +++ b/WindowsPhone/LiveTiles/LiveTiles.cs @@ -7,9 +7,9 @@ */ using System.Runtime.Serialization; -using WP7GapClassLib.PhoneGap; -using WP7GapClassLib.PhoneGap.Commands; -using WP7GapClassLib.PhoneGap.JSON; +using WP7CordovaClassLib.Cordova; +using WP7CordovaClassLib.Cordova.Commands; +using WP7CordovaClassLib.Cordova.JSON; using Microsoft.Phone.Shell; using System; using System.Collections.Generic; @@ -17,7 +17,7 @@ using System.Linq; using Microsoft.Phone.Controls; using System.Windows; -namespace PhoneGap.Extension.Commands +namespace Cordova.Extension.Commands { /// /// Implementes access to application live tiles @@ -87,9 +87,9 @@ namespace PhoneGap.Extension.Commands LiveTilesOptions liveTileOptions; try { - liveTileOptions = WP7GapClassLib.PhoneGap.JSON.JsonHelper.Deserialize(options); + liveTileOptions = JsonHelper.Deserialize(options)[0]; } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); return; @@ -110,7 +110,7 @@ namespace PhoneGap.Extension.Commands DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Can't get application tile")); } } - catch(Exception e) + catch(Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error updating application tile")); } @@ -124,9 +124,9 @@ namespace PhoneGap.Extension.Commands LiveTilesOptions liveTileOptions; try { - liveTileOptions = WP7GapClassLib.PhoneGap.JSON.JsonHelper.Deserialize(options); + liveTileOptions = JsonHelper.Deserialize(options)[0]; } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); return; @@ -157,7 +157,7 @@ namespace PhoneGap.Extension.Commands DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,"Tile already exist")); } } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,"Error creating secondary live tile")); } @@ -171,9 +171,9 @@ namespace PhoneGap.Extension.Commands LiveTilesOptions liveTileOptions; try { - liveTileOptions = WP7GapClassLib.PhoneGap.JSON.JsonHelper.Deserialize(options); + liveTileOptions = JsonHelper.Deserialize(options)[0]; } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); return; @@ -200,7 +200,7 @@ namespace PhoneGap.Extension.Commands DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Can't get secondary live tile")); } } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,"Error updating secondary live tile")); } @@ -214,9 +214,9 @@ namespace PhoneGap.Extension.Commands LiveTilesOptions liveTileOptions; try { - liveTileOptions = WP7GapClassLib.PhoneGap.JSON.JsonHelper.Deserialize(options); + liveTileOptions = JsonHelper.Deserialize(options)[0]; } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); return; @@ -238,10 +238,9 @@ namespace PhoneGap.Extension.Commands else { DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Can't get secondary live tile")); - } - + } } - catch (Exception e) + catch (Exception) { DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error deleting secondary live tile")); } diff --git a/WindowsPhone/LiveTiles/README.md b/WindowsPhone/LiveTiles/README.md index b6fe557..3f6bd7b 100644 --- a/WindowsPhone/LiveTiles/README.md +++ b/WindowsPhone/LiveTiles/README.md @@ -9,17 +9,17 @@ liveTilesExample.html- usage example In your head --- -[script type="text/javascript" charset="utf-8" src="liveTiles.js"][/script] + [script type="text/javascript" charset="utf-8" src="liveTiles.js"][/script] Somewhere in your code --- - navigator.plugins.liveTiles.updateAppTile(success, fail,{title: 'title', image:'Images/appbar.next.rest.png', count: 5, backTitle: 'Back title', backContent:'Back side', backImage : 'Images/appbar.close.rest.png'}); + navigator.plugins.liveTiles.updateAppTile(success, fail,{title: 'title', image:'Images/appbar.next.rest.png', count: 5, backTitle: 'Back title', backContent:'Back side', backImage : 'Images/appbar.close.rest.png'}); - navigator.plugins.liveTiles.createSecondaryTile(success, fail, { title: 'title', image: 'Images/appbar.save.rest.png', count: 5, secondaryTileUri: 'www/myPage.html',backTitle:'back'}); + navigator.plugins.liveTiles.createSecondaryTile(success, fail, { title: 'title', image: 'Images/appbar.save.rest.png', count: 5, secondaryTileUri: 'www/myPage.html',backTitle:'back'}); - navigator.plugins.liveTiles.updateSecondaryTile(success, fail, { title: 'title', count: 5, secondaryTileUri: 'www/myPage.html' }); + navigator.plugins.liveTiles.updateSecondaryTile(success, fail, { title: 'title', count: 5, secondaryTileUri: 'www/myPage.html' }); - navigator.plugins.liveTiles.deleteSecondaryTile(success, fail, { secondaryTileUri: 'www/myPage.html' }); \ No newline at end of file + navigator.plugins.liveTiles.deleteSecondaryTile(success, fail, { secondaryTileUri: 'www/myPage.html' }); \ No newline at end of file diff --git a/WindowsPhone/LiveTiles/liveTiles.js b/WindowsPhone/LiveTiles/liveTiles.js index 8d4895e..ca578e8 100644 --- a/WindowsPhone/LiveTiles/liveTiles.js +++ b/WindowsPhone/LiveTiles/liveTiles.js @@ -1,55 +1,43 @@ -PhoneGap.addConstructor(function () { + - navigator.plugins.liveTiles = - { - updateAppTile: function (successCallback, errorCallback, options) { - if (successCallback && (typeof successCallback !== "function")) { - console.log("LiveTiles Error: successCallback is not a function"); - return; - } +(function () { - if (errorCallback && (typeof errorCallback !== "function")) { - console.log("LiveTiles Error: errorCallback is not a function"); - return; - } - PhoneGap.exec(successCallback, errorCallback, "LiveTiles", "updateAppTile", options); - }, - - createSecondaryTile: function (successCallback, errorCallback, options) { - if (successCallback && (typeof successCallback !== "function")) { - console.log("LiveTiles Error: successCallback is not a function"); - return; - } - - if (errorCallback && (typeof errorCallback !== "function")) { - console.log("LiveTiles Error: errorCallback is not a function"); - return; - } - PhoneGap.exec(successCallback, errorCallback, "LiveTiles", "createSecondaryTile", options); - }, - updateSecondaryTile: function (successCallback, errorCallback, options) { - if (successCallback && (typeof successCallback !== "function")) { - console.log("LiveTiles Error: successCallback is not a function"); - return; - } - - if (errorCallback && (typeof errorCallback !== "function")) { - console.log("LiveTiles Error: errorCallback is not a function"); - return; - } - PhoneGap.exec(successCallback, errorCallback, "LiveTiles", "updateSecondaryTile", options); - }, - deleteSecondaryTile: function (successCallback, errorCallback, options) { - if (successCallback && (typeof successCallback !== "function")) { - console.log("LiveTile Error: successCallback is not a function"); - return; - } - - if (errorCallback && (typeof errorCallback !== "function")) { - console.log("LiveTiles Error: errorCallback is not a function"); - return; - } - PhoneGap.exec(successCallback, errorCallback, "LiveTiles", "deleteSecondaryTile", options); + function checkArgs(win,fail) { + if(win && typeof win === "function" && fail && typeof fail === "function") { + return true; + } + else { + console.log("LiveTiles Error: successCallback || errorCallback is not a function"); + return false; } } -}); \ No newline at end of file + + var cdv = window.cordova || window.Cordova; + navigator.plugins.liveTiles = { + updateAppTile: function (successCallback, errorCallback, options) { + if(checkArgs(successCallback, errorCallback)) { + cdv.exec(successCallback, errorCallback, "LiveTiles", "updateAppTile", [options]); + } + }, + + createSecondaryTile: function (successCallback, errorCallback, options) { + if(checkArgs(successCallback, errorCallback)) { + cdv.exec(successCallback, errorCallback, "LiveTiles", "createSecondaryTile", [options]); + } + }, + + updateSecondaryTile: function (successCallback, errorCallback, options) { + if(checkArgs(successCallback, errorCallback)) { + cdv.exec(successCallback, errorCallback, "LiveTiles", "updateSecondaryTile", [options]); + } + }, + + deleteSecondaryTile: function (successCallback, errorCallback, options) { + if(checkArgs(successCallback, errorCallback)) { + cdv.exec(successCallback, errorCallback, "LiveTiles", "deleteSecondaryTile", [options]); + } + } + + }; + +})(); diff --git a/WindowsPhone/LiveTiles/liveTilesExample.html b/WindowsPhone/LiveTiles/liveTilesExample.html index bd2572d..75e90ec 100644 --- a/WindowsPhone/LiveTiles/liveTilesExample.html +++ b/WindowsPhone/LiveTiles/liveTilesExample.html @@ -1,32 +1,18 @@ - + - - PhoneGap + + Live Tiles - - + + + + + + + + This is some text at the bottom of the webview. + + + + + + diff --git a/iOS/AdPlugin/www/js/index.js b/iOS/AdPlugin/www/js/index.js new file mode 100644 index 0000000..3f62386 --- /dev/null +++ b/iOS/AdPlugin/www/js/index.js @@ -0,0 +1,36 @@ +var app = { + initialize: function() { + this.bind(); + }, + bind: function() { + document.addEventListener('deviceready', this.deviceready, false); + }, + deviceready: function() { + // note that this is an event handler so the scope is that of the event + // so we need to call app.report(), and not this.report() + app.report('deviceready'); + + // listen for orientation changes + window.addEventListener("orientationchange", window.plugins.iAdPlugin.orientationChanged, false); + // listen for the "iAdBannerViewDidLoadAdEvent" that is sent by the iAdPlugin + document.addEventListener("iAdBannerViewDidLoadAdEvent", iAdBannerViewDidLoadAdEventHandler, false); + // listen for the "iAdBannerViewDidFailToReceiveAdWithErrorEvent" that is sent by the iAdPlugin + document.addEventListener("iAdBannerViewDidFailToReceiveAdWithErrorEvent", iAdBannerViewDidFailToReceiveAdWithErrorEventHandler, false); + + var adAtBottom = false; + setTimeout(function() { + window.plugins.iAdPlugin.prepare(adAtBottom); // by default, ad is at Top + }, 1000); + window.plugins.iAdPlugin.orientationChanged(true);//trigger immediately so iAd knows its orientation on first load + + + + }, + report: function(id) { + console.log("report:" + id); + // hide the .pending

and show the .complete

+ document.querySelector('#' + id + ' .pending').className += ' hide'; + var completeElem = document.querySelector('#' + id + ' .complete'); + completeElem.className = completeElem.className.split('hide').join(''); + } +}; diff --git a/iOS/AdPlugin/www/res/icon/cordova_128.png b/iOS/AdPlugin/www/res/icon/cordova_128.png new file mode 100644 index 0000000..3516df3 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_128.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_16.png b/iOS/AdPlugin/www/res/icon/cordova_16.png new file mode 100644 index 0000000..54e19c5 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_16.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_24.png b/iOS/AdPlugin/www/res/icon/cordova_24.png new file mode 100644 index 0000000..c7d43ad Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_24.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_256.png b/iOS/AdPlugin/www/res/icon/cordova_256.png new file mode 100644 index 0000000..e1cd0e6 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_256.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_32.png b/iOS/AdPlugin/www/res/icon/cordova_32.png new file mode 100644 index 0000000..734fffc Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_32.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_48.png b/iOS/AdPlugin/www/res/icon/cordova_48.png new file mode 100644 index 0000000..8ad8bac Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_48.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_512.png b/iOS/AdPlugin/www/res/icon/cordova_512.png new file mode 100644 index 0000000..c9465f3 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_512.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_64.png b/iOS/AdPlugin/www/res/icon/cordova_64.png new file mode 100644 index 0000000..03b3849 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_64.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_android_36.png b/iOS/AdPlugin/www/res/icon/cordova_android_36.png new file mode 100644 index 0000000..cd5032a Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_android_36.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_android_48.png b/iOS/AdPlugin/www/res/icon/cordova_android_48.png new file mode 100644 index 0000000..e79c606 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_android_48.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_android_72.png b/iOS/AdPlugin/www/res/icon/cordova_android_72.png new file mode 100644 index 0000000..4d27634 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_android_72.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_android_96.png b/iOS/AdPlugin/www/res/icon/cordova_android_96.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_android_96.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_bb_80.png b/iOS/AdPlugin/www/res/icon/cordova_bb_80.png new file mode 100644 index 0000000..f86a27a Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_bb_80.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_ios_114.png b/iOS/AdPlugin/www/res/icon/cordova_ios_114.png new file mode 100644 index 0000000..efd9c37 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_ios_114.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_ios_144.png b/iOS/AdPlugin/www/res/icon/cordova_ios_144.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_ios_144.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_ios_57.png b/iOS/AdPlugin/www/res/icon/cordova_ios_57.png new file mode 100644 index 0000000..c795fc4 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_ios_57.png differ diff --git a/iOS/AdPlugin/www/res/icon/cordova_ios_72.png b/iOS/AdPlugin/www/res/icon/cordova_ios_72.png new file mode 100644 index 0000000..b1cfde7 Binary files /dev/null and b/iOS/AdPlugin/www/res/icon/cordova_ios_72.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_hdpi_landscape.png b/iOS/AdPlugin/www/res/screen/android_hdpi_landscape.png new file mode 100644 index 0000000..a61e2b1 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_hdpi_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_hdpi_portrait.png b/iOS/AdPlugin/www/res/screen/android_hdpi_portrait.png new file mode 100644 index 0000000..5d6a28a Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_hdpi_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_ldpi_landscape.png b/iOS/AdPlugin/www/res/screen/android_ldpi_landscape.png new file mode 100644 index 0000000..f3934cd Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_ldpi_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_ldpi_portrait.png b/iOS/AdPlugin/www/res/screen/android_ldpi_portrait.png new file mode 100644 index 0000000..65ad163 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_ldpi_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_mdpi_landscape.png b/iOS/AdPlugin/www/res/screen/android_mdpi_landscape.png new file mode 100644 index 0000000..a1b697c Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_mdpi_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_mdpi_portrait.png b/iOS/AdPlugin/www/res/screen/android_mdpi_portrait.png new file mode 100644 index 0000000..ea15693 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_mdpi_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_xhdpi_landscape.png b/iOS/AdPlugin/www/res/screen/android_xhdpi_landscape.png new file mode 100644 index 0000000..79f2f09 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_xhdpi_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/android_xhdpi_portrait.png b/iOS/AdPlugin/www/res/screen/android_xhdpi_portrait.png new file mode 100644 index 0000000..c2e8042 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/android_xhdpi_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/blackberry_transparent_300.png b/iOS/AdPlugin/www/res/screen/blackberry_transparent_300.png new file mode 100644 index 0000000..b548bdc Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/blackberry_transparent_300.png differ diff --git a/iOS/AdPlugin/www/res/screen/blackberry_transparent_400.png b/iOS/AdPlugin/www/res/screen/blackberry_transparent_400.png new file mode 100644 index 0000000..3facdf9 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/blackberry_transparent_400.png differ diff --git a/iOS/AdPlugin/www/res/screen/ipad_landscape.png b/iOS/AdPlugin/www/res/screen/ipad_landscape.png new file mode 100644 index 0000000..04be5ac Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/ipad_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/ipad_portrait.png b/iOS/AdPlugin/www/res/screen/ipad_portrait.png new file mode 100644 index 0000000..41e839d Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/ipad_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/ipad_retina_landscape.png b/iOS/AdPlugin/www/res/screen/ipad_retina_landscape.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/ipad_retina_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/ipad_retina_portrait.png b/iOS/AdPlugin/www/res/screen/ipad_retina_portrait.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/ipad_retina_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/iphone_landscape.png b/iOS/AdPlugin/www/res/screen/iphone_landscape.png new file mode 100644 index 0000000..d154883 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/iphone_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/iphone_portrait.png b/iOS/AdPlugin/www/res/screen/iphone_portrait.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/iphone_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/iphone_retina_landscape.png b/iOS/AdPlugin/www/res/screen/iphone_retina_landscape.png new file mode 100644 index 0000000..0165669 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/iphone_retina_landscape.png differ diff --git a/iOS/AdPlugin/www/res/screen/iphone_retina_portrait.png b/iOS/AdPlugin/www/res/screen/iphone_retina_portrait.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/iphone_retina_portrait.png differ diff --git a/iOS/AdPlugin/www/res/screen/windows_phone_portrait.jpg b/iOS/AdPlugin/www/res/screen/windows_phone_portrait.jpg new file mode 100644 index 0000000..9f95387 Binary files /dev/null and b/iOS/AdPlugin/www/res/screen/windows_phone_portrait.jpg differ diff --git a/iOS/AdPlugin/www/spec.html b/iOS/AdPlugin/www/spec.html new file mode 100644 index 0000000..be4103f --- /dev/null +++ b/iOS/AdPlugin/www/spec.html @@ -0,0 +1,50 @@ + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + +

+ + diff --git a/iOS/AdPlugin/www/spec/helper.js b/iOS/AdPlugin/www/spec/helper.js new file mode 100644 index 0000000..e3a5e03 --- /dev/null +++ b/iOS/AdPlugin/www/spec/helper.js @@ -0,0 +1,11 @@ +afterEach(function() { + document.getElementById('stage').innerHTML = ''; +}); + +var helper = { + trigger: function(obj, name) { + var e = document.createEvent('Event'); + e.initEvent(name, true, true); + obj.dispatchEvent(e); + } +}; diff --git a/iOS/AdPlugin/www/spec/index.js b/iOS/AdPlugin/www/spec/index.js new file mode 100644 index 0000000..0716d1b --- /dev/null +++ b/iOS/AdPlugin/www/spec/index.js @@ -0,0 +1,49 @@ +describe('app', function() { + describe('initialize', function() { + it('should bind deviceready', function() { + runs(function() { + spyOn(app, 'deviceready'); + app.initialize(); + helper.trigger(window.document, 'deviceready'); + }); + + waitsFor(function() { + return (app.deviceready.calls.length > 0); + }, 'deviceready should be called once', 500); + + runs(function() { + expect(app.deviceready).toHaveBeenCalled(); + }); + }); + }); + + describe('deviceready', function() { + it('should report that it fired', function() { + spyOn(app, 'report'); + app.deviceready(); + expect(app.report).toHaveBeenCalledWith('deviceready'); + }); + }); + + describe('report', function() { + beforeEach(function() { + var el = document.getElementById('stage'); + el.innerHTML = ['
', + '

Pending

', + '

Complete

', + '
'].join('\n'); + }); + + it('should show the completion state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .complete:not(.hide)'); + expect(el).toBeTruthy(); + }); + + it('should hide the pending state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .pending.hide'); + expect(el).toBeTruthy(); + }); + }); +}); diff --git a/iOS/ApplicationPreferences/README.md b/iOS/ApplicationPreferences/README.md index 9b425ad..c149ad1 100755 --- a/iOS/ApplicationPreferences/README.md +++ b/iOS/ApplicationPreferences/README.md @@ -28,9 +28,11 @@ A full set example could be: } ); -## Registering the plugin ## +NOTE: The preference must exist in a settings bundle and Root.plist in your project. See http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Conceptual/UserDefaults/Preferences/Preferences.html for further details. -1. Opne Cordovo.plist file in Xcode +## Registering the plugin ## + +1. Open Cordovo.plist file in Xcode 2. To the plugins Dictionary add a new String item 3. Name and value of the item would be "applicationPreferences" diff --git a/iOS/ApplicationPreferences/applicationPreferences.js b/iOS/ApplicationPreferences/applicationPreferences.js index 19396e6..e7946d9 100755 --- a/iOS/ApplicationPreferences/applicationPreferences.js +++ b/iOS/ApplicationPreferences/applicationPreferences.js @@ -1,11 +1,12 @@ -function applicationPreferences() { -} +(function() { + +function applicationPreferences() {} applicationPreferences.prototype.get = function(key,success,fail) { var args = {}; args.key = key; - Cordova.exec(success,fail,"applicationPreferences","getSetting",[args]); + cordova.exec(success,fail,"applicationPreferences","getSetting",[args]); }; applicationPreferences.prototype.set = function(key,value,success,fail) @@ -13,14 +14,16 @@ applicationPreferences.prototype.set = function(key,value,success,fail) var args = {}; args.key = key; args.value = value; - Cordova.exec(success,fail,"applicationPreferences","setSetting",[args]); + cordova.exec(success,fail,"applicationPreferences","setSetting",[args]); }; -Cordova.addConstructor(function() -{ - if(!window.plugins) - { - window.plugins = {}; - } + +if(!window.plugins) { + window.plugins = {}; +} +if ( ! window.plugins.applicationPreferences ) { window.plugins.applicationPreferences = new applicationPreferences(); -}); \ No newline at end of file +} + +})(); + diff --git a/iOS/AugmentedReality-Wikitude/Documentation/Documentation.md b/iOS/AugmentedReality-Wikitude/Documentation/Documentation.md new file mode 100644 index 0000000..6e4b202 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/Documentation/Documentation.md @@ -0,0 +1,116 @@ +## Wikitude PhoneGap Plugin Documentation +by ``` Wikitude GmbH ``` + + +### DESCRIPTION +*** + +This document describes all available PhoneGap bindings which exists in the Wikitude PhoneGap Plugin. + + +#### Getting information about the device + +###### isDeviceSupported +Call ``` isDeviceSupported ``` to determinate if the current device is capable of launching ARchitect Worlds. + + @param onSuccessCallback - A callback which gets called if the current device supports launching ARchitect Worlds + @param onErrorCallback - An callback which gets called if the current device does not fulfil all needs to lauch ARchitect Worlds + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - No options + + cordova.exec(deviceSupportedCallback, deviceNotSupportedCallback, "WikitudePlugin", "isDeviceSupported", [""]); + + +#### Managing the ARchitectView + + +###### open +Call open to add the ARchitectView to your applications view hierarchy and loads the specified ARchitect World. + + @param onSuccessCallback - A success callback + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - a dictionary containing two pairs: + @pair apiKey : The Wikitude SDK Key provided to you after you purchased the Wikitude SDK or an empty string if you're using a trial version + @pair filePath : A filepath to a local bundle resource or to a file on e.g. your dropbox + + cordova.exec(architectWorldLaunchedCallback, architectWorldFailedLaunchingCallback, "WikitudePlugin", "open", [{ apiKey: myApiKey, filePath: worldPath}]); + +###### close +This call will remove the ARchitectView from the view hierarchy and also stops all ongoing Wikitude updates. + + @param onSuccessCallback - A success callback + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - No options + + cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "close", [""]); + +###### show +This call hides the ARchitectView, but keeps it alive in memory. + + @param onSuccessCallback - A success callback + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - No options + + cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "hide", [""]); + +###### hide +This call will show the ARchitectView again if it was hidden with the ``` hide ``` call. + + @param onSuccessCallback - A success callback + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - No options + + cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "show", [""]); + + + +#### Interacting with the ARchitectView + +###### callJavaScript +This function will evaluate the passed JavaScript in the current ARchitect World context. + + @param onSuccessCallback - A success callback + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - A JavaScript array with a JavaScript string at first position + + cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "callJavascript", [js]); + +###### onUrlInvoke +This function will set an callback which gets called if you call ``` document.location = architectsdk://aRequestWithParameters?id=5&name=Poi5 ```. + + @param onSuccessCallback - A callback which gets called every time you call ` architectsdk:// ` + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - No options + + cordova.exec(onUrlInvokeCallback, onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]); + +###### setLocation +This function allows you to set a location for your current ARchitectView + + + @param onSuccessCallback - A success callback + @param onErrorCallback - An error callback + @param Plugin name - The name of the plugin you want to call a function + @param Plugin function - The name of the function you want to call on the plugin + @param options - A dictionary containing the new latitude, longitude, altitude, accuracy + @pair lat : The new latitude + @pair lon : The new longitude + @pair alt : The new altitude + @pair acc : The accuracy of the new locations + + cordova.exec(onUrlInvokeCallback, onWikitudeError, "WikitudePlugin", "setLocation", [""]); + + diff --git a/iOS/AugmentedReality-Wikitude/Plugin/README.md b/iOS/AugmentedReality-Wikitude/Plugin/README.md new file mode 100644 index 0000000..161b7c8 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/Plugin/README.md @@ -0,0 +1,111 @@ +# Augmented Reality - Wikitude SDK Plugin +by Wikitude GmbH - [www.wikitude.com](http://www.wikitude.com) + +Interested in advanced usage of the plugin and need assistance? +[Register as a Wikitude developer](http://developer.wikitude.com) and get support in our forum. + +For further questions visit us on www.wikitude.com or contact us via `phonegap wikitude.com` + +#### Important: This plugin runs on latest [PhoneGap 2.x](http://docs.phonegap.com/en/2.0.0/guide_getting-started_android_index.md.html#Getting%20Started%20with%20Android) only + + +## DESCRIPTION + + +The Wikitude PhoneGap Plugin enables web developers to embed an Augmented Reality view into their PhoneGap project. One can create a fully featured app with advanced Augmented Reality features, including Image Recognition, purely using HTML, CSS and Javascript. + +###The Wikitude PhoneGap Plugin + +* Available for iOS and Android +* Simple and seamless native PhoneGap integration +* Fully customizable Augmented Realty view +* Includes the full feature set of the Wikitude SDK +* AR content is purely written in HTML and JavaScript + +![image](http://www.wikitude.com/wp-content/uploads/2012/09/Plugin_Phonegap.png) + +###The Augmented Reality View +From a technical point of view the SDK adds a UI component, similar to a web view. In contrast to a standard web view this AR view can render Augmented Reality content. + +Note: Content developed for this AR View is written in JavaScript and HTML. The .html and .js files for this view are different from the PhoneGap .js and .html files. The AR engine working in the background is called ARchitect Engine and is powering the SDK. + +###Further developer resources +* [Full documentation and additional tutorials](http://forum.wikitude.com/documentation) +* [Developer Forum](http://forum.wikitude.com/home) +* [Wikitude SDK Download](http://forum.wikitude.com/download) +* [Google+ Page for News](https://plus.google.com/u/0/103004921345651739447/posts) +* [Developer Newsletter](http://www.wikitude.com/developer/newsletter) + + + +## SETUP + +* Create a new PhoneGap project with the command line tool provided by PhoneGap. +* After your created the project, switch to Finder and copy the WikitudePlugin.h and .m file into the Plugins folder of your PhoneGap project. Afterwards, you have to add the files to the Plugins Group in your Xcode project. +* Add a new entry with key ``` WikitudePlugin ``` and value ``` WTWikitudePlugin ``` to ``` Plugins ``` in ``` Cordova.plist ``` +* Copy the Wikitude SDK for iOS (which you can downloaded from [our website](http://www.wikitude.com/developer/sdk)) into a folder like ```[ProjectFolder/"AppName"/WikitudeSDK] ```in your Xcode project. Make sure that you not only copy the files into the correct destination but that you also add the files to the Xcode project. + + * if you want to use Image Recognition within your App, you need to copy the Vuforia SDK folder into a folder like ```[ProjectFolder/"AppName"/Vuforia]```. Also dont forget to add the files to your Xcode project. You can download the Vuforia SDK from [Qualcomm](https://ar.qualcomm.at/sdk/ios). + + +* Add the Following Frameworks to your project: + + * CoreMotion.framework + + * CoreVideo.framework + + * libz.dylib + + * libsqlite3.dylib + + * OpenGLES.framework + + * Security.framework + +* You need to change one linker flag in order to run your App with the WikitudeSDK. Go to your project settings and change the other linker flag ```'-all_load'``` to ```'-force_load $(BUILT_PRODUCTS_DIR)/libCordova.a'```. After that, please add the following linker flag: ```-lstdc++```. + +* If you want to test your application on the iPhone Simulator, you'll need to change an additional Library Serach Path in your Xcode project settings. + + Change ```"$(SRCROOT)/HelloWorld/WikitudeSDK/SDK/lib/Release-iphoneos" ``` + To ```"$(SRCROOT)/HelloWorld/WikitudeSDK/SDK/lib/Release$(EFFECTIVE_PLATFORM_NAME)"``` + + And delete ```"$(SRCROOT)/HelloWorld/WikitudeSDK/SDK/lib/Release-iphonesimulator"``` + + +* The last step is to edit the whitelist entries. Open the Cordova.plist and add a new entry to the 'ExternalHosts' Array: * (single Asterisk) + + +## JAVASCRIPT INTERFACE + +Its simple to use the Wikitude Plugin within your PhoneGap application. + +We wrapped all ``` cordova.exec ``` calls into a separate JavaScript wrapper which handles location updates and some more functionality behind the scenes. + +You will mainly work with the ``` WikitudePlugin ``` where all you have to do is to call ```Wikitude.isDeviceReady(successCallback, errorCallback)``` and in your successCallback, you can call ```WikitudePlugin.loadARchitectWorld(successCallback, errorCallback, "path/to/your/world")```. + +If you want to communicate directly via the ```cordova.exec``` JavaScript calls, have a look at the ```Documentation``` folder which includes more information about that. + + +## Getting STARTED + +To get started, we prepared two sample projects. You can read through this samples to understand how to use the JavaScript wrapper and how you add your ARchitect World into the project. There is also a ReadMe for both projects which explain you what you have to do to get them running. + +Note: +If you have purchased a Wikitude SDK license, you can enter you SDK Key in the ```WikitudePlugin.js``` file at line 9. + + +## LICENSE + + Copyright 2012 [Wikitude GmbH ](http://www.wikitude.com) + + 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. \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/Plugin/WTWikitudePlugin.h b/iOS/AugmentedReality-Wikitude/Plugin/WTWikitudePlugin.h new file mode 100644 index 0000000..0a71316 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/Plugin/WTWikitudePlugin.h @@ -0,0 +1,14 @@ +// +// WTWikitudeSDK.h +// HelloWorld +// +// Created by Andreas Schacherbauer on 8/24/12. +// +// + + +#import + +@interface WTWikitudePlugin : CDVPlugin + +@end diff --git a/iOS/AugmentedReality-Wikitude/Plugin/WTWikitudePlugin.m b/iOS/AugmentedReality-Wikitude/Plugin/WTWikitudePlugin.m new file mode 100644 index 0000000..3e54f35 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/Plugin/WTWikitudePlugin.m @@ -0,0 +1,402 @@ +// +// WTWikitudeSDK.m +// HelloWorld +// +// Created by Andreas Schacherbauer on 8/24/12. +// +// + +#import "WTWikitudePlugin.h" + +// Wikitude SDK +#import "WTArchitectView.h" + + + + +@interface WTWikitudePlugin () + +@property (nonatomic, strong) WTArchitectView *architectView; + +@property (nonatomic, strong) NSString *currentARchitectViewCallbackID; +@property (nonatomic, strong) NSString *currentPlugInErrorCallback; + +@property (nonatomic, assign) BOOL isUsingInjectedLocation; + +@end + + +@implementation WTWikitudePlugin +@synthesize architectView=_architectView; + + +#pragma mark - View Lifecycle +/* View Lifecycle */ +- (void)isDeviceSupported:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + BOOL isDeviceSupported = [WTArchitectView isDeviceSupported]; + + if (isDeviceSupported) { + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:isDeviceSupported]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + } else { + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:isDeviceSupported]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +- (void)open:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + + BOOL success = [WTArchitectView isDeviceSupported]; + if ( success ) { + + NSString *sdkKey = [options objectForKey:@"apiKey"]; + NSString *architectWorldFilePath = [options objectForKey:@"filePath"]; + + // First, lets check if we need to init a new sdk view + if ( !_architectView ) { + self.architectView = [[WTArchitectView alloc] initWithFrame:self.viewController.view.bounds]; + self.architectView.delegate = self; + [self.architectView initializeWithKey:sdkKey motionManager:nil]; + } + + // then add the view in its own navController to the ui. we need a own navController to have a backButton which can be used to dismiss the view + UIViewController *viewController = [[UIViewController alloc] init]; + viewController.view = self.architectView; + + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController]; + navController.navigationBar.tintColor = [UIColor blackColor]; + navController.navigationBar.topItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissARchitectView)]; + [self.viewController presentViewController:navController animated:NO completion:^{ + // completion code + }]; + + + + // and finaly load the architect world, specified in the open function in js + if (architectWorldFilePath) { + + NSString *worldName = [architectWorldFilePath lastPathComponent]; + worldName = [worldName stringByDeletingPathExtension]; + NSString *worldNameExtension = [architectWorldFilePath pathExtension]; + + NSString *architectWorldDirectoryPath = [architectWorldFilePath stringByDeletingLastPathComponent]; + + NSString *loadablePath = [[NSBundle mainBundle] pathForResource:worldName ofType:worldNameExtension inDirectory:architectWorldDirectoryPath]; + [self.architectView loadArchitectWorldFromUrl:loadablePath]; + } + } + + // start the sdk view updates + [self.architectView start]; + + + if ( success ) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)close:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + + [self.architectView stop]; +// [self.architectView removeFromSuperview]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)show:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + self.architectView.hidden = NO; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)hide:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + self.architectView.hidden = YES; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)onResume:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + [self.architectView start]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)onPause:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + [self.architectView stop]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +#pragma mark - Location Handling + +- (void)setLocation:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + + float latitude = [[options objectForKey:@"lat"] floatValue]; + float longitude = [[options objectForKey:@"lon"] floatValue]; + float altitude = [[options objectForKey:@"alt"] floatValue]; + float accuracy = [[options objectForKey:@"acc"] floatValue]; + + if (!self.isUsingInjectedLocation) { + [self.architectView setUseInjectedLocation:YES]; + self.isUsingInjectedLocation = YES; + } + + [self.architectView injectLocationWithLatitude:latitude longitude:longitude altitude:altitude accuracy:accuracy]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +#pragma mark - Javascript + +- (void)callJavascript:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + + @try { + + if (arguments.count >= 1) { + + NSMutableString *javascriptToCall = [[arguments objectAtIndex:1] mutableCopy]; + for (NSUInteger i = 2; i < arguments.count; i++) { + [javascriptToCall appendString:[arguments objectAtIndex:i]]; + } + + if (self.architectView) { + [self.architectView callJavaScript:javascriptToCall]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + }else + { + // return error no javascript to call found + } + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +- (void)onUrlInvoke:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + + + self.currentARchitectViewCallbackID = callbackId; + self.currentPlugInErrorCallback = callbackId; + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT]; + [pluginResult setKeepCallbackAsBool:YES]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +#pragma mark - WTArchitectView Delegate +- (void)urlWasInvoked:(NSString *)url +{ + + CDVPluginResult *pluginResult = nil; + NSString *javaScriptResult = nil; + + + if (url && self.currentARchitectViewCallbackID) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:url]; + [pluginResult setKeepCallbackAsBool:YES]; + javaScriptResult = [pluginResult toSuccessCallbackString:self.currentARchitectViewCallbackID]; + + + }else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + javaScriptResult = [pluginResult toSuccessCallbackString:self.currentPlugInErrorCallback]; + } + + [self writeJavascript:javaScriptResult]; +} + +- (void)dismissARchitectView +{ + [self.viewController dismissModalViewControllerAnimated:NO]; + [self.architectView stop]; +} + +@end diff --git a/iOS/AugmentedReality-Wikitude/Plugin/WikitudePlugin.js b/iOS/AugmentedReality-Wikitude/Plugin/WikitudePlugin.js new file mode 100644 index 0000000..bd09eb2 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/Plugin/WikitudePlugin.js @@ -0,0 +1,383 @@ +var WikitudePlugin = { + + /** + * + * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk + * If you're having a trial version, leave this string empty + * + */ + mySDKKey : "ENTER-YOUR-KEY-HERE", + + /** true if architectview is open */ + isOpened : false, + + /** + * + * called when user pressed back-button on Android device + * + */ + backButtonCallback : null, + + /** + * + * Change the value of this variable to modify the location update rate + * + */ + locationUpdateRate : 3000, + + /** + * + * This variable represents if the current device is capable of running the Wikitude SDK + * + */ + isDeviceSupported : false, + + /** + * + * This watchID is used to shedule location updates + * + */ + watchID : null, + + /** + * + * Callbacks to get device information if ARchitect Worlds can be launched + * + */ + onDeviceSupportedCallback : null, + onDeviceNotSupportedCallback : null, + + /** + * + * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch + * + */ + onARchitectWorldLaunchedCallback : null, + onARchitectWorldFailedLaunchingCallback : null, + + /** + * + * This function gets called if PhoneGap reports that it has finished loading successfully. + * + */ + isDeviceSupported: function(successCallback, errorCallback) + { + + WikitudePlugin.onDeviceSupportedCallback = successCallback; + WikitudePlugin.onDeviceNotSupportedCallback = errorCallback; + + + // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin + cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]); + + }, + + /** + * + * Declare what should happen when user pressed back-button while architect-view is opened. + * If not declared/called or null: ARchitect-View is closed + * + */ + onPressedBackButton : function(callback) + { + WikitudePlugin.backButtonCallback = callback; + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK + * + */ + deviceIsARchitectReady : function() + { + // We keep track of the device status + WikitudePlugin.isDeviceSupported = true; + + + if(WikitudePlugin.onDeviceSupportedCallback) + { + WikitudePlugin.onDeviceSupportedCallback(); + } + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK. + * + */ + deviceIsNotARchitectReady : function() + { + WikitudePlugin.isDeviceSupported = false; + + // In this case we notify the user that his device is not supported by the Wikitude SDK + if(WikitudePlugin.onDeviceNotSupportedCallback) + { + WikitudePlugin.onDeviceNotSupportedCallback(); + } + }, + + /* + * ============================================================================================================================= + * + * PUBLIC API + * + * ============================================================================================================================= + */ + + /* Managing ARchitect world loading */ + + /** + * + * Call this function if you want to load an ARchitect World + * + * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox + * + */ + loadARchitectWorld : function(worldPath) + { + + // before we actually call load, we check again if the device is able to open the world + if(WikitudePlugin.isDeviceSupported) + { + + // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys: + // @param {Object} options (required) + // @param {String} options.sdkKey License key for the Wikitude SDK + // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox + + cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]); + + + // We add an event listener on the resume and pause event of the application lifecycle + document.addEventListener("resume", WikitudePlugin.onResume, false); + document.addEventListener("pause", WikitudePlugin.onPause, false); + + WikitudePlugin.isOpened = true; + document.addEventListener("backbutton", WikitudePlugin.onBackButton, false); + + // After we started loading the world, we start location updates + WikitudePlugin.startLocationUpdates(); + + }else + { + // if the device is not able to start the Wikitude SDK, we notify the user again + WikitudePlugin.deviceNotARchitectReady(); + } + }, + + /* Managing the Wikitude SDK Lifecycle */ + + /** + * + * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen + * + */ + close : function() + { + document.removeEventListener("pause", WikitudePlugin.onPause, false); + document.removeEventListener("resume", WikitudePlugin.onResume, false); + + WikitudePlugin.stopLocationUpdates(); + + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]); + WikitudePlugin.isOpened = false; + }, + + /** + * + * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active + * + */ + hide : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]); + }, + + /** + * + * Use this function to show the Wikitude SDK if it was hidden before + * + */ + show : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]); + }, + + /* Interacting with the Wikitude SDK */ + + /** + * + * Use this function to call javascript which will be executed in the context of your ARchitect World + * + * + * @param js The JavaScript that gets evaluated in context of the ARchitect World + * + */ + callJavaScript : function(js) + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]); + }, + + /** + * + * Use this function to set a callback which will be invoked when the ARchitect World calls for example + * document.location = "architectsdk://opendetailpage?id=9"; + * + * + * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://" + */ + setOnUrlInvokeCallback : function(onUrlInvokeCallback) + { + cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]); + }, + + + /* + * ============================================================================================================================= + * + * Callbacks of public functions + * + * ============================================================================================================================= + */ + + /** + * + * Use this callback to get notified if the world loaded successfully + * + */ + worldLaunched : function() + { + if(WikitudePlugin.onARchitectWorldLaunchedCallback) + { + WikitudePlugin.onARchitectWorldLaunchedCallback(); + } + }, + + /** + * + * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World + * + */ + worldFailedLaunching : function(err) + { + if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback) + { + WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err); + } + }, + + /* Lifecycle updates */ + + /** + * + * This function actually starts the PhoneGap location updates + * + */ + startLocationUpdates : function() + { + + WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate }); + }, + + /** + * + * This callback gets called everytime the location did update + * + */ + onReceivedLocation : function(position) + { + + // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]); + }, + + /** + * + * Use this function to stop location updates + * + */ + stopLocationUpdates : function() + { + + // We clear the location update watch which was responsible for updating the location in a specific time interval + navigator.geolocation.clearWatch(WikitudePlugin.watchID); + WikitudePlugin.watchID = null; + }, + + /** + * + * This function gets called every time the application did become active. + * + */ + onResume : function() + { + + // Call the Wikitude SDK that the application did become active again + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]); + + // And start continuing updating the user location + WikitudePlugin.startLocationUpdates(); + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onPause : function() + { + + // Call the Wikitude SDK that the application did resign active + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]); + + // And stop all ongoing location updates + WikitudePlugin.stopLocationUpdates(); + }, + + /** + * + * Android specific! + * This function gets called if the user presses the back button. + * You may define your own implementation by using WikitudePlugin.onPressedBackButton(method) + * + */ + onBackButton : function() + { + if (WikitudePlugin.isOpened) { + if (WikitudePlugin.backButtonCallback==null) { + WikitudePlugin.close(); + document.removeEventListener("backbutton", WikitudePlugin.onBackButton, false); + } else { + WikitudePlugin.backButtonCallback(); + } + } + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeOK : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeError : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + report: function(id) + { + console.log("app report: " + id); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/TemplateIcon.icns b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/TemplateIcon.icns new file mode 100644 index 0000000..47f85e5 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/TemplateIcon.icns differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/TemplateInfo.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/TemplateInfo.plist new file mode 100644 index 0000000..6e6b271 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/TemplateInfo.plist @@ -0,0 +1,28 @@ + + + + + + Description + This template provides a starting point for a Cordova based application. Just modify the www folder contents with your HTML, CSS and Javascript. + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.pbxproj b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a0fab64 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.pbxproj @@ -0,0 +1,732 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; }; + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 1F766FE113BBADB100FB74C0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1F766FDC13BBADB100FB74C0 /* Localizable.strings */; }; + 1F766FE213BBADB100FB74C0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1F766FDF13BBADB100FB74C0 /* Localizable.strings */; }; + 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; }; + 301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; }; + 301BF570109A69640062928A /* www in Resources */ = {isa = PBXBuildFile; fileRef = 301BF56E109A69640062928A /* www */; }; + 301BF5B5109A6A2B0062928A /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B4109A6A2B0062928A /* AddressBook.framework */; }; + 301BF5B7109A6A2B0062928A /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */; }; + 301BF5B9109A6A2B0062928A /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */; }; + 301BF5BB109A6A2B0062928A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BA109A6A2B0062928A /* AVFoundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 301BF5BD109A6A2B0062928A /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BC109A6A2B0062928A /* CFNetwork.framework */; }; + 301BF5BF109A6A2B0062928A /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BE109A6A2B0062928A /* CoreLocation.framework */; }; + 301BF5C1109A6A2B0062928A /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */; }; + 301BF5C3109A6A2B0062928A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C2109A6A2B0062928A /* QuartzCore.framework */; }; + 301BF5C5109A6A2B0062928A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */; }; + 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; }; + 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; }; + 3053AC6F109B7857006FCFE7 /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 3053AC6E109B7857006FCFE7 /* VERSION */; }; + 305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */; }; + 3072F99713A8081B00425683 /* Capture.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3072F99613A8081B00425683 /* Capture.bundle */; }; + 3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */; }; + 3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */; }; + 3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */; }; + 3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */; }; + 3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */; }; + 3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBC154F3926009F9C59 /* Default~iphone.png */; }; + 308D05371370CCF300D202BF /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052E1370CCF300D202BF /* icon-72.png */; }; + 308D05381370CCF300D202BF /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052F1370CCF300D202BF /* icon.png */; }; + 308D05391370CCF300D202BF /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05301370CCF300D202BF /* icon@2x.png */; }; + 30A0434814DC770100060A13 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30A0434314DC770100060A13 /* Localizable.strings */; }; + 30A0434914DC770100060A13 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30A0434614DC770100060A13 /* Localizable.strings */; }; + 30E1352710E2C1420031B30D /* Cordova.plist in Resources */ = {isa = PBXBuildFile; fileRef = 30E1352610E2C1420031B30D /* Cordova.plist */; }; + 30E5649213A7FCAF007403D8 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30E5649113A7FCAF007403D8 /* CoreMedia.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + A93EDE2C16022ECD00FF615F /* assets in Resources */ = {isa = PBXBuildFile; fileRef = A93EDE2B16022ECD00FF615F /* assets */; }; + A93EDE301602307200FF615F /* WTWikitudePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A93EDE2F1602307200FF615F /* WTWikitudePlugin.m */; }; + A93EDE501602315500FF615F /* libWikitudeSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE4C1602315500FF615F /* libWikitudeSDK.a */; }; + A93EDE511602315600FF615F /* libWikitudeSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE4E1602315500FF615F /* libWikitudeSDK.a */; }; + A93EDE7D1602348700FF615F /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE771602348700FF615F /* CoreMotion.framework */; }; + A93EDE7E1602348700FF615F /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE781602348700FF615F /* CoreVideo.framework */; }; + A93EDE7F1602348700FF615F /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE791602348700FF615F /* libsqlite3.dylib */; }; + A93EDE801602348700FF615F /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE7A1602348700FF615F /* libz.dylib */; }; + A93EDE811602348700FF615F /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE7B1602348700FF615F /* OpenGLES.framework */; }; + A93EDE821602348700FF615F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93EDE7C1602348700FF615F /* Security.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 301BF534109A57CC0062928A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D2AAC07E0554694100DB518D; + remoteInfo = CordovaLib; + }; + 301BF550109A68C00062928A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D2AAC07D0554694100DB518D; + remoteInfo = CordovaLib; + }; + 302D95EB14D23909003F00A1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 686357A9141002F100DF4CF2; + remoteInfo = CordovaLibTests; + }; + 3088BBB4154F38EF009F9C59 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 303A4068152124BB00182201; + remoteInfo = CordovaLibApp; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 1D6058910D05DD3D006BFB54 /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 1F766FDD13BBADB100FB74C0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = ""; }; + 1F766FE013BBADB100FB74C0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Localizable.strings; sourceTree = ""; }; + 288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = CordovaLib.xcodeproj; sourceTree = CORDOVALIB; }; + 301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; }; + 301BF5B4109A6A2B0062928A /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; + 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; + 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 301BF5BA109A6A2B0062928A /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 301BF5BC109A6A2B0062928A /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; + 301BF5BE109A6A2B0062928A /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; + 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + 301BF5C2109A6A2B0062928A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + 302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; + 302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + 302D95F014D2391D003F00A1 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = ""; }; + 3053AC6E109B7857006FCFE7 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = CORDOVALIB; }; + 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; + 3072F99613A8081B00425683 /* Capture.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Capture.bundle; sourceTree = ""; }; + 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = CordovaBuildSettings.xcconfig; path = ../CordovaBuildSettings.xcconfig; sourceTree = ""; }; + 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x~ipad.png"; sourceTree = ""; }; + 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = ""; }; + 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = ""; }; + 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = ""; }; + 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x~iphone.png"; sourceTree = ""; }; + 3088BBBC154F3926009F9C59 /* Default~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~iphone.png"; sourceTree = ""; }; + 308D052E1370CCF300D202BF /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = ""; }; + 308D052F1370CCF300D202BF /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = ""; }; + 308D05301370CCF300D202BF /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = ""; }; + 30A0434414DC770100060A13 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Localizable.strings; sourceTree = ""; }; + 30A0434714DC770100060A13 /* se */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = se; path = Localizable.strings; sourceTree = ""; }; + 30E1352610E2C1420031B30D /* Cordova.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Cordova.plist; path = ../Cordova.plist; sourceTree = ""; }; + 30E5649113A7FCAF007403D8 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + 32CA4F630368D1EE00C91783 /* HelloWorld-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HelloWorld-Prefix.pch"; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* HelloWorld-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "HelloWorld-Info.plist"; path = "../HelloWorld-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; + A93EDE2B16022ECD00FF615F /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; }; + A93EDE2E1602307200FF615F /* WTWikitudePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WTWikitudePlugin.h; sourceTree = ""; }; + A93EDE2F1602307200FF615F /* WTWikitudePlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WTWikitudePlugin.m; sourceTree = ""; }; + A93EDE491602315500FF615F /* WTArchitectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WTArchitectView.h; sourceTree = ""; }; + A93EDE4C1602315500FF615F /* libWikitudeSDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libWikitudeSDK.a; sourceTree = ""; }; + A93EDE4E1602315500FF615F /* libWikitudeSDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libWikitudeSDK.a; sourceTree = ""; }; + A93EDE771602348700FF615F /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; }; + A93EDE781602348700FF615F /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + A93EDE791602348700FF615F /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + A93EDE7A1602348700FF615F /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + A93EDE7B1602348700FF615F /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + A93EDE7C1602348700FF615F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A93EDE7D1602348700FF615F /* CoreMotion.framework in Frameworks */, + A93EDE7E1602348700FF615F /* CoreVideo.framework in Frameworks */, + A93EDE7F1602348700FF615F /* libsqlite3.dylib in Frameworks */, + A93EDE801602348700FF615F /* libz.dylib in Frameworks */, + A93EDE811602348700FF615F /* OpenGLES.framework in Frameworks */, + A93EDE821602348700FF615F /* Security.framework in Frameworks */, + 301BF552109A68D80062928A /* libCordova.a in Frameworks */, + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */, + 301BF5B5109A6A2B0062928A /* AddressBook.framework in Frameworks */, + 301BF5B7109A6A2B0062928A /* AddressBookUI.framework in Frameworks */, + 301BF5B9109A6A2B0062928A /* AudioToolbox.framework in Frameworks */, + 301BF5BB109A6A2B0062928A /* AVFoundation.framework in Frameworks */, + 301BF5BD109A6A2B0062928A /* CFNetwork.framework in Frameworks */, + 301BF5BF109A6A2B0062928A /* CoreLocation.framework in Frameworks */, + 301BF5C1109A6A2B0062928A /* MediaPlayer.framework in Frameworks */, + 301BF5C3109A6A2B0062928A /* QuartzCore.framework in Frameworks */, + 301BF5C5109A6A2B0062928A /* SystemConfiguration.framework in Frameworks */, + 305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */, + 30E5649213A7FCAF007403D8 /* CoreMedia.framework in Frameworks */, + A93EDE501602315500FF615F /* libWikitudeSDK.a in Frameworks */, + A93EDE511602315600FF615F /* libWikitudeSDK.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 302D95EE14D2391D003F00A1 /* MainViewController.h */, + 302D95EF14D2391D003F00A1 /* MainViewController.m */, + 302D95F014D2391D003F00A1 /* MainViewController.xib */, + 1D3623240D0F684500981E51 /* AppDelegate.h */, + 1D3623250D0F684500981E51 /* AppDelegate.m */, + ); + name = Classes; + path = HelloWorld/Classes; + sourceTree = SOURCE_ROOT; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* HelloWorld.app */, + ); + name = Products; + sourceTree = ""; + }; + 1F766FDB13BBADB100FB74C0 /* en.lproj */ = { + isa = PBXGroup; + children = ( + 1F766FDC13BBADB100FB74C0 /* Localizable.strings */, + ); + path = en.lproj; + sourceTree = ""; + }; + 1F766FDE13BBADB100FB74C0 /* es.lproj */ = { + isa = PBXGroup; + children = ( + 1F766FDF13BBADB100FB74C0 /* Localizable.strings */, + ); + path = es.lproj; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + A93EDE2B16022ECD00FF615F /* assets */, + 301BF56E109A69640062928A /* www */, + 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */, + A93EDE421602315500FF615F /* WikitudeSDK */, + 080E96DDFE201D6D7F000001 /* Classes */, + 307C750510C5A3420062BCA9 /* Plugins */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* HelloWorld-Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + path = HelloWorld; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 30A0434214DC770100060A13 /* de.lproj */, + 30A0434514DC770100060A13 /* se.lproj */, + 1F766FDB13BBADB100FB74C0 /* en.lproj */, + 1F766FDE13BBADB100FB74C0 /* es.lproj */, + 3072F99613A8081B00425683 /* Capture.bundle */, + 308D052D1370CCF300D202BF /* icons */, + 308D05311370CCF300D202BF /* splash */, + 30E1352610E2C1420031B30D /* Cordova.plist */, + 3053AC6E109B7857006FCFE7 /* VERSION */, + 8D1107310486CEB800E47090 /* HelloWorld-Info.plist */, + 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */, + ); + name = Resources; + path = HelloWorld/Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + A93EDE771602348700FF615F /* CoreMotion.framework */, + A93EDE781602348700FF615F /* CoreVideo.framework */, + A93EDE791602348700FF615F /* libsqlite3.dylib */, + A93EDE7A1602348700FF615F /* libz.dylib */, + A93EDE7B1602348700FF615F /* OpenGLES.framework */, + A93EDE7C1602348700FF615F /* Security.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + 288765FC0DF74451002DB57D /* CoreGraphics.framework */, + 301BF5B4109A6A2B0062928A /* AddressBook.framework */, + 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */, + 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */, + 301BF5BA109A6A2B0062928A /* AVFoundation.framework */, + 301BF5BC109A6A2B0062928A /* CFNetwork.framework */, + 301BF5BE109A6A2B0062928A /* CoreLocation.framework */, + 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */, + 301BF5C2109A6A2B0062928A /* QuartzCore.framework */, + 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */, + 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */, + 30E5649113A7FCAF007403D8 /* CoreMedia.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 301BF52E109A57CC0062928A /* Products */ = { + isa = PBXGroup; + children = ( + 301BF535109A57CC0062928A /* libCordova.a */, + 302D95EC14D23909003F00A1 /* CordovaLibTests.octest */, + 3088BBB5154F38EF009F9C59 /* CordovaLibApp.app */, + ); + name = Products; + sourceTree = ""; + }; + 307C750510C5A3420062BCA9 /* Plugins */ = { + isa = PBXGroup; + children = ( + A93EDE2D1602307200FF615F /* Wikitude */, + ); + name = Plugins; + path = HelloWorld/Plugins; + sourceTree = SOURCE_ROOT; + }; + 308D052D1370CCF300D202BF /* icons */ = { + isa = PBXGroup; + children = ( + 308D052E1370CCF300D202BF /* icon-72.png */, + 308D052F1370CCF300D202BF /* icon.png */, + 308D05301370CCF300D202BF /* icon@2x.png */, + ); + path = icons; + sourceTree = ""; + }; + 308D05311370CCF300D202BF /* splash */ = { + isa = PBXGroup; + children = ( + 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */, + 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */, + 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */, + 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */, + 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */, + 3088BBBC154F3926009F9C59 /* Default~iphone.png */, + ); + path = splash; + sourceTree = ""; + }; + 30A0434214DC770100060A13 /* de.lproj */ = { + isa = PBXGroup; + children = ( + 30A0434314DC770100060A13 /* Localizable.strings */, + ); + path = de.lproj; + sourceTree = ""; + }; + 30A0434514DC770100060A13 /* se.lproj */ = { + isa = PBXGroup; + children = ( + 30A0434614DC770100060A13 /* Localizable.strings */, + ); + path = se.lproj; + sourceTree = ""; + }; + A93EDE2D1602307200FF615F /* Wikitude */ = { + isa = PBXGroup; + children = ( + A93EDE2E1602307200FF615F /* WTWikitudePlugin.h */, + A93EDE2F1602307200FF615F /* WTWikitudePlugin.m */, + ); + path = Wikitude; + sourceTree = ""; + }; + A93EDE421602315500FF615F /* WikitudeSDK */ = { + isa = PBXGroup; + children = ( + A93EDE471602315500FF615F /* SDK */, + ); + name = WikitudeSDK; + path = HelloWorld/WikitudeSDK; + sourceTree = ""; + }; + A93EDE471602315500FF615F /* SDK */ = { + isa = PBXGroup; + children = ( + A93EDE481602315500FF615F /* inc */, + A93EDE4A1602315500FF615F /* lib */, + ); + path = SDK; + sourceTree = ""; + }; + A93EDE481602315500FF615F /* inc */ = { + isa = PBXGroup; + children = ( + A93EDE491602315500FF615F /* WTArchitectView.h */, + ); + path = inc; + sourceTree = ""; + }; + A93EDE4A1602315500FF615F /* lib */ = { + isa = PBXGroup; + children = ( + A93EDE4B1602315500FF615F /* Release-iphoneos */, + A93EDE4D1602315500FF615F /* Release-iphonesimulator */, + ); + path = lib; + sourceTree = ""; + }; + A93EDE4B1602315500FF615F /* Release-iphoneos */ = { + isa = PBXGroup; + children = ( + A93EDE4C1602315500FF615F /* libWikitudeSDK.a */, + ); + path = "Release-iphoneos"; + sourceTree = ""; + }; + A93EDE4D1602315500FF615F /* Release-iphonesimulator */ = { + isa = PBXGroup; + children = ( + A93EDE4E1602315500FF615F /* libWikitudeSDK.a */, + ); + path = "Release-iphonesimulator"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* HelloWorld */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloWorld" */; + buildPhases = ( + 304B58A110DAC018002A0835 /* Touch www folder */, + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 301BF551109A68C00062928A /* PBXTargetDependency */, + ); + name = HelloWorld; + productName = HelloWorld; + productReference = 1D6058910D05DD3D006BFB54 /* HelloWorld.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0420; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HelloWorld" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + en, + es, + de, + se, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 301BF52E109A57CC0062928A /* Products */; + ProjectRef = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* HelloWorld */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 301BF535109A57CC0062928A /* libCordova.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libCordova.a; + remoteRef = 301BF534109A57CC0062928A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 302D95EC14D23909003F00A1 /* CordovaLibTests.octest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CordovaLibTests.octest; + remoteRef = 302D95EB14D23909003F00A1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3088BBB5154F38EF009F9C59 /* CordovaLibApp.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = CordovaLibApp.app; + remoteRef = 3088BBB4154F38EF009F9C59 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 301BF570109A69640062928A /* www in Resources */, + 3053AC6F109B7857006FCFE7 /* VERSION in Resources */, + 30E1352710E2C1420031B30D /* Cordova.plist in Resources */, + 308D05371370CCF300D202BF /* icon-72.png in Resources */, + 308D05381370CCF300D202BF /* icon.png in Resources */, + 308D05391370CCF300D202BF /* icon@2x.png in Resources */, + 3072F99713A8081B00425683 /* Capture.bundle in Resources */, + 1F766FE113BBADB100FB74C0 /* Localizable.strings in Resources */, + 1F766FE213BBADB100FB74C0 /* Localizable.strings in Resources */, + 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */, + 30A0434814DC770100060A13 /* Localizable.strings in Resources */, + 30A0434914DC770100060A13 /* Localizable.strings in Resources */, + 3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */, + 3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */, + 3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */, + 3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */, + 3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */, + 3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */, + A93EDE2C16022ECD00FF615F /* assets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 304B58A110DAC018002A0835 /* Touch www folder */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Touch www folder"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "touch -cm ${PROJECT_DIR}/www"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */, + 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */, + A93EDE301602307200FF615F /* WTWikitudePlugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 301BF551109A68C00062928A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CordovaLib; + targetProxy = 301BF550109A68C00062928A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 1F766FDC13BBADB100FB74C0 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 1F766FDD13BBADB100FB74C0 /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 1F766FDF13BBADB100FB74C0 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 1F766FE013BBADB100FB74C0 /* es */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 30A0434314DC770100060A13 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 30A0434414DC770100060A13 /* de */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 30A0434614DC770100060A13 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 30A0434714DC770100060A13 /* se */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HelloWorld/HelloWorld-Prefix.pch"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INFOPLIST_FILE = "HelloWorld/HelloWorld-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/HelloWorld/WikitudeSDK/SDK/lib/Release$(EFFECTIVE_PLATFORM_NAME)\"", + ); + PRODUCT_NAME = HelloWorld; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HelloWorld/HelloWorld-Prefix.pch"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INFOPLIST_FILE = "HelloWorld/HelloWorld-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/HelloWorld/WikitudeSDK/SDK/lib/Release$(EFFECTIVE_PLATFORM_NAME)\"", + ); + PRODUCT_NAME = HelloWorld; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"", + "\"$(OBJROOT)/UninstalledProducts/include\"", + "\"$(BUILT_PRODUCTS_DIR)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + OTHER_LDFLAGS = ( + "-weak_framework", + CoreFoundation, + "-weak_framework", + UIKit, + "-weak_framework", + AVFoundation, + "-weak_framework", + CoreMedia, + "-weak-lSystem", + "-force_load", + "$(BUILT_PRODUCTS_DIR)/libCordova.a", + "-ObjC", + "-lstdc++", + ); + SDKROOT = iphoneos; + SKIP_INSTALL = NO; + USER_HEADER_SEARCH_PATHS = ""; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"", + "\"$(OBJROOT)/UninstalledProducts/include\"", + "\"$(BUILT_PRODUCTS_DIR)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + OTHER_LDFLAGS = ( + "-weak_framework", + CoreFoundation, + "-weak_framework", + UIKit, + "-weak_framework", + AVFoundation, + "-weak_framework", + CoreMedia, + "-weak-lSystem", + "-force_load$(BUILT_PRODUCTS_DIR)/libCordova.a)", + "-Obj-C", + "-lstdc++", + ); + SDKROOT = iphoneos; + SKIP_INSTALL = NO; + USER_HEADER_SEARCH_PATHS = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 67% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 85f375e..174a04e 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:HelloWorld.xcodeproj"> diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.xcworkspace/xcuserdata/Andi.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.xcworkspace/xcuserdata/Andi.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..280a3dd Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/project.xcworkspace/xcuserdata/Andi.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/xcuserdata/randymcmillan.xcuserdatad/xcschemes/PayPalExampleCDV.xcscheme b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/HelloWorld.xcscheme similarity index 70% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/xcuserdata/randymcmillan.xcuserdatad/xcschemes/PayPalExampleCDV.xcscheme rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/HelloWorld.xcscheme index 3d9af23..ab518cf 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/xcuserdata/randymcmillan.xcuserdatad/xcschemes/PayPalExampleCDV.xcscheme +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/HelloWorld.xcscheme @@ -1,5 +1,6 @@ + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "HelloWorld.app" + BlueprintName = "HelloWorld" + ReferencedContainer = "container:HelloWorld.xcodeproj"> @@ -31,10 +32,10 @@ + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "HelloWorld.app" + BlueprintName = "HelloWorld" + ReferencedContainer = "container:HelloWorld.xcodeproj"> @@ -50,10 +51,10 @@ + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "HelloWorld.app" + BlueprintName = "HelloWorld" + ReferencedContainer = "container:HelloWorld.xcodeproj"> @@ -68,10 +69,10 @@ + BlueprintIdentifier = "1D6058900D05DD3D006BFB54" + BuildableName = "HelloWorld.app" + BlueprintName = "HelloWorld" + ReferencedContainer = "container:HelloWorld.xcodeproj"> diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..14dd04a --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + HelloWorld.xcscheme + + orderHint + 2 + + + SuppressBuildableAutocreation + + 1D6058900D05DD3D006BFB54 + + primary + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/.gitignore b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/.gitignore new file mode 100644 index 0000000..d30c0b0 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/.gitignore @@ -0,0 +1,6 @@ +*.mode1v3 +*.perspectivev3 +*.pbxuser +.DS_Store +build +www/phonegap.js diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/AppDelegate.h b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/AppDelegate.h similarity index 72% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/AppDelegate.h rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/AppDelegate.h index 464408c..2ffeb08 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/AppDelegate.h +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/AppDelegate.h @@ -19,32 +19,25 @@ // // AppDelegate.h -// PayPalExampleCDV +// HelloWorld // -// Created by Randy McMillan on 3/15/12. -// Copyright OpenOSX.org 2012. All rights reserved. +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. // #import -#ifdef CORDOVA_FRAMEWORK - #import -#else - #import "CDVViewController.h" -#endif +#import +@interface AppDelegate : NSObject < UIApplicationDelegate > { -@interface AppDelegate : NSObject < UIApplicationDelegate, UIWebViewDelegate, CDVCommandDelegate > { - - NSString* invokeString; } // invoke string is passed to your app on launch, this is only valid if you -// edit FooBar.plist to add a protocol +// edit HelloWorld-Info.plist to add a protocol // a simple tutorial can be found here : // http://iphonedevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html -@property (nonatomic, copy) NSString* invokeString; @property (nonatomic, retain) IBOutlet UIWindow* window; @property (nonatomic, retain) IBOutlet CDVViewController* viewController; diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/AppDelegate.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/AppDelegate.m similarity index 66% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/AppDelegate.m rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/AppDelegate.m index 52e1b42..141e0b3 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/AppDelegate.m +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/AppDelegate.m @@ -19,27 +19,22 @@ // // AppDelegate.m -// PayPalExampleCDV +// HelloWorld // -// Created by Randy McMillan on 3/15/12. -// Copyright OpenOSX.org 2012. All rights reserved. +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. // #import "AppDelegate.h" #import "MainViewController.h" -#ifdef CORDOVA_FRAMEWORK - #import - #import -#else - #import "CDVPlugin.h" - #import "CDVURLProtocol.h" -#endif +#import +#import @implementation AppDelegate -@synthesize invokeString, window, viewController; +@synthesize window, viewController; - (id) init { @@ -48,8 +43,8 @@ **/ NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; - - [CDVURLProtocol registerPGHttpURLProtocol]; + + [CDVURLProtocol registerURLProtocol]; return [super init]; } @@ -62,9 +57,11 @@ - (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { NSURL* url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey]; + NSString* invokeString = nil; + if (url && [url isKindOfClass:[NSURL class]]) { - self.invokeString = [url absoluteString]; - NSLog(@"PayPalExampleCDV launchOptions = %@", url); + invokeString = [url absoluteString]; + NSLog(@"HelloWorld launchOptions = %@", url); } CGRect screenBounds = [[UIScreen mainScreen] bounds]; @@ -77,12 +74,9 @@ self.viewController.useSplashScreen = YES; self.viewController.wwwFolderName = @"www"; self.viewController.startPage = @"index.html"; + self.viewController.invokeString = invokeString; self.viewController.view.frame = viewBounds; - // over-ride delegates - self.viewController.webView.delegate = self; - self.viewController.commandDelegate = self; - // check whether the current orientation is supported: if it is, keep it, rather than forcing a rotation BOOL forceStartupRotation = YES; UIDeviceOrientation curDevOrientation = [[UIDevice currentDevice] orientation]; @@ -116,7 +110,7 @@ } // this happens while we are running ( in the background, or from within our own app ) -// only valid if FooBar.plist specifies a protocol to handle +// only valid if HelloWorld-Info.plist specifies a protocol to handle - (BOOL) application:(UIApplication*)application handleOpenURL:(NSURL*)url { if (!url) { @@ -133,56 +127,6 @@ return YES; } -#pragma PGCommandDelegate implementation - -- (id) getCommandInstance:(NSString*)className -{ - return [self.viewController getCommandInstance:className]; -} - -- (BOOL) execute:(CDVInvokedUrlCommand*)command -{ - return [self.viewController execute:command]; -} - -- (NSString*) pathForResource:(NSString*)resourcepath; -{ - return [self.viewController pathForResource:resourcepath]; -} - -#pragma UIWebDelegate implementation - -- (void) webViewDidFinishLoad:(UIWebView*) theWebView -{ - // only valid if FooBar.plist specifies a protocol to handle - if (self.invokeString) - { - // this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready - NSString* jsString = [NSString stringWithFormat:@"var invokeString = \"%@\";", self.invokeString]; - [theWebView stringByEvaluatingJavaScriptFromString:jsString]; - } - - // Black base color for background matches the native apps - theWebView.backgroundColor = [UIColor blackColor]; - - return [self.viewController webViewDidFinishLoad:theWebView]; -} - -- (void) webViewDidStartLoad:(UIWebView*)theWebView -{ - return [self.viewController webViewDidStartLoad:theWebView]; -} - -- (void) webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error -{ - return [self.viewController webView:theWebView didFailLoadWithError:error]; -} - -- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType -{ - return [self.viewController webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; -} - - (void) dealloc { [super dealloc]; diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.h b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.h similarity index 79% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.h rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.h index e8be223..0f5577c 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.h +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.h @@ -19,17 +19,13 @@ // // MainViewController.h -// PayPalExampleCDV +// HelloWorld // -// Created by Randy McMillan on 3/15/12. -// Copyright OpenOSX.org 2012. All rights reserved. +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. // -#ifdef CORDOVA_FRAMEWORK - #import -#else - #import "CDVViewController.h" -#endif +#import @interface MainViewController : CDVViewController diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.m new file mode 100644 index 0000000..86497c3 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.m @@ -0,0 +1,141 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// +// MainViewController.h +// HelloWorld +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import "MainViewController.h" + +@implementation MainViewController + +- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void) didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void) viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void) viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; +} + +/* Comment out the block below to over-ride */ +/* +- (CDVCordovaView*) newCordovaViewWithFrame:(CGRect)bounds +{ + return[super newCordovaViewWithFrame:bounds]; +} +*/ + +/* Comment out the block below to over-ride */ +/* +#pragma CDVCommandDelegate implementation + +- (id) getCommandInstance:(NSString*)className +{ + return [super getCommandInstance:className]; +} + +- (BOOL) execute:(CDVInvokedUrlCommand*)command +{ + return [super execute:command]; +} + +- (NSString*) pathForResource:(NSString*)resourcepath; +{ + return [super pathForResource:resourcepath]; +} + +- (void) registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className +{ + return [super registerPlugin:plugin withClassName:className]; +} +*/ + +#pragma UIWebDelegate implementation + +- (void) webViewDidFinishLoad:(UIWebView*) theWebView +{ + // only valid if ___PROJECTNAME__-Info.plist specifies a protocol to handle + if (self.invokeString) + { + // this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready + NSLog(@"DEPRECATED: window.invokeString - use the window.handleOpenURL(url) function instead, which is always called when the app is launched through a custom scheme url."); + NSString* jsString = [NSString stringWithFormat:@"var invokeString = \"%@\";", self.invokeString]; + [theWebView stringByEvaluatingJavaScriptFromString:jsString]; + } + + // Black base color for background matches the native apps + theWebView.backgroundColor = [UIColor blackColor]; + + return [super webViewDidFinishLoad:theWebView]; +} + +/* Comment out the block below to over-ride */ +/* + +- (void) webViewDidStartLoad:(UIWebView*)theWebView +{ + return [super webViewDidStartLoad:theWebView]; +} + +- (void) webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + return [super webView:theWebView didFailLoadWithError:error]; +} + +- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + return [super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; +} +*/ + +@end diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.xib b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.xib similarity index 86% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.xib rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.xib index 9837f57..e45d65c 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.xib +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Classes/MainViewController.xib @@ -1,4 +1,24 @@ + 1280 diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Cordova.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Cordova.plist similarity index 64% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Cordova.plist rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Cordova.plist index a85649d..01a59d6 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Cordova.plist +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Cordova.plist @@ -20,39 +20,47 @@ OpenAllWhitelistURLsInWebView + BackupWebStorage + ExternalHosts * Plugins - SAiOSPaypalPlugin - SAiOSPaypalPlugin - org.apache.cordova.accelerometer - CDVAccelerometer - org.apache.cordova.camera - CDVCamera - org.apache.cordova.connection - CDVConnection - org.apache.cordova.contacts - CDVContacts - org.apache.cordova.debugconsole - CDVDebugConsole - org.apache.cordova.file - CDVFile - org.apache.cordova.filetransfer - CDVFileTransfer - org.apache.cordova.geolocation + WikitudePlugin + WTWikitudePlugin + Device + CDVDevice + Logger + CDVLogger + Compass CDVLocation - org.apache.cordova.notification + Accelerometer + CDVAccelerometer + Camera + CDVCamera + NetworkStatus + CDVConnection + Contacts + CDVContacts + Debug Console + CDVDebugConsole + File + CDVFile + FileTransfer + CDVFileTransfer + Geolocation + CDVLocation + Notification CDVNotification - org.apache.cordova.media + Media CDVSound - org.apache.cordova.mediacapture + Capture CDVCapture - org.apache.cordova.splashscreen + SplashScreen CDVSplashScreen - org.apache.cordova.battery + Battery CDVBattery diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/CordovaBuildSettings.xcconfig b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/CordovaBuildSettings.xcconfig new file mode 100644 index 0000000..c70086c --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/CordovaBuildSettings.xcconfig @@ -0,0 +1,26 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. +// + +// Cordova.xcconfig +// __TESTING__ +// + +// Override this to use your project specific CORDOVALIB. +// You can base it off the current project path, $(PROJECT_DIR) +CORDOVALIB = $(CORDOVALIB) diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/PayPalExampleCDV-Info.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/HelloWorld-Info.plist similarity index 77% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/PayPalExampleCDV-Info.plist rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/HelloWorld-Info.plist index a9757ce..ef7c161 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/PayPalExampleCDV-Info.plist +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/HelloWorld-Info.plist @@ -2,12 +2,21 @@ - CFBundleIconFiles - - icon.png - icon@2x.png - icon-72.png - + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + icon.png + icon@2x.png + icon-72.png + icon-72@2x.png + + UIPrerenderedIcon + + + UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait @@ -28,7 +37,7 @@ CFBundleIconFile icon.png CFBundleIdentifier - net.randymcmillan.PayPalExampleCDV + com.wikitude.phonegapsamplehelloworld CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/HelloWorld-Prefix.pch b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/HelloWorld-Prefix.pch new file mode 100644 index 0000000..4105f6a --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/HelloWorld-Prefix.pch @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ +// +// Prefix header for all source files of the 'HelloWorld' target in the 'HelloWorld' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/README b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/README new file mode 100644 index 0000000..f6e19d7 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/README @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder. \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/Wikitude/WTWikitudePlugin.h b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/Wikitude/WTWikitudePlugin.h new file mode 100644 index 0000000..0a71316 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/Wikitude/WTWikitudePlugin.h @@ -0,0 +1,14 @@ +// +// WTWikitudeSDK.h +// HelloWorld +// +// Created by Andreas Schacherbauer on 8/24/12. +// +// + + +#import + +@interface WTWikitudePlugin : CDVPlugin + +@end diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/Wikitude/WTWikitudePlugin.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/Wikitude/WTWikitudePlugin.m new file mode 100644 index 0000000..3e54f35 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Plugins/Wikitude/WTWikitudePlugin.m @@ -0,0 +1,402 @@ +// +// WTWikitudeSDK.m +// HelloWorld +// +// Created by Andreas Schacherbauer on 8/24/12. +// +// + +#import "WTWikitudePlugin.h" + +// Wikitude SDK +#import "WTArchitectView.h" + + + + +@interface WTWikitudePlugin () + +@property (nonatomic, strong) WTArchitectView *architectView; + +@property (nonatomic, strong) NSString *currentARchitectViewCallbackID; +@property (nonatomic, strong) NSString *currentPlugInErrorCallback; + +@property (nonatomic, assign) BOOL isUsingInjectedLocation; + +@end + + +@implementation WTWikitudePlugin +@synthesize architectView=_architectView; + + +#pragma mark - View Lifecycle +/* View Lifecycle */ +- (void)isDeviceSupported:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + BOOL isDeviceSupported = [WTArchitectView isDeviceSupported]; + + if (isDeviceSupported) { + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:isDeviceSupported]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + } else { + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:isDeviceSupported]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +- (void)open:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + + BOOL success = [WTArchitectView isDeviceSupported]; + if ( success ) { + + NSString *sdkKey = [options objectForKey:@"apiKey"]; + NSString *architectWorldFilePath = [options objectForKey:@"filePath"]; + + // First, lets check if we need to init a new sdk view + if ( !_architectView ) { + self.architectView = [[WTArchitectView alloc] initWithFrame:self.viewController.view.bounds]; + self.architectView.delegate = self; + [self.architectView initializeWithKey:sdkKey motionManager:nil]; + } + + // then add the view in its own navController to the ui. we need a own navController to have a backButton which can be used to dismiss the view + UIViewController *viewController = [[UIViewController alloc] init]; + viewController.view = self.architectView; + + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController]; + navController.navigationBar.tintColor = [UIColor blackColor]; + navController.navigationBar.topItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissARchitectView)]; + [self.viewController presentViewController:navController animated:NO completion:^{ + // completion code + }]; + + + + // and finaly load the architect world, specified in the open function in js + if (architectWorldFilePath) { + + NSString *worldName = [architectWorldFilePath lastPathComponent]; + worldName = [worldName stringByDeletingPathExtension]; + NSString *worldNameExtension = [architectWorldFilePath pathExtension]; + + NSString *architectWorldDirectoryPath = [architectWorldFilePath stringByDeletingLastPathComponent]; + + NSString *loadablePath = [[NSBundle mainBundle] pathForResource:worldName ofType:worldNameExtension inDirectory:architectWorldDirectoryPath]; + [self.architectView loadArchitectWorldFromUrl:loadablePath]; + } + } + + // start the sdk view updates + [self.architectView start]; + + + if ( success ) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)close:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + + [self.architectView stop]; +// [self.architectView removeFromSuperview]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)show:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + self.architectView.hidden = NO; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)hide:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + self.architectView.hidden = YES; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)onResume:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + [self.architectView start]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)onPause:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + [self.architectView stop]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +#pragma mark - Location Handling + +- (void)setLocation:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + + float latitude = [[options objectForKey:@"lat"] floatValue]; + float longitude = [[options objectForKey:@"lon"] floatValue]; + float altitude = [[options objectForKey:@"alt"] floatValue]; + float accuracy = [[options objectForKey:@"acc"] floatValue]; + + if (!self.isUsingInjectedLocation) { + [self.architectView setUseInjectedLocation:YES]; + self.isUsingInjectedLocation = YES; + } + + [self.architectView injectLocationWithLatitude:latitude longitude:longitude altitude:altitude accuracy:accuracy]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +#pragma mark - Javascript + +- (void)callJavascript:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + + @try { + + if (arguments.count >= 1) { + + NSMutableString *javascriptToCall = [[arguments objectAtIndex:1] mutableCopy]; + for (NSUInteger i = 2; i < arguments.count; i++) { + [javascriptToCall appendString:[arguments objectAtIndex:i]]; + } + + if (self.architectView) { + [self.architectView callJavaScript:javascriptToCall]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + }else + { + // return error no javascript to call found + } + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +- (void)onUrlInvoke:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + + + self.currentARchitectViewCallbackID = callbackId; + self.currentPlugInErrorCallback = callbackId; + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT]; + [pluginResult setKeepCallbackAsBool:YES]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +#pragma mark - WTArchitectView Delegate +- (void)urlWasInvoked:(NSString *)url +{ + + CDVPluginResult *pluginResult = nil; + NSString *javaScriptResult = nil; + + + if (url && self.currentARchitectViewCallbackID) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:url]; + [pluginResult setKeepCallbackAsBool:YES]; + javaScriptResult = [pluginResult toSuccessCallbackString:self.currentARchitectViewCallbackID]; + + + }else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + javaScriptResult = [pluginResult toSuccessCallbackString:self.currentPlugInErrorCallback]; + } + + [self writeJavascript:javaScriptResult]; +} + +- (void)dismissARchitectView +{ + [self.viewController dismissModalViewControllerAnimated:NO]; + [self.architectView stop]; +} + +@end diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/controls_bg.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/controls_bg.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/controls_bg@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg@2x.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/controls_bg@2x.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg@2x.png diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg@2x~ipad.png new file mode 100644 index 0000000..d4e3483 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg@2x~ipad.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/controls_bg~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg~ipad.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/controls_bg~ipad.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/controls_bg~ipad.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/microphone.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/microphone.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/microphone@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone@2x.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/microphone@2x.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone@2x.png diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone@2x~ipad.png new file mode 100644 index 0000000..af1bbb2 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone@2x~ipad.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/microphone~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone~ipad.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/microphone~ipad.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/microphone~ipad.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/record_button.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/record_button.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/record_button@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button@2x.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/record_button@2x.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button@2x.png diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button@2x~ipad.png new file mode 100644 index 0000000..0ac2e67 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button@2x~ipad.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/record_button~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button~ipad.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/record_button~ipad.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/record_button~ipad.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/recording_bg.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/recording_bg.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/recording_bg@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg@2x.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/recording_bg@2x.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg@2x.png diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg@2x~ipad.png new file mode 100644 index 0000000..a1b7208 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg@2x~ipad.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/recording_bg~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg~ipad.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/recording_bg~ipad.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/recording_bg~ipad.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/stop_button.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/stop_button.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/stop_button@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button@2x.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/stop_button@2x.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button@2x.png diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button@2x~ipad.png new file mode 100644 index 0000000..88b606c Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button@2x~ipad.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/stop_button~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button~ipad.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/Capture.bundle/stop_button~ipad.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/Capture.bundle/stop_button~ipad.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/de.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/de.lproj/Localizable.strings similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/de.lproj/Localizable.strings rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/de.lproj/Localizable.strings diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/en.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/en.lproj/Localizable.strings similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/en.lproj/Localizable.strings rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/en.lproj/Localizable.strings diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/es.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/es.lproj/Localizable.strings similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/es.lproj/Localizable.strings rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/es.lproj/Localizable.strings diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/icons/icon-72.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon-72.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/icons/icon-72.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon-72.png diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon-72@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon-72@2x.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon-72@2x.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/icons/icon.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/icons/icon.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/icons/icon@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon@2x.png similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/icons/icon@2x.png rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/icons/icon@2x.png diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/se.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/se.lproj/Localizable.strings similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/se.lproj/Localizable.strings rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/se.lproj/Localizable.strings diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Landscape@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Landscape@2x~ipad.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Landscape@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Landscape~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Landscape~ipad.png new file mode 100644 index 0000000..f8e2b52 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Landscape~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Portrait@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Portrait@2x~ipad.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Portrait@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Portrait~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Portrait~ipad.png new file mode 100644 index 0000000..af9158a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default-Portrait~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default@2x~iphone.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default@2x~iphone.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default@2x~iphone.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default~iphone.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default~iphone.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/Resources/splash/Default~iphone.png differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/main.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/main.m similarity index 88% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/main.m rename to iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/main.m index a076711..25e83db 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/main.m +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/HelloWorld/main.m @@ -18,10 +18,10 @@ */ // // main.m -// PayPalExampleCDV +// HelloWorld // -// Created by Randy McMillan on 3/15/12. -// Copyright OpenOSX.org 2012. All rights reserved. +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. // #import diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/world/HelloWorld.html b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/world/HelloWorld.html new file mode 100644 index 0000000..3ae6442 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/world/HelloWorld.html @@ -0,0 +1,52 @@ + + + + + + +My ARchitect World + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/debug b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/debug new file mode 100755 index 0000000..37b0d63 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/debug @@ -0,0 +1,47 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# +# compile and launch a Cordova/iOS project to the simulator +# + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) +PROJECT_PATH=$CORDOVA_PATH/.. + +for file in $PROJECT_PATH/*.xcodeproj; do + PROJECT_NAME=$(basename "$file" .xcodeproj) +done; + +cd $PROJECT_PATH + +APP=build/$PROJECT_NAME.app +SDK=`xcodebuild -showsdks | grep Sim | tail -1 | awk '{print $6}'` + +xcodebuild -project $PROJECT_NAME.xcodeproj -arch i386 -target $PROJECT_NAME -configuration Debug -sdk $SDK clean build VALID_ARCHS="i386" CONFIGURATION_BUILD_DIR=$PROJECT_PATH/build + +# launch using emulate + +$CORDOVA_PATH/emulate $PROJECT_PATH/$APP + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/emulate b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/emulate new file mode 100755 index 0000000..3822121 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/emulate @@ -0,0 +1,58 @@ +#! /bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) +PROJECT_PATH=$CORDOVA_PATH/.. + +function getAppPath() { + for file in $PROJECT_PATH/*.xcodeproj; do + PROJECT_NAME=$(basename "$file" .xcodeproj) + done; + APP=build/$PROJECT_NAME.app + APP_PATH=$PROJECT_PATH/$APP +} + +APP_PATH=$1 + +if [ $# -lt 1 ]; then + getAppPath +fi + +if [ ! -d "$APP_PATH" ]; then + read -p "Project '$APP_PATH' is not built. Build? [y/n]: " REPLY + if [ "$REPLY" == "y" ]; then + $CORDOVA_PATH/debug + exit 0 + else + echo "$APP_PATH not found to emulate." + exit 1 + fi +fi + +# launch using ios-sim + +if which ios-sim >/dev/null; then + ios-sim launch $APP_PATH --stderr console.log --stdout console.log & +else + echo -e '\033[31mError: ios-sim was not found. Please download, build and install version 1.4 or greater from https://github.com/phonegap/ios-sim into your path. Or "brew install ios-sim" using homebrew: http://mxcl.github.com/homebrew/\033[m'; exit 1; +fi + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/log b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/log new file mode 100755 index 0000000..fd1261c --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/log @@ -0,0 +1,26 @@ +#! /bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# +# USAGE +# +# ./log [path] +# +tail -f ${1:-".."}/console.log diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/config.xml b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/config.xml new file mode 100644 index 0000000..a7e35db --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/config.xml @@ -0,0 +1,47 @@ + + + Hello Cordova + + + A sample Apache Cordova application that responds to the deviceready event. + + + + Apache Cordova Team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/cordova-2.0.0.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/cordova-2.0.0.js new file mode 100644 index 0000000..c2caa2f --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/cordova-2.0.0.js @@ -0,0 +1,5240 @@ +// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0 + +// File generated at :: Wed Jul 18 2012 16:47:25 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + */ + fireDocumentEvent: function(type, data) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + // TODO: this is Android only; think about how to do this better + shuttingDown:false, + UsePolling:false, + // END TODO + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'); + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.guid = 1; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; +}; + +module.exports = FileUploadOptions; +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger then file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + me.successCallback(); + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param status The status code (int) + * @param msg The status message (string) + */ +Media.onStatus = function(id, msg, value) { + var media = mediaObjects[id]; + // If state update + if (msg === Media.MEDIA_STATE) { + if (value === Media.MEDIA_STOPPED) { + if (media.successCallback) { + media.successCallback(); + } + } + if (media.statusCallback) { + media.statusCallback(value); + } + } + else if (msg === Media.MEDIA_DURATION) { + media._duration = value; + } + else if (msg === Media.MEDIA_ERROR) { + if (media.errorCallback) { + // value should be a MediaError object when msg == MEDIA_ERROR + media.errorCallback(value); + } + } + else if (msg === Media.MEDIA_POSITION) { + media._position = value; + } +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. + * @constructor + */ +var MediaError = function(code, msg) { + this.code = (code !== undefined ? code : null); + this.message = msg || ""; +}; + +MediaError.MEDIA_ERR_NONE_ACTIVE = 0; +MediaError.MEDIA_ERR_ABORTED = 1; +MediaError.MEDIA_ERR_NETWORK = 2; +MediaError.MEDIA_ERR_DECODE = 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; + +module.exports = MediaError; +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +// TODO: can we axe this? +/** + * Casts a PluginResult message property (array of objects) to an array of MediaFile objects + * (used in Objective-C and Android) + * + * @param {PluginResult} pluginResult + */ +MediaFile.cast = function(pluginResult) { + var mediaFiles = []; + for (var i=0; i.dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retreived a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object who's properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/ios/plugin/ios/Contact.js +define("cordova/plugin/ios/Contact", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'); + +/** + * Provides iOS Contact.display API. + */ +module.exports = { + display : function(errorCB, options) { + /* + * Display a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + */ + + if (this.id === null) { + if (typeof errorCB === "function") { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + } + else { + exec(null, errorCB, "Contacts","displayContact", [this.id, options]); + } + } +}; +}); + +// file: lib/ios/plugin/ios/Entry.js +define("cordova/plugin/ios/Entry", function(require, exports, module) { +module.exports = { + toURL:function() { + // TODO: refactor path in a cross-platform way so we can eliminate + // these kinds of platform-specific hacks. + return "file://localhost" + this.fullPath; + }, + toURI: function() { + console.log("DEPRECATED: Update your code to use 'toURL'"); + return "file://localhost" + this.fullPath; + } +}; +}); + +// file: lib/ios/plugin/ios/FileReader.js +define("cordova/plugin/ios/FileReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + FileReader = require('cordova/plugin/FileReader'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +module.exports = { + readAsText:function(file, encoding) { + // Figure out pathing + this.fileName = ''; + if (typeof file.fullPath === 'undefined') { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + // Default encoding is UTF-8 + var enc = encoding ? encoding : "UTF-8"; + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save result + me.result = decodeURIComponent(r); + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // null result + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + "File", "readAsText", [this.fileName, enc]); + } +}; +}); + +// file: lib/ios/plugin/ios/console.js +define("cordova/plugin/ios/console", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * This class provides access to the debugging console. + * @constructor + */ +var DebugConsole = function() { + this.winConsole = window.console; + this.logLevel = DebugConsole.INFO_LEVEL; +}; + +// from most verbose, to least verbose +DebugConsole.ALL_LEVEL = 1; // same as first level +DebugConsole.INFO_LEVEL = 1; +DebugConsole.WARN_LEVEL = 2; +DebugConsole.ERROR_LEVEL = 4; +DebugConsole.NONE_LEVEL = 8; + +DebugConsole.prototype.setLevel = function(level) { + this.logLevel = level; +}; + +var stringify = function(message) { + try { + if (typeof message === "object" && JSON && JSON.stringify) { + try { + return JSON.stringify(message); + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + } else { + return message.toString(); + } + } catch (e) { + return e.toString(); + } +}; + +/** + * Print a normal log message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.log = function(message) { + if (this.logLevel <= DebugConsole.INFO_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'INFO' } ]); + } + else if (this.winConsole && this.winConsole.log) { + this.winConsole.log(message); + } +}; + +/** + * Print a warning message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.warn = function(message) { + if (this.logLevel <= DebugConsole.WARN_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'WARN' } ]); + } + else if (this.winConsole && this.winConsole.warn) { + this.winConsole.warn(message); + } +}; + +/** + * Print an error message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.error = function(message) { + if (this.logLevel <= DebugConsole.ERROR_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'ERROR' } ]); + } + else if (this.winConsole && this.winConsole.error){ + this.winConsole.error(message); + } +}; + +module.exports = new DebugConsole(); +}); + +// file: lib/ios/plugin/ios/contacts.js +define("cordova/plugin/ios/contacts", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides iOS enhanced contacts API. + */ +module.exports = { + newContactUI : function(successCallback) { + /* + * Create a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * returns: the id of the created contact as param to successCallback + */ + exec(successCallback, null, "Contacts","newContact", []); + }, + chooseContact : function(successCallback, options) { + /* + * Select a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + * + * returns: the id of the selected contact as param to successCallback + */ + exec(successCallback, null, "Contacts","chooseContact", [options]); + } +}; +}); + +// file: lib/ios/plugin/ios/nativecomm.js +define("cordova/plugin/ios/nativecomm", function(require, exports, module) { +var cordova = require('cordova'); + +/** + * Called by native code to retrieve all queued commands and clear the queue. + */ +module.exports = function() { + var json = JSON.stringify(cordova.commandQueue); + cordova.commandQueue = []; + return json; +}; +}); + +// file: lib/ios/plugin/ios/notification.js +define("cordova/plugin/ios/notification", function(require, exports, module) { +var Media = require('cordova/plugin/Media'); + +module.exports = { + beep:function(count) { + (new Media('beep.wav')).play(); + } +}; +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + + Hello Cordova + + +
+

Apache Cordovaâ„¢

+
+ + +
+
+ + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/js/WikitudePlugin.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/js/WikitudePlugin.js new file mode 100644 index 0000000..997558e --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/js/WikitudePlugin.js @@ -0,0 +1,353 @@ +var WikitudePlugin = { + + /** + * + * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk + * If you're having a trial version, leave this string empty + * + */ + mySDKKey : "ENTER-YOUR-KEY-HERE", + + /** + * + * Change the value of this variable to modify the location update rate + * + */ + locationUpdateRate : 3000, + + /** + * + * This variable represents if the current device is capable of running the Wikitude SDK + * + */ + isDeviceSupported : false, + + /** + * + * This watchID is used to shedule location updates + * + */ + watchID : null, + + /** + * + * Callbacks to get device information if ARchitect Worlds can be launched + * + */ + onDeviceSupportedCallback : null, + onDeviceNotSupportedCallback : null, + + /** + * + * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch + * + */ + onARchitectWorldLaunchedCallback : null, + onARchitectWorldFailedLaunchingCallback : null, + + /** + * + * This function gets called if PhoneGap reports that it has finished loading successfully. + * + */ + isDeviceSupported: function(successCallback, errorCallback) + { + + WikitudePlugin.onDeviceSupportedCallback = successCallback; + WikitudePlugin.onDeviceNotSupportedCallback = errorCallback; + + + // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin + cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]); + + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK + * + */ + deviceIsARchitectReady : function() + { + // We keep track of the device status + WikitudePlugin.isDeviceSupported = true; + + + if(WikitudePlugin.onDeviceSupportedCallback) + { + WikitudePlugin.onDeviceSupportedCallback(); + } + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK. + * + */ + deviceIsNotARchitectReady : function() + { + WikitudePlugin.isDeviceSupported = false; + + // In this case we notify the user that his device is not supported by the Wikitude SDK + if(WikitudePlugin.onDeviceNotSupportedCallback) + { + WikitudePlugin.onDeviceNotSupportedCallback(); + } + }, + + + /* + * ============================================================================================================================= + * + * PUBLIC API + * + * ============================================================================================================================= + */ + + /* Managing ARchitect world loading */ + + /** + * + * Call this function if you want to load an ARchitect World + * + * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox + * + */ + loadARchitectWorld : function(worldPath) + { + + // before we actually call load, we check again if the device is able to open the world + if(WikitudePlugin.isDeviceSupported) + { + + // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys: + // @param {Object} options (required) + // @param {String} options.sdkKey License key for the Wikitude SDK + // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox + + cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]); + + + // We add an event listener on the resume and pause event of the application lifecycle + document.addEventListener("resume", WikitudePlugin.onResume, false); + document.addEventListener("pause", WikitudePlugin.onPause, false); + + // After we started loading the world, we start location updates + WikitudePlugin.startLocationUpdates(); + + }else + { + // if the device is not able to start the Wikitude SDK, we notify the user again + WikitudePlugin.deviceNotARchitectReady(); + } + }, + + /* Managing the Wikitude SDK Lifecycle */ + + /** + * + * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen + * + */ + close : function() + { + document.removeEventListener("pause", WikitudePlugin.onPause, false); + document.removeEventListener("resume", WikitudePlugin.onResume, false); + + WikitudePlugin.stopLocationUpdates(); + + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]); + }, + + /** + * + * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active + * + */ + hide : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]); + }, + + /** + * + * Use this function to show the Wikitude SDK if it was hidden before + * + */ + show : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]); + }, + + /* Interacting with the Wikitude SDK */ + + /** + * + * Use this function to call javascript which will be executed in the context of your ARchitect World + * + * + * @param js The JavaScript that gets evaluated in context of the ARchitect World + * + */ + callJavaScript : function(js) + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]); + }, + + /** + * + * Use this function to set a callback which will be invoked when the ARchitect World calls for example + * document.location = "architectsdk://opendetailpage?id=9"; + * + * + * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://" + */ + setOnUrlInvokeCallback : function(onUrlInvokeCallback) + { + cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]); + }, + + + /* + * ============================================================================================================================= + * + * Callbacks of public functions + * + * ============================================================================================================================= + */ + + /** + * + * Use this callback to get notified if the world loaded successfully + * + */ + worldLaunched : function() + { + if(WikitudePlugin.onARchitectWorldLaunchedCallback) + { + WikitudePlugin.onARchitectWorldLaunchedCallback(); + } + }, + + /** + * + * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World + * + */ + worldFailedLaunching : function(err) + { + if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback) + { + WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err); + } + }, + + /* Lifecycle updates */ + + /** + * + * This function actually starts the PhoneGap location updates + * + */ + startLocationUpdates : function() + { + + WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate }); + }, + + /** + * + * This callback gets called everytime the location did update + * + */ + onReceivedLocation : function(position) + { + + // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]); + }, + + /** + * + * Use this function to stop location updates + * + */ + stopLocationUpdates : function() + { + + // We clear the location update watch which was responsible for updating the location in a specific time interval + navigator.geolocation.clearWatch(WikitudePlugin.watchID); + WikitudePlugin.watchID = null; + }, + + /** + * + * This function gets called every time the application did become active. + * + */ + onResume : function() + { + + // Call the Wikitude SDK that the application did become active again + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]); + + // And start continuing updating the user location + WikitudePlugin.startLocationUpdates(); + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onPause : function() + { + + // Call the Wikitude SDK that the application did resign active + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]); + + // And stop all ongoing location updates + WikitudePlugin.stopLocationUpdates(); + }, + + /** + * + * Android specific! + * This function gets called if the user presses the back button + * + */ + onBackButton : function() + { + + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]); + WikitudePlugin.stopLocationUpdates(); + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeOK : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeError : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + report: function(id) + { + console.log("app report: " + id); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/js/index.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/js/index.js new file mode 100644 index 0000000..412a0fe --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/js/index.js @@ -0,0 +1,116 @@ +var app = { + initialize: function() { + this.bind(); + }, + bind: function() { + document.addEventListener('deviceready', this.deviceready, false); + }, + + /** + * + * This function extracts an url parameter + * + */ + getUrlParameterForKey : function( url, requestedParam ) + { + requestedParam = requestedParam.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); + var regexS = "[\\?&]"+requestedParam+"=([^&#]*)"; + var regex = new RegExp( regexS ); + var results = regex.exec( url ); + if( results == null ) + return ""; + else + { + var result = decodeURIComponent(results[1]); + return result; + } + + }, + + /** + * + * This function gets called if you call "document.location = architectsdk://" in your ARchitect World + * + * + * @param url The url which was called in ARchitect World + * + */ + onClickInARchitectWorld : function(url) + { + + app.report("you clicked on a label with text: " + app.getUrlParameterForKey(url, 'text')); + }, + + /** + * + * This function gets called it the Wikitude SDK is able to start an ARchitect World (the current device is supported by the Wikitude SDK) + * + */ + onDeviceIsReadyCallback : function() + { + // The device is able to launch ARchitect World, so we load the 'Hello World' example + WikitudePlugin.loadARchitectWorld("assets/world/HelloWorld.html"); + + // To be able to respond on events inside the ARchitect World, we set a onURLInvoke callback + WikitudePlugin.setOnUrlInvokeCallback(app.onClickInARchitectWorld); + + // This is a example how you can interact with the ARchitect World to pass in additional information + // In this example, a JavaScript function gets called which sets a new text for a label + WikitudePlugin.callJavaScript("didReceivedNewTextForLabel('Hello World')"); + }, + + /** + * + * This function gets if the current device is not capable of running ARchitect Worlds + * + */ + onDeviceIsUnsupportedCallback : function() + { + app.report('device is not supported'); + }, + + /** + * + * This function gets if the ARchitect World finished loading + * + */ + onARchitectWorldLaunchedCallback : function() + { + app.report('ARchitect World launched'); + }, + + /** + * + * This function gets if the ARchitect failed loading + * + */ + onARchitectWorldFailedLaunchingCallback : function(err) + { + app.report('ARchitect World failed launching'); + }, + + /** + * + * This function gets called when the Wikitude SDK is ready to start an ARchitect World (the current device is supported by the Wikitude SDK) + * + */ + deviceready: function() { + // note that this is an event handler so the scope is that of the event + // so we need to call app.report(), and not this.report() + app.report('deviceready'); + + // When PhoneGap finished loading we forward this event into the Wikitude SDK wrapper. + // @param {function} A function which gets called if the device is able to launch ARchitect Worlds + // @param {function} A function which gets called if the device is not able to launch ARchitect Worlds + WikitudePlugin.isDeviceSupported(app.onDeviceIsReadyCallback, app.onDeviceIsUnsupportedCallback); + + // set a callback on the WikitudePlugin to get informed when the ARchitect World finished loading + WikitudePlugin.onARchitectWorldLaunchedCallback = app.onARchitectWorldLaunchedCallback; + + // Set a callback on the WikitudePlugin to get informed when the ARchitect World failed loading + WikitudePlugin.onARchitectWorldFailedLaunchingCallback = app.onARchitectWorldFailedLaunchingCallback; + }, + report: function(id) { + console.log("report:" + id); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_128.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_128.png new file mode 100644 index 0000000..3516df3 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_128.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_16.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_16.png new file mode 100644 index 0000000..54e19c5 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_16.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_24.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_24.png new file mode 100644 index 0000000..c7d43ad Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_24.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_256.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_256.png new file mode 100644 index 0000000..e1cd0e6 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_256.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_32.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_32.png new file mode 100644 index 0000000..734fffc Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_32.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_48.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_48.png new file mode 100644 index 0000000..8ad8bac Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_48.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_512.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_512.png new file mode 100644 index 0000000..c9465f3 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_512.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_64.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_64.png new file mode 100644 index 0000000..03b3849 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_64.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_36.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_36.png new file mode 100644 index 0000000..cd5032a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_36.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_48.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_48.png new file mode 100644 index 0000000..e79c606 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_48.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_72.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_72.png new file mode 100644 index 0000000..4d27634 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_72.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_96.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_96.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_android_96.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_bb_80.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_bb_80.png new file mode 100644 index 0000000..f86a27a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_bb_80.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_114.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_114.png new file mode 100644 index 0000000..efd9c37 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_114.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_144.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_144.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_144.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_57.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_57.png new file mode 100644 index 0000000..c795fc4 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_57.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_72.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_72.png new file mode 100644 index 0000000..b1cfde7 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/icon/cordova_ios_72.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_hdpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_hdpi_landscape.png new file mode 100644 index 0000000..a61e2b1 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_hdpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_hdpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_hdpi_portrait.png new file mode 100644 index 0000000..5d6a28a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_hdpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_ldpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_ldpi_landscape.png new file mode 100644 index 0000000..f3934cd Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_ldpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_ldpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_ldpi_portrait.png new file mode 100644 index 0000000..65ad163 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_ldpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_mdpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_mdpi_landscape.png new file mode 100644 index 0000000..a1b697c Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_mdpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_mdpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_mdpi_portrait.png new file mode 100644 index 0000000..ea15693 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_mdpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_xhdpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_xhdpi_landscape.png new file mode 100644 index 0000000..79f2f09 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_xhdpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_xhdpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_xhdpi_portrait.png new file mode 100644 index 0000000..c2e8042 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/android_xhdpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/blackberry_transparent_300.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/blackberry_transparent_300.png new file mode 100644 index 0000000..b548bdc Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/blackberry_transparent_300.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/blackberry_transparent_400.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/blackberry_transparent_400.png new file mode 100644 index 0000000..3facdf9 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/blackberry_transparent_400.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_landscape.png new file mode 100644 index 0000000..04be5ac Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_portrait.png new file mode 100644 index 0000000..41e839d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_retina_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_retina_landscape.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_retina_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_retina_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_retina_portrait.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/ipad_retina_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_landscape.png new file mode 100644 index 0000000..d154883 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_portrait.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_retina_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_retina_landscape.png new file mode 100644 index 0000000..0165669 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_retina_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_retina_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_retina_portrait.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/iphone_retina_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/windows_phone_portrait.jpg b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/windows_phone_portrait.jpg new file mode 100644 index 0000000..9f95387 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/res/screen/windows_phone_portrait.jpg differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec.html b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec.html new file mode 100644 index 0000000..83d7d2e --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec.html @@ -0,0 +1,50 @@ + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec/helper.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec/helper.js new file mode 100644 index 0000000..9f99445 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec/helper.js @@ -0,0 +1,11 @@ +afterEach(function() { + document.getElementById('stage').innerHTML = ''; +}); + +var helper = { + trigger: function(obj, name) { + var e = document.createEvent('Event'); + e.initEvent(name, true, true); + obj.dispatchEvent(e); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec/index.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec/index.js new file mode 100644 index 0000000..121cf63 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/www/spec/index.js @@ -0,0 +1,49 @@ +describe('app', function() { + describe('initialize', function() { + it('should bind deviceready', function() { + runs(function() { + spyOn(app, 'deviceready'); + app.initialize(); + helper.trigger(window.document, 'deviceready'); + }); + + waitsFor(function() { + return (app.deviceready.calls.length > 0); + }, 'deviceready should be called once', 500); + + runs(function() { + expect(app.deviceready).toHaveBeenCalled(); + }); + }); + }); + + describe('deviceready', function() { + it('should report that it fired', function() { + spyOn(app, 'report'); + app.deviceready(); + expect(app.report).toHaveBeenCalledWith('deviceready'); + }); + }); + + describe('report', function() { + beforeEach(function() { + var el = document.getElementById('stage'); + el.innerHTML = ['
', + '

Pending

', + '

Complete

', + '
'].join('\n'); + }); + + it('should show the completion state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .complete:not(.hide)'); + expect(el).toBeTruthy(); + }); + + it('should hide the pending state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .pending.hide'); + expect(el).toBeTruthy(); + }); + }); +}); diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/README.md b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/README.md new file mode 100644 index 0000000..985ec76 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Basic/README.md @@ -0,0 +1,13 @@ +# Hello World iOS + +This document describes all necessary steps to get the sample running. + + +###Setup +*** + + +* Download the Wikitude SDK from [our website](http://www.wikitude.com/developer/sdk) (You need to register yourself as a Wikitude developer) + +* Copy the SDK folder from ``` [DownloadedSDKRoot/iOS/SDK/SDK] ``` into ``` [ProjectFolder/"AppName"/WikitudeSDK] ``` + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/TemplateIcon.icns b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/TemplateIcon.icns new file mode 100644 index 0000000..47f85e5 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/TemplateIcon.icns differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/TemplateInfo.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/TemplateInfo.plist new file mode 100644 index 0000000..6e6b271 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/TemplateInfo.plist @@ -0,0 +1,28 @@ + + + + + + Description + This template provides a starting point for a Cordova based application. Just modify the www folder contents with your HTML, CSS and Javascript. + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.pbxproj b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fcfa7c8 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.pbxproj @@ -0,0 +1,870 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; }; + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 1F766FE113BBADB100FB74C0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1F766FDC13BBADB100FB74C0 /* Localizable.strings */; }; + 1F766FE213BBADB100FB74C0 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1F766FDF13BBADB100FB74C0 /* Localizable.strings */; }; + 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; }; + 301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; }; + 301BF570109A69640062928A /* www in Resources */ = {isa = PBXBuildFile; fileRef = 301BF56E109A69640062928A /* www */; }; + 301BF5B5109A6A2B0062928A /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B4109A6A2B0062928A /* AddressBook.framework */; }; + 301BF5B7109A6A2B0062928A /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */; }; + 301BF5B9109A6A2B0062928A /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */; }; + 301BF5BB109A6A2B0062928A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BA109A6A2B0062928A /* AVFoundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 301BF5BD109A6A2B0062928A /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BC109A6A2B0062928A /* CFNetwork.framework */; }; + 301BF5BF109A6A2B0062928A /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5BE109A6A2B0062928A /* CoreLocation.framework */; }; + 301BF5C1109A6A2B0062928A /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */; }; + 301BF5C3109A6A2B0062928A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C2109A6A2B0062928A /* QuartzCore.framework */; }; + 301BF5C5109A6A2B0062928A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */; }; + 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; }; + 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; }; + 3053AC6F109B7857006FCFE7 /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 3053AC6E109B7857006FCFE7 /* VERSION */; }; + 305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */; }; + 3072F99713A8081B00425683 /* Capture.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3072F99613A8081B00425683 /* Capture.bundle */; }; + 3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */; }; + 3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */; }; + 3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */; }; + 3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */; }; + 3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */; }; + 3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBC154F3926009F9C59 /* Default~iphone.png */; }; + 308D05371370CCF300D202BF /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052E1370CCF300D202BF /* icon-72.png */; }; + 308D05381370CCF300D202BF /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052F1370CCF300D202BF /* icon.png */; }; + 308D05391370CCF300D202BF /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05301370CCF300D202BF /* icon@2x.png */; }; + 30A0434814DC770100060A13 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30A0434314DC770100060A13 /* Localizable.strings */; }; + 30A0434914DC770100060A13 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30A0434614DC770100060A13 /* Localizable.strings */; }; + 30E1352710E2C1420031B30D /* Cordova.plist in Resources */ = {isa = PBXBuildFile; fileRef = 30E1352610E2C1420031B30D /* Cordova.plist */; }; + 30E5649213A7FCAF007403D8 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30E5649113A7FCAF007403D8 /* CoreMedia.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + A93E85C516034FEE0004D29A /* WTWikitudePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A93E85C416034FEE0004D29A /* WTWikitudePlugin.m */; }; + A93E85F7160351690004D29A /* libQCAR.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85E9160351690004D29A /* libQCAR.a */; }; + A93E85F8160351690004D29A /* libExtensionVuforia.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85EE160351690004D29A /* libExtensionVuforia.a */; }; + A93E85F9160351690004D29A /* libWikitudeSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85F4160351690004D29A /* libWikitudeSDK.a */; }; + A93E85FA1603516A0004D29A /* libWikitudeSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85F6160351690004D29A /* libWikitudeSDK.a */; }; + A93E8601160352820004D29A /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85FB160352820004D29A /* CoreMotion.framework */; }; + A93E8602160352820004D29A /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85FC160352820004D29A /* CoreVideo.framework */; }; + A93E8603160352820004D29A /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85FD160352820004D29A /* libsqlite3.dylib */; }; + A93E8604160352820004D29A /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85FE160352820004D29A /* libz.dylib */; }; + A93E8605160352820004D29A /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E85FF160352820004D29A /* OpenGLES.framework */; }; + A93E8606160352820004D29A /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A93E8600160352820004D29A /* Security.framework */; }; + A93E8608160352BE0004D29A /* assets in Resources */ = {isa = PBXBuildFile; fileRef = A93E8607160352BE0004D29A /* assets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 301BF534109A57CC0062928A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D2AAC07E0554694100DB518D; + remoteInfo = CordovaLib; + }; + 301BF550109A68C00062928A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D2AAC07D0554694100DB518D; + remoteInfo = CordovaLib; + }; + 302D95EB14D23909003F00A1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 686357A9141002F100DF4CF2; + remoteInfo = CordovaLibTests; + }; + 3088BBB4154F38EF009F9C59 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 303A4068152124BB00182201; + remoteInfo = CordovaLibApp; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 1D6058910D05DD3D006BFB54 /* HelloImageRecognition.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloImageRecognition.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 1F766FDD13BBADB100FB74C0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = ""; }; + 1F766FE013BBADB100FB74C0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Localizable.strings; sourceTree = ""; }; + 288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = CordovaLib.xcodeproj; sourceTree = CORDOVALIB; }; + 301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; }; + 301BF5B4109A6A2B0062928A /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; + 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; + 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 301BF5BA109A6A2B0062928A /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 301BF5BC109A6A2B0062928A /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; + 301BF5BE109A6A2B0062928A /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; + 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + 301BF5C2109A6A2B0062928A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + 302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; + 302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + 302D95F014D2391D003F00A1 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = ""; }; + 3053AC6E109B7857006FCFE7 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = CORDOVALIB; }; + 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; + 3072F99613A8081B00425683 /* Capture.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Capture.bundle; sourceTree = ""; }; + 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = CordovaBuildSettings.xcconfig; path = ../CordovaBuildSettings.xcconfig; sourceTree = ""; }; + 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x~ipad.png"; sourceTree = ""; }; + 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = ""; }; + 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = ""; }; + 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = ""; }; + 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x~iphone.png"; sourceTree = ""; }; + 3088BBBC154F3926009F9C59 /* Default~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~iphone.png"; sourceTree = ""; }; + 308D052E1370CCF300D202BF /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = ""; }; + 308D052F1370CCF300D202BF /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = ""; }; + 308D05301370CCF300D202BF /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = ""; }; + 30A0434414DC770100060A13 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Localizable.strings; sourceTree = ""; }; + 30A0434714DC770100060A13 /* se */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = se; path = Localizable.strings; sourceTree = ""; }; + 30E1352610E2C1420031B30D /* Cordova.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Cordova.plist; path = ../Cordova.plist; sourceTree = ""; }; + 30E5649113A7FCAF007403D8 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + 32CA4F630368D1EE00C91783 /* HelloImageRecognition-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HelloImageRecognition-Prefix.pch"; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* HelloImageRecognition-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "HelloImageRecognition-Info.plist"; path = "../HelloImageRecognition-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; + A93E85C316034FEE0004D29A /* WTWikitudePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WTWikitudePlugin.h; sourceTree = ""; }; + A93E85C416034FEE0004D29A /* WTWikitudePlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WTWikitudePlugin.m; sourceTree = ""; }; + A93E85C9160351690004D29A /* Area.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Area.h; sourceTree = ""; }; + A93E85CA160351690004D29A /* CameraCalibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraCalibration.h; sourceTree = ""; }; + A93E85CB160351690004D29A /* CameraDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CameraDevice.h; sourceTree = ""; }; + A93E85CC160351690004D29A /* DataSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSet.h; sourceTree = ""; }; + A93E85CD160351690004D29A /* Frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Frame.h; sourceTree = ""; }; + A93E85CE160351690004D29A /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = ""; }; + A93E85CF160351690004D29A /* ImageTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageTarget.h; sourceTree = ""; }; + A93E85D0160351690004D29A /* ImageTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageTracker.h; sourceTree = ""; }; + A93E85D1160351690004D29A /* Marker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Marker.h; sourceTree = ""; }; + A93E85D2160351690004D29A /* MarkerTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkerTracker.h; sourceTree = ""; }; + A93E85D3160351690004D29A /* Matrices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Matrices.h; sourceTree = ""; }; + A93E85D4160351690004D29A /* MultiTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiTarget.h; sourceTree = ""; }; + A93E85D5160351690004D29A /* NonCopyable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NonCopyable.h; sourceTree = ""; }; + A93E85D6160351690004D29A /* QCAR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QCAR.h; sourceTree = ""; }; + A93E85D7160351690004D29A /* QCAR_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QCAR_iOS.h; sourceTree = ""; }; + A93E85D8160351690004D29A /* Rectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Rectangle.h; sourceTree = ""; }; + A93E85D9160351690004D29A /* Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Renderer.h; sourceTree = ""; }; + A93E85DA160351690004D29A /* State.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = State.h; sourceTree = ""; }; + A93E85DB160351690004D29A /* System.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = System.h; sourceTree = ""; }; + A93E85DC160351690004D29A /* Tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tool.h; sourceTree = ""; }; + A93E85DD160351690004D29A /* Trackable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Trackable.h; sourceTree = ""; }; + A93E85DE160351690004D29A /* Tracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tracker.h; sourceTree = ""; }; + A93E85DF160351690004D29A /* TrackerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackerManager.h; sourceTree = ""; }; + A93E85E0160351690004D29A /* UIGLViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIGLViewProtocol.h; sourceTree = ""; }; + A93E85E1160351690004D29A /* UpdateCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdateCallback.h; sourceTree = ""; }; + A93E85E2160351690004D29A /* Vectors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Vectors.h; sourceTree = ""; }; + A93E85E3160351690004D29A /* VideoBackgroundConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoBackgroundConfig.h; sourceTree = ""; }; + A93E85E4160351690004D29A /* VideoBackgroundTextureInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoBackgroundTextureInfo.h; sourceTree = ""; }; + A93E85E5160351690004D29A /* VideoMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoMode.h; sourceTree = ""; }; + A93E85E6160351690004D29A /* VirtualButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VirtualButton.h; sourceTree = ""; }; + A93E85E9160351690004D29A /* libQCAR.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libQCAR.a; sourceTree = ""; }; + A93E85EE160351690004D29A /* libExtensionVuforia.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libExtensionVuforia.a; sourceTree = ""; }; + A93E85F1160351690004D29A /* WTArchitectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WTArchitectView.h; sourceTree = ""; }; + A93E85F4160351690004D29A /* libWikitudeSDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libWikitudeSDK.a; sourceTree = ""; }; + A93E85F6160351690004D29A /* libWikitudeSDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libWikitudeSDK.a; sourceTree = ""; }; + A93E85FB160352820004D29A /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; }; + A93E85FC160352820004D29A /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + A93E85FD160352820004D29A /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + A93E85FE160352820004D29A /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + A93E85FF160352820004D29A /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + A93E8600160352820004D29A /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + A93E8607160352BE0004D29A /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A93E8601160352820004D29A /* CoreMotion.framework in Frameworks */, + A93E8602160352820004D29A /* CoreVideo.framework in Frameworks */, + A93E8603160352820004D29A /* libsqlite3.dylib in Frameworks */, + A93E8604160352820004D29A /* libz.dylib in Frameworks */, + A93E8605160352820004D29A /* OpenGLES.framework in Frameworks */, + A93E8606160352820004D29A /* Security.framework in Frameworks */, + 301BF552109A68D80062928A /* libCordova.a in Frameworks */, + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */, + 301BF5B5109A6A2B0062928A /* AddressBook.framework in Frameworks */, + 301BF5B7109A6A2B0062928A /* AddressBookUI.framework in Frameworks */, + 301BF5B9109A6A2B0062928A /* AudioToolbox.framework in Frameworks */, + 301BF5BB109A6A2B0062928A /* AVFoundation.framework in Frameworks */, + 301BF5BD109A6A2B0062928A /* CFNetwork.framework in Frameworks */, + 301BF5BF109A6A2B0062928A /* CoreLocation.framework in Frameworks */, + 301BF5C1109A6A2B0062928A /* MediaPlayer.framework in Frameworks */, + 301BF5C3109A6A2B0062928A /* QuartzCore.framework in Frameworks */, + 301BF5C5109A6A2B0062928A /* SystemConfiguration.framework in Frameworks */, + 305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */, + 30E5649213A7FCAF007403D8 /* CoreMedia.framework in Frameworks */, + A93E85F7160351690004D29A /* libQCAR.a in Frameworks */, + A93E85F8160351690004D29A /* libExtensionVuforia.a in Frameworks */, + A93E85F9160351690004D29A /* libWikitudeSDK.a in Frameworks */, + A93E85FA1603516A0004D29A /* libWikitudeSDK.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 302D95EE14D2391D003F00A1 /* MainViewController.h */, + 302D95EF14D2391D003F00A1 /* MainViewController.m */, + 302D95F014D2391D003F00A1 /* MainViewController.xib */, + 1D3623240D0F684500981E51 /* AppDelegate.h */, + 1D3623250D0F684500981E51 /* AppDelegate.m */, + ); + name = Classes; + path = HelloImageRecognition/Classes; + sourceTree = SOURCE_ROOT; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* HelloImageRecognition.app */, + ); + name = Products; + sourceTree = ""; + }; + 1F766FDB13BBADB100FB74C0 /* en.lproj */ = { + isa = PBXGroup; + children = ( + 1F766FDC13BBADB100FB74C0 /* Localizable.strings */, + ); + path = en.lproj; + sourceTree = ""; + }; + 1F766FDE13BBADB100FB74C0 /* es.lproj */ = { + isa = PBXGroup; + children = ( + 1F766FDF13BBADB100FB74C0 /* Localizable.strings */, + ); + path = es.lproj; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + A93E8607160352BE0004D29A /* assets */, + 301BF56E109A69640062928A /* www */, + 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */, + A93E85C6160351690004D29A /* Vuforia */, + A93E85EA160351690004D29A /* WikitudeSDK */, + 080E96DDFE201D6D7F000001 /* Classes */, + 307C750510C5A3420062BCA9 /* Plugins */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* HelloImageRecognition-Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + path = HelloImageRecognition; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 30A0434214DC770100060A13 /* de.lproj */, + 30A0434514DC770100060A13 /* se.lproj */, + 1F766FDB13BBADB100FB74C0 /* en.lproj */, + 1F766FDE13BBADB100FB74C0 /* es.lproj */, + 3072F99613A8081B00425683 /* Capture.bundle */, + 308D052D1370CCF300D202BF /* icons */, + 308D05311370CCF300D202BF /* splash */, + 30E1352610E2C1420031B30D /* Cordova.plist */, + 3053AC6E109B7857006FCFE7 /* VERSION */, + 8D1107310486CEB800E47090 /* HelloImageRecognition-Info.plist */, + 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */, + ); + name = Resources; + path = HelloImageRecognition/Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + A93E85FB160352820004D29A /* CoreMotion.framework */, + A93E85FC160352820004D29A /* CoreVideo.framework */, + A93E85FD160352820004D29A /* libsqlite3.dylib */, + A93E85FE160352820004D29A /* libz.dylib */, + A93E85FF160352820004D29A /* OpenGLES.framework */, + A93E8600160352820004D29A /* Security.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + 288765FC0DF74451002DB57D /* CoreGraphics.framework */, + 301BF5B4109A6A2B0062928A /* AddressBook.framework */, + 301BF5B6109A6A2B0062928A /* AddressBookUI.framework */, + 301BF5B8109A6A2B0062928A /* AudioToolbox.framework */, + 301BF5BA109A6A2B0062928A /* AVFoundation.framework */, + 301BF5BC109A6A2B0062928A /* CFNetwork.framework */, + 301BF5BE109A6A2B0062928A /* CoreLocation.framework */, + 301BF5C0109A6A2B0062928A /* MediaPlayer.framework */, + 301BF5C2109A6A2B0062928A /* QuartzCore.framework */, + 301BF5C4109A6A2B0062928A /* SystemConfiguration.framework */, + 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */, + 30E5649113A7FCAF007403D8 /* CoreMedia.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 301BF52E109A57CC0062928A /* Products */ = { + isa = PBXGroup; + children = ( + 301BF535109A57CC0062928A /* libCordova.a */, + 302D95EC14D23909003F00A1 /* CordovaLibTests.octest */, + 3088BBB5154F38EF009F9C59 /* CordovaLibApp.app */, + ); + name = Products; + sourceTree = ""; + }; + 307C750510C5A3420062BCA9 /* Plugins */ = { + isa = PBXGroup; + children = ( + A93E85C216034FEE0004D29A /* Wikitude */, + ); + name = Plugins; + path = HelloImageRecognition/Plugins; + sourceTree = SOURCE_ROOT; + }; + 308D052D1370CCF300D202BF /* icons */ = { + isa = PBXGroup; + children = ( + 308D052E1370CCF300D202BF /* icon-72.png */, + 308D052F1370CCF300D202BF /* icon.png */, + 308D05301370CCF300D202BF /* icon@2x.png */, + ); + path = icons; + sourceTree = ""; + }; + 308D05311370CCF300D202BF /* splash */ = { + isa = PBXGroup; + children = ( + 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */, + 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */, + 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */, + 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */, + 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */, + 3088BBBC154F3926009F9C59 /* Default~iphone.png */, + ); + path = splash; + sourceTree = ""; + }; + 30A0434214DC770100060A13 /* de.lproj */ = { + isa = PBXGroup; + children = ( + 30A0434314DC770100060A13 /* Localizable.strings */, + ); + path = de.lproj; + sourceTree = ""; + }; + 30A0434514DC770100060A13 /* se.lproj */ = { + isa = PBXGroup; + children = ( + 30A0434614DC770100060A13 /* Localizable.strings */, + ); + path = se.lproj; + sourceTree = ""; + }; + A93E85C216034FEE0004D29A /* Wikitude */ = { + isa = PBXGroup; + children = ( + A93E85C316034FEE0004D29A /* WTWikitudePlugin.h */, + A93E85C416034FEE0004D29A /* WTWikitudePlugin.m */, + ); + path = Wikitude; + sourceTree = ""; + }; + A93E85C6160351690004D29A /* Vuforia */ = { + isa = PBXGroup; + children = ( + A93E85C7160351690004D29A /* include */, + A93E85E7160351690004D29A /* lib */, + ); + name = Vuforia; + path = HelloImageRecognition/Vuforia; + sourceTree = ""; + }; + A93E85C7160351690004D29A /* include */ = { + isa = PBXGroup; + children = ( + A93E85C8160351690004D29A /* QCAR */, + ); + path = include; + sourceTree = ""; + }; + A93E85C8160351690004D29A /* QCAR */ = { + isa = PBXGroup; + children = ( + A93E85C9160351690004D29A /* Area.h */, + A93E85CA160351690004D29A /* CameraCalibration.h */, + A93E85CB160351690004D29A /* CameraDevice.h */, + A93E85CC160351690004D29A /* DataSet.h */, + A93E85CD160351690004D29A /* Frame.h */, + A93E85CE160351690004D29A /* Image.h */, + A93E85CF160351690004D29A /* ImageTarget.h */, + A93E85D0160351690004D29A /* ImageTracker.h */, + A93E85D1160351690004D29A /* Marker.h */, + A93E85D2160351690004D29A /* MarkerTracker.h */, + A93E85D3160351690004D29A /* Matrices.h */, + A93E85D4160351690004D29A /* MultiTarget.h */, + A93E85D5160351690004D29A /* NonCopyable.h */, + A93E85D6160351690004D29A /* QCAR.h */, + A93E85D7160351690004D29A /* QCAR_iOS.h */, + A93E85D8160351690004D29A /* Rectangle.h */, + A93E85D9160351690004D29A /* Renderer.h */, + A93E85DA160351690004D29A /* State.h */, + A93E85DB160351690004D29A /* System.h */, + A93E85DC160351690004D29A /* Tool.h */, + A93E85DD160351690004D29A /* Trackable.h */, + A93E85DE160351690004D29A /* Tracker.h */, + A93E85DF160351690004D29A /* TrackerManager.h */, + A93E85E0160351690004D29A /* UIGLViewProtocol.h */, + A93E85E1160351690004D29A /* UpdateCallback.h */, + A93E85E2160351690004D29A /* Vectors.h */, + A93E85E3160351690004D29A /* VideoBackgroundConfig.h */, + A93E85E4160351690004D29A /* VideoBackgroundTextureInfo.h */, + A93E85E5160351690004D29A /* VideoMode.h */, + A93E85E6160351690004D29A /* VirtualButton.h */, + ); + path = QCAR; + sourceTree = ""; + }; + A93E85E7160351690004D29A /* lib */ = { + isa = PBXGroup; + children = ( + A93E85E8160351690004D29A /* arm */, + ); + path = lib; + sourceTree = ""; + }; + A93E85E8160351690004D29A /* arm */ = { + isa = PBXGroup; + children = ( + A93E85E9160351690004D29A /* libQCAR.a */, + ); + path = arm; + sourceTree = ""; + }; + A93E85EA160351690004D29A /* WikitudeSDK */ = { + isa = PBXGroup; + children = ( + A93E85EB160351690004D29A /* Extensions */, + A93E85EF160351690004D29A /* SDK */, + ); + name = WikitudeSDK; + path = HelloImageRecognition/WikitudeSDK; + sourceTree = ""; + }; + A93E85EB160351690004D29A /* Extensions */ = { + isa = PBXGroup; + children = ( + A93E85EC160351690004D29A /* VuforiaExtension */, + ); + path = Extensions; + sourceTree = ""; + }; + A93E85EC160351690004D29A /* VuforiaExtension */ = { + isa = PBXGroup; + children = ( + A93E85ED160351690004D29A /* lib */, + ); + path = VuforiaExtension; + sourceTree = ""; + }; + A93E85ED160351690004D29A /* lib */ = { + isa = PBXGroup; + children = ( + A93E85EE160351690004D29A /* libExtensionVuforia.a */, + ); + path = lib; + sourceTree = ""; + }; + A93E85EF160351690004D29A /* SDK */ = { + isa = PBXGroup; + children = ( + A93E85F0160351690004D29A /* inc */, + A93E85F2160351690004D29A /* lib */, + ); + path = SDK; + sourceTree = ""; + }; + A93E85F0160351690004D29A /* inc */ = { + isa = PBXGroup; + children = ( + A93E85F1160351690004D29A /* WTArchitectView.h */, + ); + path = inc; + sourceTree = ""; + }; + A93E85F2160351690004D29A /* lib */ = { + isa = PBXGroup; + children = ( + A93E85F3160351690004D29A /* Release-iphoneos */, + A93E85F5160351690004D29A /* Release-iphonesimulator */, + ); + path = lib; + sourceTree = ""; + }; + A93E85F3160351690004D29A /* Release-iphoneos */ = { + isa = PBXGroup; + children = ( + A93E85F4160351690004D29A /* libWikitudeSDK.a */, + ); + path = "Release-iphoneos"; + sourceTree = ""; + }; + A93E85F5160351690004D29A /* Release-iphonesimulator */ = { + isa = PBXGroup; + children = ( + A93E85F6160351690004D29A /* libWikitudeSDK.a */, + ); + path = "Release-iphonesimulator"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* HelloImageRecognition */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloImageRecognition" */; + buildPhases = ( + 304B58A110DAC018002A0835 /* Touch www folder */, + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 301BF551109A68C00062928A /* PBXTargetDependency */, + ); + name = HelloImageRecognition; + productName = HelloImageRecognition; + productReference = 1D6058910D05DD3D006BFB54 /* HelloImageRecognition.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0420; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HelloImageRecognition" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + en, + es, + de, + se, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 301BF52E109A57CC0062928A /* Products */; + ProjectRef = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* HelloImageRecognition */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 301BF535109A57CC0062928A /* libCordova.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libCordova.a; + remoteRef = 301BF534109A57CC0062928A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 302D95EC14D23909003F00A1 /* CordovaLibTests.octest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CordovaLibTests.octest; + remoteRef = 302D95EB14D23909003F00A1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3088BBB5154F38EF009F9C59 /* CordovaLibApp.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = CordovaLibApp.app; + remoteRef = 3088BBB4154F38EF009F9C59 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 301BF570109A69640062928A /* www in Resources */, + 3053AC6F109B7857006FCFE7 /* VERSION in Resources */, + 30E1352710E2C1420031B30D /* Cordova.plist in Resources */, + 308D05371370CCF300D202BF /* icon-72.png in Resources */, + 308D05381370CCF300D202BF /* icon.png in Resources */, + 308D05391370CCF300D202BF /* icon@2x.png in Resources */, + 3072F99713A8081B00425683 /* Capture.bundle in Resources */, + 1F766FE113BBADB100FB74C0 /* Localizable.strings in Resources */, + 1F766FE213BBADB100FB74C0 /* Localizable.strings in Resources */, + 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */, + 30A0434814DC770100060A13 /* Localizable.strings in Resources */, + 30A0434914DC770100060A13 /* Localizable.strings in Resources */, + 3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */, + 3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */, + 3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */, + 3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */, + 3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */, + 3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */, + A93E8608160352BE0004D29A /* assets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 304B58A110DAC018002A0835 /* Touch www folder */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Touch www folder"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "touch -cm ${PROJECT_DIR}/www"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */, + 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */, + A93E85C516034FEE0004D29A /* WTWikitudePlugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 301BF551109A68C00062928A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CordovaLib; + targetProxy = 301BF550109A68C00062928A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 1F766FDC13BBADB100FB74C0 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 1F766FDD13BBADB100FB74C0 /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 1F766FDF13BBADB100FB74C0 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 1F766FE013BBADB100FB74C0 /* es */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 30A0434314DC770100060A13 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 30A0434414DC770100060A13 /* de */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 30A0434614DC770100060A13 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 30A0434714DC770100060A13 /* se */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HelloImageRecognition/HelloImageRecognition-Prefix.pch"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INFOPLIST_FILE = "HelloImageRecognition/HelloImageRecognition-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/HelloImageRecognition/Vuforia/lib/arm\"", + "\"$(SRCROOT)/HelloImageRecognition/WikitudeSDK/Extensions/VuforiaExtension/lib\"", + "\"$(SRCROOT)/HelloImageRecognition/WikitudeSDK/SDK/lib/Release$(EFFECTIVE_PLATFORM_NAME)\"", + ); + PRODUCT_NAME = HelloImageRecognition; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "HelloImageRecognition/HelloImageRecognition-Prefix.pch"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + INFOPLIST_FILE = "HelloImageRecognition/HelloImageRecognition-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/HelloImageRecognition/Vuforia/lib/arm\"", + "\"$(SRCROOT)/HelloImageRecognition/WikitudeSDK/Extensions/VuforiaExtension/lib\"", + "\"$(SRCROOT)/HelloImageRecognition/WikitudeSDK/SDK/lib/Release$(EFFECTIVE_PLATFORM_NAME)\"", + ); + PRODUCT_NAME = HelloImageRecognition; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"", + "\"$(OBJROOT)/UninstalledProducts/include\"", + "\"$(BUILT_PRODUCTS_DIR)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + OTHER_LDFLAGS = ( + "-weak_framework", + CoreFoundation, + "-weak_framework", + UIKit, + "-weak_framework", + AVFoundation, + "-weak_framework", + CoreMedia, + "-weak-lSystem", + "-force_load", + "$(BUILT_PRODUCTS_DIR)/libCordova.a", + "-ObjC", + "-lstdc++", + ); + SDKROOT = iphoneos; + SKIP_INSTALL = NO; + USER_HEADER_SEARCH_PATHS = ""; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 307D28A1123043350040C0FA /* CordovaBuildSettings.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"", + "\"$(OBJROOT)/UninstalledProducts/include\"", + "\"$(BUILT_PRODUCTS_DIR)\"", + ); + IPHONEOS_DEPLOYMENT_TARGET = 4.2; + OTHER_LDFLAGS = ( + "-weak_framework", + CoreFoundation, + "-weak_framework", + UIKit, + "-weak_framework", + AVFoundation, + "-weak_framework", + CoreMedia, + "-weak-lSystem", + "-force_load", + "$(BUILT_PRODUCTS_DIR)/libCordova.a", + "-ObjC", + "-lstdc++", + ); + SDKROOT = iphoneos; + SKIP_INSTALL = NO; + USER_HEADER_SEARCH_PATHS = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloImageRecognition" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HelloImageRecognition" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..5ee0a53 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.xcworkspace/xcuserdata/Andi.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.xcworkspace/xcuserdata/Andi.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..348184b Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/project.xcworkspace/xcuserdata/Andi.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/HelloImageRecognition.xcscheme b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/HelloImageRecognition.xcscheme new file mode 100644 index 0000000..363f631 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/HelloImageRecognition.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/xcuserdata/randymcmillan.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 83% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/xcuserdata/randymcmillan.xcuserdatad/xcschemes/xcschememanagement.plist rename to iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/xcschememanagement.plist index 767b871..b074716 100644 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/xcuserdata/randymcmillan.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition.xcodeproj/xcuserdata/Andi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,7 +4,7 @@ SchemeUserState - PayPalExampleCDV.xcscheme + HelloImageRecognition.xcscheme orderHint 0 @@ -12,7 +12,7 @@ SuppressBuildableAutocreation - 48BA2DC715129ACE00F58A27 + 1D6058900D05DD3D006BFB54 primary diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/.gitignore b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/.gitignore new file mode 100644 index 0000000..d30c0b0 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/.gitignore @@ -0,0 +1,6 @@ +*.mode1v3 +*.perspectivev3 +*.pbxuser +.DS_Store +build +www/phonegap.js diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/AppDelegate.h b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/AppDelegate.h new file mode 100644 index 0000000..6f0c82f --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/AppDelegate.h @@ -0,0 +1,45 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// +// AppDelegate.h +// HelloImageRecognition +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import + +#import + +@interface AppDelegate : NSObject < UIApplicationDelegate > { + +} + +// invoke string is passed to your app on launch, this is only valid if you +// edit HelloImageRecognition-Info.plist to add a protocol +// a simple tutorial can be found here : +// http://iphonedevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html + +@property (nonatomic, retain) IBOutlet UIWindow* window; +@property (nonatomic, retain) IBOutlet CDVViewController* viewController; + +@end + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/AppDelegate.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/AppDelegate.m new file mode 100644 index 0000000..0cfe1fd --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/AppDelegate.m @@ -0,0 +1,135 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// +// AppDelegate.m +// HelloImageRecognition +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import "AppDelegate.h" +#import "MainViewController.h" + +#import +#import + + +@implementation AppDelegate + +@synthesize window, viewController; + +- (id) init +{ + /** If you need to do any extra app-specific initialization, you can do it here + * -jm + **/ + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; + + [CDVURLProtocol registerURLProtocol]; + + return [super init]; +} + +#pragma UIApplicationDelegate implementation + +/** + * This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up) + */ +- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions +{ + NSURL* url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey]; + NSString* invokeString = nil; + + if (url && [url isKindOfClass:[NSURL class]]) { + invokeString = [url absoluteString]; + NSLog(@"HelloImageRecognition launchOptions = %@", url); + } + + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease]; + self.window.autoresizesSubviews = YES; + + CGRect viewBounds = [[UIScreen mainScreen] applicationFrame]; + + self.viewController = [[[MainViewController alloc] init] autorelease]; + self.viewController.useSplashScreen = YES; + self.viewController.wwwFolderName = @"www"; + self.viewController.startPage = @"index.html"; + self.viewController.invokeString = invokeString; + self.viewController.view.frame = viewBounds; + + // check whether the current orientation is supported: if it is, keep it, rather than forcing a rotation + BOOL forceStartupRotation = YES; + UIDeviceOrientation curDevOrientation = [[UIDevice currentDevice] orientation]; + + if (UIDeviceOrientationUnknown == curDevOrientation) { + // UIDevice isn't firing orientation notifications yet… go look at the status bar + curDevOrientation = (UIDeviceOrientation)[[UIApplication sharedApplication] statusBarOrientation]; + } + + if (UIDeviceOrientationIsValidInterfaceOrientation(curDevOrientation)) { + for (NSNumber *orient in self.viewController.supportedOrientations) { + if ([orient intValue] == curDevOrientation) { + forceStartupRotation = NO; + break; + } + } + } + + if (forceStartupRotation) { + NSLog(@"supportedOrientations: %@", self.viewController.supportedOrientations); + // The first item in the supportedOrientations array is the start orientation (guaranteed to be at least Portrait) + UIInterfaceOrientation newOrient = [[self.viewController.supportedOrientations objectAtIndex:0] intValue]; + NSLog(@"AppDelegate forcing status bar to: %d from: %d", newOrient, curDevOrientation); + [[UIApplication sharedApplication] setStatusBarOrientation:newOrient]; + } + + [self.window addSubview:self.viewController.view]; + [self.window makeKeyAndVisible]; + + return YES; +} + +// this happens while we are running ( in the background, or from within our own app ) +// only valid if HelloImageRecognition-Info.plist specifies a protocol to handle +- (BOOL) application:(UIApplication*)application handleOpenURL:(NSURL*)url +{ + if (!url) { + return NO; + } + + // calls into javascript global function 'handleOpenURL' + NSString* jsString = [NSString stringWithFormat:@"handleOpenURL(\"%@\");", url]; + [self.viewController.webView stringByEvaluatingJavaScriptFromString:jsString]; + + // all plugins will get the notification, and their handlers will be called + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + + return YES; +} + +- (void) dealloc +{ + [super dealloc]; +} + +@end diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.h b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.h new file mode 100644 index 0000000..d7bebc1 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.h @@ -0,0 +1,32 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// +// MainViewController.h +// HelloImageRecognition +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import + +@interface MainViewController : CDVViewController + +@end diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.m new file mode 100644 index 0000000..64c9bcb --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.m @@ -0,0 +1,141 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// +// MainViewController.h +// HelloImageRecognition +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import "MainViewController.h" + +@implementation MainViewController + +- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void) didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void) viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void) viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; +} + +/* Comment out the block below to over-ride */ +/* +- (CDVCordovaView*) newCordovaViewWithFrame:(CGRect)bounds +{ + return[super newCordovaViewWithFrame:bounds]; +} +*/ + +/* Comment out the block below to over-ride */ +/* +#pragma CDVCommandDelegate implementation + +- (id) getCommandInstance:(NSString*)className +{ + return [super getCommandInstance:className]; +} + +- (BOOL) execute:(CDVInvokedUrlCommand*)command +{ + return [super execute:command]; +} + +- (NSString*) pathForResource:(NSString*)resourcepath; +{ + return [super pathForResource:resourcepath]; +} + +- (void) registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className +{ + return [super registerPlugin:plugin withClassName:className]; +} +*/ + +#pragma UIWebDelegate implementation + +- (void) webViewDidFinishLoad:(UIWebView*) theWebView +{ + // only valid if ___PROJECTNAME__-Info.plist specifies a protocol to handle + if (self.invokeString) + { + // this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready + NSLog(@"DEPRECATED: window.invokeString - use the window.handleOpenURL(url) function instead, which is always called when the app is launched through a custom scheme url."); + NSString* jsString = [NSString stringWithFormat:@"var invokeString = \"%@\";", self.invokeString]; + [theWebView stringByEvaluatingJavaScriptFromString:jsString]; + } + + // Black base color for background matches the native apps + theWebView.backgroundColor = [UIColor blackColor]; + + return [super webViewDidFinishLoad:theWebView]; +} + +/* Comment out the block below to over-ride */ +/* + +- (void) webViewDidStartLoad:(UIWebView*)theWebView +{ + return [super webViewDidStartLoad:theWebView]; +} + +- (void) webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + return [super webView:theWebView didFailLoadWithError:error]; +} + +- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + return [super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; +} +*/ + +@end diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.xib b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.xib new file mode 100644 index 0000000..e45d65c --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Classes/MainViewController.xib @@ -0,0 +1,138 @@ + + + + + 1280 + 11C25 + 1919 + 1138.11 + 566.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 916 + + + IBProxyObject + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + {{0, 20}, {320, 460}} + + + + 3 + MQA + + 2 + + + + IBCocoaTouchFramework + + + + + + + view + + + + 3 + + + + + + 0 + + + + + + 1 + + + + + -1 + + + File's Owner + + + -2 + + + + + + + MainViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 3 + + + + + MainViewController + UIViewController + + IBProjectSource + ./Classes/MainViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 916 + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Cordova.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Cordova.plist new file mode 100644 index 0000000..01a59d6 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Cordova.plist @@ -0,0 +1,67 @@ + + + + + UIWebViewBounce + + TopActivityIndicator + gray + EnableLocation + + EnableViewportScale + + AutoHideSplashScreen + + ShowSplashScreenSpinner + + MediaPlaybackRequiresUserAction + + AllowInlineMediaPlayback + + OpenAllWhitelistURLsInWebView + + BackupWebStorage + + ExternalHosts + + * + + Plugins + + WikitudePlugin + WTWikitudePlugin + Device + CDVDevice + Logger + CDVLogger + Compass + CDVLocation + Accelerometer + CDVAccelerometer + Camera + CDVCamera + NetworkStatus + CDVConnection + Contacts + CDVContacts + Debug Console + CDVDebugConsole + File + CDVFile + FileTransfer + CDVFileTransfer + Geolocation + CDVLocation + Notification + CDVNotification + Media + CDVSound + Capture + CDVCapture + SplashScreen + CDVSplashScreen + Battery + CDVBattery + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/CordovaBuildSettings.xcconfig b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/CordovaBuildSettings.xcconfig new file mode 100644 index 0000000..c70086c --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/CordovaBuildSettings.xcconfig @@ -0,0 +1,26 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. +// + +// Cordova.xcconfig +// __TESTING__ +// + +// Override this to use your project specific CORDOVALIB. +// You can base it off the current project path, $(PROJECT_DIR) +CORDOVALIB = $(CORDOVALIB) diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/HelloImageRecognition-Info.plist b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/HelloImageRecognition-Info.plist new file mode 100644 index 0000000..1f62951 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/HelloImageRecognition-Info.plist @@ -0,0 +1,58 @@ + + + + + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + icon.png + icon@2x.png + icon-72.png + icon-72@2x.png + + UIPrerenderedIcon + + + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + icon.png + CFBundleIdentifier + com.wikitude.phonegapsamplehelloimagerecognition + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSMainNibFile + + NSMainNibFile~ipad + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/HelloImageRecognition-Prefix.pch b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/HelloImageRecognition-Prefix.pch new file mode 100644 index 0000000..b4aef82 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/HelloImageRecognition-Prefix.pch @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ +// +// Prefix header for all source files of the 'HelloImageRecognition' target in the 'HelloImageRecognition' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/README b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/README new file mode 100644 index 0000000..f6e19d7 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/README @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder. \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/Wikitude/WTWikitudePlugin.h b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/Wikitude/WTWikitudePlugin.h new file mode 100644 index 0000000..0a71316 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/Wikitude/WTWikitudePlugin.h @@ -0,0 +1,14 @@ +// +// WTWikitudeSDK.h +// HelloWorld +// +// Created by Andreas Schacherbauer on 8/24/12. +// +// + + +#import + +@interface WTWikitudePlugin : CDVPlugin + +@end diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/Wikitude/WTWikitudePlugin.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/Wikitude/WTWikitudePlugin.m new file mode 100644 index 0000000..3e54f35 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Plugins/Wikitude/WTWikitudePlugin.m @@ -0,0 +1,402 @@ +// +// WTWikitudeSDK.m +// HelloWorld +// +// Created by Andreas Schacherbauer on 8/24/12. +// +// + +#import "WTWikitudePlugin.h" + +// Wikitude SDK +#import "WTArchitectView.h" + + + + +@interface WTWikitudePlugin () + +@property (nonatomic, strong) WTArchitectView *architectView; + +@property (nonatomic, strong) NSString *currentARchitectViewCallbackID; +@property (nonatomic, strong) NSString *currentPlugInErrorCallback; + +@property (nonatomic, assign) BOOL isUsingInjectedLocation; + +@end + + +@implementation WTWikitudePlugin +@synthesize architectView=_architectView; + + +#pragma mark - View Lifecycle +/* View Lifecycle */ +- (void)isDeviceSupported:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + BOOL isDeviceSupported = [WTArchitectView isDeviceSupported]; + + if (isDeviceSupported) { + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:isDeviceSupported]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + } else { + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:isDeviceSupported]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +- (void)open:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + + BOOL success = [WTArchitectView isDeviceSupported]; + if ( success ) { + + NSString *sdkKey = [options objectForKey:@"apiKey"]; + NSString *architectWorldFilePath = [options objectForKey:@"filePath"]; + + // First, lets check if we need to init a new sdk view + if ( !_architectView ) { + self.architectView = [[WTArchitectView alloc] initWithFrame:self.viewController.view.bounds]; + self.architectView.delegate = self; + [self.architectView initializeWithKey:sdkKey motionManager:nil]; + } + + // then add the view in its own navController to the ui. we need a own navController to have a backButton which can be used to dismiss the view + UIViewController *viewController = [[UIViewController alloc] init]; + viewController.view = self.architectView; + + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController]; + navController.navigationBar.tintColor = [UIColor blackColor]; + navController.navigationBar.topItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissARchitectView)]; + [self.viewController presentViewController:navController animated:NO completion:^{ + // completion code + }]; + + + + // and finaly load the architect world, specified in the open function in js + if (architectWorldFilePath) { + + NSString *worldName = [architectWorldFilePath lastPathComponent]; + worldName = [worldName stringByDeletingPathExtension]; + NSString *worldNameExtension = [architectWorldFilePath pathExtension]; + + NSString *architectWorldDirectoryPath = [architectWorldFilePath stringByDeletingLastPathComponent]; + + NSString *loadablePath = [[NSBundle mainBundle] pathForResource:worldName ofType:worldNameExtension inDirectory:architectWorldDirectoryPath]; + [self.architectView loadArchitectWorldFromUrl:loadablePath]; + } + } + + // start the sdk view updates + [self.architectView start]; + + + if ( success ) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)close:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + + [self.architectView stop]; +// [self.architectView removeFromSuperview]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)show:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { +// NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + self.architectView.hidden = NO; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)hide:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + self.architectView.hidden = YES; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)onResume:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + [self.architectView start]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +- (void)onPause:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + [self.architectView stop]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +#pragma mark - Location Handling + +- (void)setLocation:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + // NSString* echo = [arguments objectAtIndex:1]; + + if (self.architectView) { + + float latitude = [[options objectForKey:@"lat"] floatValue]; + float longitude = [[options objectForKey:@"lon"] floatValue]; + float altitude = [[options objectForKey:@"alt"] floatValue]; + float accuracy = [[options objectForKey:@"acc"] floatValue]; + + if (!self.isUsingInjectedLocation) { + [self.architectView setUseInjectedLocation:YES]; + self.isUsingInjectedLocation = YES; + } + + [self.architectView injectLocationWithLatitude:latitude longitude:longitude altitude:altitude accuracy:accuracy]; + } + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +#pragma mark - Javascript + +- (void)callJavascript:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + + @try { + + if (arguments.count >= 1) { + + NSMutableString *javascriptToCall = [[arguments objectAtIndex:1] mutableCopy]; + for (NSUInteger i = 2; i < arguments.count; i++) { + [javascriptToCall appendString:[arguments objectAtIndex:i]]; + } + + if (self.architectView) { + [self.architectView callJavaScript:javascriptToCall]; + } + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + }else + { + // return error no javascript to call found + } + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + + +- (void)onUrlInvoke:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + CDVPluginResult* pluginResult = nil; + NSString* javaScript = nil; + + @try { + + + self.currentARchitectViewCallbackID = callbackId; + self.currentPlugInErrorCallback = callbackId; + + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT]; + [pluginResult setKeepCallbackAsBool:YES]; + javaScript = [pluginResult toSuccessCallbackString:callbackId]; + + + } @catch (NSException* exception) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:[exception reason]]; + javaScript = [pluginResult toErrorCallbackString:callbackId]; + } + + [self writeJavascript:javaScript]; +} + +#pragma mark - WTArchitectView Delegate +- (void)urlWasInvoked:(NSString *)url +{ + + CDVPluginResult *pluginResult = nil; + NSString *javaScriptResult = nil; + + + if (url && self.currentARchitectViewCallbackID) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:url]; + [pluginResult setKeepCallbackAsBool:YES]; + javaScriptResult = [pluginResult toSuccessCallbackString:self.currentARchitectViewCallbackID]; + + + }else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR]; + javaScriptResult = [pluginResult toSuccessCallbackString:self.currentPlugInErrorCallback]; + } + + [self writeJavascript:javaScriptResult]; +} + +- (void)dismissARchitectView +{ + [self.viewController dismissModalViewControllerAnimated:NO]; + [self.architectView stop]; +} + +@end diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg.png new file mode 100644 index 0000000..784e9c7 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg@2x.png new file mode 100644 index 0000000..1e28c6d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg@2x~ipad.png new file mode 100644 index 0000000..d4e3483 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg~ipad.png new file mode 100644 index 0000000..efbef8a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/controls_bg~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone.png new file mode 100644 index 0000000..155b88c Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone@2x.png new file mode 100644 index 0000000..79ef16b Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone@2x~ipad.png new file mode 100644 index 0000000..af1bbb2 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone~ipad.png new file mode 100644 index 0000000..ef1c472 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/microphone~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button.png new file mode 100644 index 0000000..ceb9589 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button@2x.png new file mode 100644 index 0000000..d6ce302 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button@2x~ipad.png new file mode 100644 index 0000000..0ac2e67 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button~ipad.png new file mode 100644 index 0000000..d8e24a4 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/record_button~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg.png new file mode 100644 index 0000000..bafc087 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg@2x.png new file mode 100644 index 0000000..798490b Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg@2x~ipad.png new file mode 100644 index 0000000..a1b7208 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg~ipad.png new file mode 100644 index 0000000..3b467f6 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/recording_bg~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button.png new file mode 100644 index 0000000..9c31838 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button@2x.png new file mode 100644 index 0000000..8cf657e Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button@2x~ipad.png new file mode 100644 index 0000000..88b606c Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button~ipad.png new file mode 100644 index 0000000..59bb7a5 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/Capture.bundle/stop_button~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/de.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/de.lproj/Localizable.strings new file mode 100644 index 0000000..f1cdb42 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/de.lproj/Localizable.strings @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + + +// accessibility label for recording button +"toggle audio recording" = "starten/beenden der Tonaufnahme"; +// notification spoken by VoiceOver when timed recording finishes +"timed recording complete" = "programmierte Aufnahme beendet"; +// accessibility hint for display of recorded elapsed time +"recorded time in minutes and seconds" = "aufgenommene Zeit in Minuten und Sekunden"; \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/en.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/en.lproj/Localizable.strings new file mode 100644 index 0000000..8972684 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/en.lproj/Localizable.strings @@ -0,0 +1,25 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// accessibility label for recording button +"toggle audio recording" = "toggle audio recording"; +// notification spoken by VoiceOver when timed recording finishes +"timed recording complete" = "timed recording complete"; +// accessibility hint for display of recorded elapsed time +"recorded time in minutes and seconds" = "recorded time in minutes and seconds"; \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/es.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/es.lproj/Localizable.strings new file mode 100644 index 0000000..23831e6 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/es.lproj/Localizable.strings @@ -0,0 +1,25 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +// accessibility label for recording button +"toggle audio recording" = "grabación de audio cambiar"; +// notification spoken by VoiceOver when timed recording finishes +"timed recording complete" = "tiempo de grabación completo"; +// accessibility hint for display of recorded elapsed time +"recorded time in minutes and seconds" = "tiempo registrado en minutos y segundos"; \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon-72.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon-72.png new file mode 100644 index 0000000..8c6e5df Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon-72.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon-72@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon-72@2x.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon-72@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon.png new file mode 100644 index 0000000..b2571a7 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon@2x.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon@2x.png new file mode 100644 index 0000000..d75098f Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/icons/icon@2x.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/se.lproj/Localizable.strings b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/se.lproj/Localizable.strings new file mode 100644 index 0000000..0af9646 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/se.lproj/Localizable.strings @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + + +// accessibility label for recording button +"toggle audio recording" = "börja/avsluta inspelning"; +// notification spoken by VoiceOver when timed recording finishes +"timed recording complete" = "inspelning har avslutad"; +// accessibility hint for display of recorded elapsed time +"recorded time in minutes and seconds" = "inspelad tid in minuter och sekund"; \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Landscape@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Landscape@2x~ipad.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Landscape@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Landscape~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Landscape~ipad.png new file mode 100644 index 0000000..f8e2b52 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Landscape~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Portrait@2x~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Portrait@2x~ipad.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Portrait@2x~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Portrait~ipad.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Portrait~ipad.png new file mode 100644 index 0000000..af9158a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default-Portrait~ipad.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default@2x~iphone.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default@2x~iphone.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default@2x~iphone.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default~iphone.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default~iphone.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/Resources/splash/Default~iphone.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/main.m b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/main.m new file mode 100644 index 0000000..aee71a5 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/HelloImageRecognition/main.m @@ -0,0 +1,35 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ +// +// main.m +// HelloImageRecognition +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import + +int main(int argc, char *argv[]) { + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate"); + [pool release]; + return retVal; +} diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/DirectionArrow.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/DirectionArrow.png new file mode 100644 index 0000000..8a0ccb6 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/DirectionArrow.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/SimpleIRWorld.html b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/SimpleIRWorld.html new file mode 100644 index 0000000..57f4423 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/SimpleIRWorld.html @@ -0,0 +1,116 @@ + + + + + + +Simple IR World + + + + + + + + + + + + + +
Loading ...
+ + + + + \ No newline at end of file diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png new file mode 100644 index 0000000..f820ca0 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip new file mode 100644 index 0000000..34db5dc Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png new file mode 100644 index 0000000..5d2dfcd Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug new file mode 100755 index 0000000..37b0d63 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug @@ -0,0 +1,47 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# +# compile and launch a Cordova/iOS project to the simulator +# + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) +PROJECT_PATH=$CORDOVA_PATH/.. + +for file in $PROJECT_PATH/*.xcodeproj; do + PROJECT_NAME=$(basename "$file" .xcodeproj) +done; + +cd $PROJECT_PATH + +APP=build/$PROJECT_NAME.app +SDK=`xcodebuild -showsdks | grep Sim | tail -1 | awk '{print $6}'` + +xcodebuild -project $PROJECT_NAME.xcodeproj -arch i386 -target $PROJECT_NAME -configuration Debug -sdk $SDK clean build VALID_ARCHS="i386" CONFIGURATION_BUILD_DIR=$PROJECT_PATH/build + +# launch using emulate + +$CORDOVA_PATH/emulate $PROJECT_PATH/$APP + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate new file mode 100755 index 0000000..3822121 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate @@ -0,0 +1,58 @@ +#! /bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +set -e + +CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) +PROJECT_PATH=$CORDOVA_PATH/.. + +function getAppPath() { + for file in $PROJECT_PATH/*.xcodeproj; do + PROJECT_NAME=$(basename "$file" .xcodeproj) + done; + APP=build/$PROJECT_NAME.app + APP_PATH=$PROJECT_PATH/$APP +} + +APP_PATH=$1 + +if [ $# -lt 1 ]; then + getAppPath +fi + +if [ ! -d "$APP_PATH" ]; then + read -p "Project '$APP_PATH' is not built. Build? [y/n]: " REPLY + if [ "$REPLY" == "y" ]; then + $CORDOVA_PATH/debug + exit 0 + else + echo "$APP_PATH not found to emulate." + exit 1 + fi +fi + +# launch using ios-sim + +if which ios-sim >/dev/null; then + ios-sim launch $APP_PATH --stderr console.log --stdout console.log & +else + echo -e '\033[31mError: ios-sim was not found. Please download, build and install version 1.4 or greater from https://github.com/phonegap/ios-sim into your path. Or "brew install ios-sim" using homebrew: http://mxcl.github.com/homebrew/\033[m'; exit 1; +fi + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log new file mode 100755 index 0000000..fd1261c --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log @@ -0,0 +1,26 @@ +#! /bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# +# USAGE +# +# ./log [path] +# +tail -f ${1:-".."}/console.log diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/config.xml b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/config.xml new file mode 100644 index 0000000..a7e35db --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/config.xml @@ -0,0 +1,47 @@ + + + Hello Cordova + + + A sample Apache Cordova application that responds to the deviceready event. + + + + Apache Cordova Team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/cordova-2.0.0.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/cordova-2.0.0.js new file mode 100644 index 0000000..c2caa2f --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/cordova-2.0.0.js @@ -0,0 +1,5240 @@ +// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0 + +// File generated at :: Wed Jul 18 2012 16:47:25 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + */ + fireDocumentEvent: function(type, data) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + // TODO: this is Android only; think about how to do this better + shuttingDown:false, + UsePolling:false, + // END TODO + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'); + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.guid = 1; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; +}; + +module.exports = FileUploadOptions; +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger then file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + me.successCallback(); + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param status The status code (int) + * @param msg The status message (string) + */ +Media.onStatus = function(id, msg, value) { + var media = mediaObjects[id]; + // If state update + if (msg === Media.MEDIA_STATE) { + if (value === Media.MEDIA_STOPPED) { + if (media.successCallback) { + media.successCallback(); + } + } + if (media.statusCallback) { + media.statusCallback(value); + } + } + else if (msg === Media.MEDIA_DURATION) { + media._duration = value; + } + else if (msg === Media.MEDIA_ERROR) { + if (media.errorCallback) { + // value should be a MediaError object when msg == MEDIA_ERROR + media.errorCallback(value); + } + } + else if (msg === Media.MEDIA_POSITION) { + media._position = value; + } +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. + * @constructor + */ +var MediaError = function(code, msg) { + this.code = (code !== undefined ? code : null); + this.message = msg || ""; +}; + +MediaError.MEDIA_ERR_NONE_ACTIVE = 0; +MediaError.MEDIA_ERR_ABORTED = 1; +MediaError.MEDIA_ERR_NETWORK = 2; +MediaError.MEDIA_ERR_DECODE = 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; + +module.exports = MediaError; +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +// TODO: can we axe this? +/** + * Casts a PluginResult message property (array of objects) to an array of MediaFile objects + * (used in Objective-C and Android) + * + * @param {PluginResult} pluginResult + */ +MediaFile.cast = function(pluginResult) { + var mediaFiles = []; + for (var i=0; i.dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retreived a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object who's properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/ios/plugin/ios/Contact.js +define("cordova/plugin/ios/Contact", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'); + +/** + * Provides iOS Contact.display API. + */ +module.exports = { + display : function(errorCB, options) { + /* + * Display a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + */ + + if (this.id === null) { + if (typeof errorCB === "function") { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + } + else { + exec(null, errorCB, "Contacts","displayContact", [this.id, options]); + } + } +}; +}); + +// file: lib/ios/plugin/ios/Entry.js +define("cordova/plugin/ios/Entry", function(require, exports, module) { +module.exports = { + toURL:function() { + // TODO: refactor path in a cross-platform way so we can eliminate + // these kinds of platform-specific hacks. + return "file://localhost" + this.fullPath; + }, + toURI: function() { + console.log("DEPRECATED: Update your code to use 'toURL'"); + return "file://localhost" + this.fullPath; + } +}; +}); + +// file: lib/ios/plugin/ios/FileReader.js +define("cordova/plugin/ios/FileReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + FileReader = require('cordova/plugin/FileReader'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +module.exports = { + readAsText:function(file, encoding) { + // Figure out pathing + this.fileName = ''; + if (typeof file.fullPath === 'undefined') { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + // Default encoding is UTF-8 + var enc = encoding ? encoding : "UTF-8"; + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save result + me.result = decodeURIComponent(r); + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // null result + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + "File", "readAsText", [this.fileName, enc]); + } +}; +}); + +// file: lib/ios/plugin/ios/console.js +define("cordova/plugin/ios/console", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * This class provides access to the debugging console. + * @constructor + */ +var DebugConsole = function() { + this.winConsole = window.console; + this.logLevel = DebugConsole.INFO_LEVEL; +}; + +// from most verbose, to least verbose +DebugConsole.ALL_LEVEL = 1; // same as first level +DebugConsole.INFO_LEVEL = 1; +DebugConsole.WARN_LEVEL = 2; +DebugConsole.ERROR_LEVEL = 4; +DebugConsole.NONE_LEVEL = 8; + +DebugConsole.prototype.setLevel = function(level) { + this.logLevel = level; +}; + +var stringify = function(message) { + try { + if (typeof message === "object" && JSON && JSON.stringify) { + try { + return JSON.stringify(message); + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + } else { + return message.toString(); + } + } catch (e) { + return e.toString(); + } +}; + +/** + * Print a normal log message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.log = function(message) { + if (this.logLevel <= DebugConsole.INFO_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'INFO' } ]); + } + else if (this.winConsole && this.winConsole.log) { + this.winConsole.log(message); + } +}; + +/** + * Print a warning message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.warn = function(message) { + if (this.logLevel <= DebugConsole.WARN_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'WARN' } ]); + } + else if (this.winConsole && this.winConsole.warn) { + this.winConsole.warn(message); + } +}; + +/** + * Print an error message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.error = function(message) { + if (this.logLevel <= DebugConsole.ERROR_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'ERROR' } ]); + } + else if (this.winConsole && this.winConsole.error){ + this.winConsole.error(message); + } +}; + +module.exports = new DebugConsole(); +}); + +// file: lib/ios/plugin/ios/contacts.js +define("cordova/plugin/ios/contacts", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides iOS enhanced contacts API. + */ +module.exports = { + newContactUI : function(successCallback) { + /* + * Create a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * returns: the id of the created contact as param to successCallback + */ + exec(successCallback, null, "Contacts","newContact", []); + }, + chooseContact : function(successCallback, options) { + /* + * Select a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + * + * returns: the id of the selected contact as param to successCallback + */ + exec(successCallback, null, "Contacts","chooseContact", [options]); + } +}; +}); + +// file: lib/ios/plugin/ios/nativecomm.js +define("cordova/plugin/ios/nativecomm", function(require, exports, module) { +var cordova = require('cordova'); + +/** + * Called by native code to retrieve all queued commands and clear the queue. + */ +module.exports = function() { + var json = JSON.stringify(cordova.commandQueue); + cordova.commandQueue = []; + return json; +}; +}); + +// file: lib/ios/plugin/ios/notification.js +define("cordova/plugin/ios/notification", function(require, exports, module) { +var Media = require('cordova/plugin/Media'); + +module.exports = { + beep:function(count) { + (new Media('beep.wav')).play(); + } +}; +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + + Hello Cordova + + +
+

Apache Cordovaâ„¢

+
+ + +
+
+ + + + + + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/js/WikitudePlugin.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/js/WikitudePlugin.js new file mode 100644 index 0000000..997558e --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/js/WikitudePlugin.js @@ -0,0 +1,353 @@ +var WikitudePlugin = { + + /** + * + * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk + * If you're having a trial version, leave this string empty + * + */ + mySDKKey : "ENTER-YOUR-KEY-HERE", + + /** + * + * Change the value of this variable to modify the location update rate + * + */ + locationUpdateRate : 3000, + + /** + * + * This variable represents if the current device is capable of running the Wikitude SDK + * + */ + isDeviceSupported : false, + + /** + * + * This watchID is used to shedule location updates + * + */ + watchID : null, + + /** + * + * Callbacks to get device information if ARchitect Worlds can be launched + * + */ + onDeviceSupportedCallback : null, + onDeviceNotSupportedCallback : null, + + /** + * + * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch + * + */ + onARchitectWorldLaunchedCallback : null, + onARchitectWorldFailedLaunchingCallback : null, + + /** + * + * This function gets called if PhoneGap reports that it has finished loading successfully. + * + */ + isDeviceSupported: function(successCallback, errorCallback) + { + + WikitudePlugin.onDeviceSupportedCallback = successCallback; + WikitudePlugin.onDeviceNotSupportedCallback = errorCallback; + + + // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin + cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]); + + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK + * + */ + deviceIsARchitectReady : function() + { + // We keep track of the device status + WikitudePlugin.isDeviceSupported = true; + + + if(WikitudePlugin.onDeviceSupportedCallback) + { + WikitudePlugin.onDeviceSupportedCallback(); + } + }, + + /** + * + * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK. + * + */ + deviceIsNotARchitectReady : function() + { + WikitudePlugin.isDeviceSupported = false; + + // In this case we notify the user that his device is not supported by the Wikitude SDK + if(WikitudePlugin.onDeviceNotSupportedCallback) + { + WikitudePlugin.onDeviceNotSupportedCallback(); + } + }, + + + /* + * ============================================================================================================================= + * + * PUBLIC API + * + * ============================================================================================================================= + */ + + /* Managing ARchitect world loading */ + + /** + * + * Call this function if you want to load an ARchitect World + * + * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox + * + */ + loadARchitectWorld : function(worldPath) + { + + // before we actually call load, we check again if the device is able to open the world + if(WikitudePlugin.isDeviceSupported) + { + + // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys: + // @param {Object} options (required) + // @param {String} options.sdkKey License key for the Wikitude SDK + // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox + + cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]); + + + // We add an event listener on the resume and pause event of the application lifecycle + document.addEventListener("resume", WikitudePlugin.onResume, false); + document.addEventListener("pause", WikitudePlugin.onPause, false); + + // After we started loading the world, we start location updates + WikitudePlugin.startLocationUpdates(); + + }else + { + // if the device is not able to start the Wikitude SDK, we notify the user again + WikitudePlugin.deviceNotARchitectReady(); + } + }, + + /* Managing the Wikitude SDK Lifecycle */ + + /** + * + * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen + * + */ + close : function() + { + document.removeEventListener("pause", WikitudePlugin.onPause, false); + document.removeEventListener("resume", WikitudePlugin.onResume, false); + + WikitudePlugin.stopLocationUpdates(); + + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]); + }, + + /** + * + * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active + * + */ + hide : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]); + }, + + /** + * + * Use this function to show the Wikitude SDK if it was hidden before + * + */ + show : function() + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]); + }, + + /* Interacting with the Wikitude SDK */ + + /** + * + * Use this function to call javascript which will be executed in the context of your ARchitect World + * + * + * @param js The JavaScript that gets evaluated in context of the ARchitect World + * + */ + callJavaScript : function(js) + { + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]); + }, + + /** + * + * Use this function to set a callback which will be invoked when the ARchitect World calls for example + * document.location = "architectsdk://opendetailpage?id=9"; + * + * + * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://" + */ + setOnUrlInvokeCallback : function(onUrlInvokeCallback) + { + cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]); + }, + + + /* + * ============================================================================================================================= + * + * Callbacks of public functions + * + * ============================================================================================================================= + */ + + /** + * + * Use this callback to get notified if the world loaded successfully + * + */ + worldLaunched : function() + { + if(WikitudePlugin.onARchitectWorldLaunchedCallback) + { + WikitudePlugin.onARchitectWorldLaunchedCallback(); + } + }, + + /** + * + * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World + * + */ + worldFailedLaunching : function(err) + { + if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback) + { + WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err); + } + }, + + /* Lifecycle updates */ + + /** + * + * This function actually starts the PhoneGap location updates + * + */ + startLocationUpdates : function() + { + + WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate }); + }, + + /** + * + * This callback gets called everytime the location did update + * + */ + onReceivedLocation : function(position) + { + + // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]); + }, + + /** + * + * Use this function to stop location updates + * + */ + stopLocationUpdates : function() + { + + // We clear the location update watch which was responsible for updating the location in a specific time interval + navigator.geolocation.clearWatch(WikitudePlugin.watchID); + WikitudePlugin.watchID = null; + }, + + /** + * + * This function gets called every time the application did become active. + * + */ + onResume : function() + { + + // Call the Wikitude SDK that the application did become active again + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]); + + // And start continuing updating the user location + WikitudePlugin.startLocationUpdates(); + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onPause : function() + { + + // Call the Wikitude SDK that the application did resign active + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]); + + // And stop all ongoing location updates + WikitudePlugin.stopLocationUpdates(); + }, + + /** + * + * Android specific! + * This function gets called if the user presses the back button + * + */ + onBackButton : function() + { + + cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]); + WikitudePlugin.stopLocationUpdates(); + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeOK : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + onWikitudeError : function() + { + }, + + /** + * + * This function gets called every time the application is about to become inactive + * + */ + report: function(id) + { + console.log("app report: " + id); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/js/index.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/js/index.js new file mode 100644 index 0000000..b82a058 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/js/index.js @@ -0,0 +1,37 @@ +var app = { + initialize: function() { + this.bind(); + }, + bind: function() { + document.addEventListener('deviceready', this.deviceready, false); + }, + + // A callback which gets called if the device is able to launch ARchitect Worlds + onDeviceSupportedCallback : function() + { + // The device is able to launch ARchitect World, so lets do so :) + WikitudePlugin.loadARchitectWorld("assets/world/SimpleImageRecognition/SimpleIRWorld.html"); + }, + + // A callback which gets called if the device is not able to start ARchitect Worlds + onDeviceNotSupportedCallback : function() + { + app.report("Unable to launch ARchitect Worlds on this device"); + }, + + deviceready: function() { + // note that this is an event handler so the scope is that of the event + // so we need to call app.report(), and not this.report() + app.report('deviceready'); + + // check if the current device is able to launch ARchitect Worlds + WikitudePlugin.isDeviceSupported(app.onDeviceSupportedCallback, app.onDeviceNotSupportedCallback); + }, + report: function(id) { + console.log("report:" + id); + // hide the .pending

and show the .complete

+ document.querySelector('#' + id + ' .pending').className += ' hide'; + var completeElem = document.querySelector('#' + id + ' .complete'); + completeElem.className = completeElem.className.split('hide').join(''); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_128.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_128.png new file mode 100644 index 0000000..3516df3 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_128.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_16.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_16.png new file mode 100644 index 0000000..54e19c5 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_16.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_24.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_24.png new file mode 100644 index 0000000..c7d43ad Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_24.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_256.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_256.png new file mode 100644 index 0000000..e1cd0e6 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_256.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_32.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_32.png new file mode 100644 index 0000000..734fffc Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_32.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_48.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_48.png new file mode 100644 index 0000000..8ad8bac Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_48.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_512.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_512.png new file mode 100644 index 0000000..c9465f3 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_512.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_64.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_64.png new file mode 100644 index 0000000..03b3849 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_64.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_36.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_36.png new file mode 100644 index 0000000..cd5032a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_36.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_48.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_48.png new file mode 100644 index 0000000..e79c606 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_48.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_72.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_72.png new file mode 100644 index 0000000..4d27634 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_72.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_96.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_96.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_android_96.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_bb_80.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_bb_80.png new file mode 100644 index 0000000..f86a27a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_bb_80.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_114.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_114.png new file mode 100644 index 0000000..efd9c37 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_114.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_144.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_144.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_144.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_57.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_57.png new file mode 100644 index 0000000..c795fc4 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_57.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_72.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_72.png new file mode 100644 index 0000000..b1cfde7 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/icon/cordova_ios_72.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_hdpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_hdpi_landscape.png new file mode 100644 index 0000000..a61e2b1 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_hdpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_hdpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_hdpi_portrait.png new file mode 100644 index 0000000..5d6a28a Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_hdpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_ldpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_ldpi_landscape.png new file mode 100644 index 0000000..f3934cd Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_ldpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_ldpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_ldpi_portrait.png new file mode 100644 index 0000000..65ad163 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_ldpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_mdpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_mdpi_landscape.png new file mode 100644 index 0000000..a1b697c Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_mdpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_mdpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_mdpi_portrait.png new file mode 100644 index 0000000..ea15693 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_mdpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_xhdpi_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_xhdpi_landscape.png new file mode 100644 index 0000000..79f2f09 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_xhdpi_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_xhdpi_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_xhdpi_portrait.png new file mode 100644 index 0000000..c2e8042 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/android_xhdpi_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/blackberry_transparent_300.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/blackberry_transparent_300.png new file mode 100644 index 0000000..b548bdc Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/blackberry_transparent_300.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/blackberry_transparent_400.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/blackberry_transparent_400.png new file mode 100644 index 0000000..3facdf9 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/blackberry_transparent_400.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_landscape.png new file mode 100644 index 0000000..04be5ac Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_portrait.png new file mode 100644 index 0000000..41e839d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_retina_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_retina_landscape.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_retina_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_retina_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_retina_portrait.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/ipad_retina_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_landscape.png new file mode 100644 index 0000000..d154883 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_portrait.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_retina_landscape.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_retina_landscape.png new file mode 100644 index 0000000..0165669 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_retina_landscape.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_retina_portrait.png b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_retina_portrait.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/iphone_retina_portrait.png differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/windows_phone_portrait.jpg b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/windows_phone_portrait.jpg new file mode 100644 index 0000000..9f95387 Binary files /dev/null and b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/res/screen/windows_phone_portrait.jpg differ diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec.html b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec.html new file mode 100644 index 0000000..83d7d2e --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec.html @@ -0,0 +1,50 @@ + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + +

+ + diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec/helper.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec/helper.js new file mode 100644 index 0000000..9f99445 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec/helper.js @@ -0,0 +1,11 @@ +afterEach(function() { + document.getElementById('stage').innerHTML = ''; +}); + +var helper = { + trigger: function(obj, name) { + var e = document.createEvent('Event'); + e.initEvent(name, true, true); + obj.dispatchEvent(e); + } +}; diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec/index.js b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec/index.js new file mode 100644 index 0000000..121cf63 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/www/spec/index.js @@ -0,0 +1,49 @@ +describe('app', function() { + describe('initialize', function() { + it('should bind deviceready', function() { + runs(function() { + spyOn(app, 'deviceready'); + app.initialize(); + helper.trigger(window.document, 'deviceready'); + }); + + waitsFor(function() { + return (app.deviceready.calls.length > 0); + }, 'deviceready should be called once', 500); + + runs(function() { + expect(app.deviceready).toHaveBeenCalled(); + }); + }); + }); + + describe('deviceready', function() { + it('should report that it fired', function() { + spyOn(app, 'report'); + app.deviceready(); + expect(app.report).toHaveBeenCalledWith('deviceready'); + }); + }); + + describe('report', function() { + beforeEach(function() { + var el = document.getElementById('stage'); + el.innerHTML = ['
', + '

Pending

', + '

Complete

', + '
'].join('\n'); + }); + + it('should show the completion state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .complete:not(.hide)'); + expect(el).toBeTruthy(); + }); + + it('should hide the pending state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .pending.hide'); + expect(el).toBeTruthy(); + }); + }); +}); diff --git a/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/README.md b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/README.md new file mode 100644 index 0000000..378bd65 --- /dev/null +++ b/iOS/AugmentedReality-Wikitude/SampleProjects/Extended/README.md @@ -0,0 +1,16 @@ +# Hello Image Recognition iOS + +This document describes all necessary steps to get the sample running. + + +###Setup +*** + +* Download the Vuforia Framework from [Qualcomm](https://ar.qualcomm.at/sdk/ios) (You need to register yourself as a Qualcomm developer) + +* After that, copy the lib and and header files from your downloaded Vuforia SDK into the ``` [ProjectFolder/"AppName"/Vuforia/include/QCAR] ``` and ``` [ProjectFolder/"AppName"/Vuforia/lib/arm/] ``` folder of this project + +* Download the Wikitude SDK from [our website](http://www.wikitude.com/developer/sdk) (You need to register yourself as a Wikitude developer) + +* Copy the SDK folder from ``` [DownloadedSDKRoot/iOS/SDK] ``` into ``` [ProjectFolder/"AppName"/WikitudeSDK] ``` + diff --git a/iOS/BarcodeScanner/CDVBarcodeScanner.mm b/iOS/BarcodeScanner/CDVBarcodeScanner.mm index 338ba49..aaa140a 100644 --- a/iOS/BarcodeScanner/CDVBarcodeScanner.mm +++ b/iOS/BarcodeScanner/CDVBarcodeScanner.mm @@ -16,11 +16,20 @@ //------------------------------------------------------------------------------ #import "zxing-all-in-one.h" -#ifdef CORDOVA_FRAMEWORK -#import -#else -#import "CDVPlugin.h" -#endif +#import + + +//------------------------------------------------------------------------------ +// Delegate to handle orientation functions +// +//------------------------------------------------------------------------------ +@protocol CDVBarcodeScannerOrientationDelegate + +- (NSUInteger)supportedInterfaceOrientations; +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +- (BOOL)shouldAutorotate; + +@end //------------------------------------------------------------------------------ // Adds a shutter button to the UI, and changes the scan from continuous to @@ -76,11 +85,13 @@ //------------------------------------------------------------------------------ // view controller for the ui //------------------------------------------------------------------------------ -@interface CDVbcsViewController : UIViewController {} +@interface CDVbcsViewController : UIViewController {} @property (nonatomic, retain) CDVbcsProcessor* processor; @property (nonatomic, retain) NSString* alternateXib; @property (nonatomic) BOOL shutterPressed; @property (nonatomic, retain) IBOutlet UIView* overlayView; +// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the property below +@property (nonatomic, unsafe_unretained) id orientationDelegate; - (id)initWithProcessor:(CDVbcsProcessor*)processor alternateOverlay:(NSString *)alternateXib; - (void)startCapturing; @@ -238,6 +249,8 @@ parentViewController:(UIViewController*)parentViewController } self.viewController = [[[CDVbcsViewController alloc] initWithProcessor: self alternateOverlay:self.alternateXib] autorelease]; + // here we set the orientation delegate to the MainViewController of the app (orientation controlled in the Project Settings) + self.viewController.orientationDelegate = self.plugin.viewController; // delayed [self openDialog]; [self performSelector:@selector(openDialog) withObject:nil afterDelay:1]; @@ -633,6 +646,17 @@ parentViewController:(UIViewController*)parentViewController [self.view addSubview:[self buildOverlayView]]; } +//-------------------------------------------------------------------------- +- (void)viewWillAppear:(BOOL)animated { + + // set video orientation to what the camera sees + self.processor.previewLayer.orientation = [[UIApplication sharedApplication] statusBarOrientation]; + + // this fixes the bug when the statusbar is landscape, and the preview layer + // starts up in portrait (not filling the whole view) + self.processor.previewLayer.frame = self.view.bounds; +} + //-------------------------------------------------------------------------- - (void)viewDidAppear:(BOOL)animated { [self startCapturing]; @@ -645,13 +669,6 @@ parentViewController:(UIViewController*)parentViewController self.processor.capturing = YES; } -//-------------------------------------------------------------------------- -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - // rotation currently not supported - if (interfaceOrientation == UIInterfaceOrientationPortrait) return YES; - return NO; -} - //-------------------------------------------------------------------------- - (void)shutterButtonPressed { self.shutterPressed = YES; @@ -800,4 +817,45 @@ parentViewController:(UIViewController*)parentViewController return result; } +#pragma mark CDVBarcodeScannerOrientationDelegate + +- (BOOL)shouldAutorotate +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { + return [self.orientationDelegate shouldAutorotate]; + } + + return YES; +} + +- (NSUInteger)supportedInterfaceOrientations +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { + return [self.orientationDelegate supportedInterfaceOrientations]; + } + + return UIInterfaceOrientationMaskPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { + return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + + return YES; +} + +- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration +{ + [CATransaction begin]; + + self.processor.previewLayer.orientation = orientation; + [self.processor.previewLayer layoutSublayers]; + self.processor.previewLayer.frame = self.view.bounds; + + [CATransaction commit]; + [super willAnimateRotationToInterfaceOrientation:orientation duration:duration]; +} + @end diff --git a/iOS/BarcodeScanner/barcodescanner.js b/iOS/BarcodeScanner/barcodescanner.js old mode 100644 new mode 100755 index 82f30bd..c1df94e --- a/iOS/BarcodeScanner/barcodescanner.js +++ b/iOS/BarcodeScanner/barcodescanner.js @@ -8,10 +8,6 @@ ;(function(){ -if (Cordova.hasResource("barcodeScanner")) return - -Cordova.addResource("barcodeScanner") - //------------------------------------------------------------------- var BarcodeScanner = function() { } @@ -69,15 +65,12 @@ BarcodeScanner.prototype.encode = function(type, data, success, fail, options) { } //------------------------------------------------------------------- -Cordova.addConstructor(function() { - if (!window.plugins) window.plugins = {} - if (!window.plugins.barcodeScanner) { - window.plugins.barcodeScanner = new BarcodeScanner() - } - else { - console.log("Not installing barcodeScanner: window.plugins.barcodeScanner already exists") - } -}) +// remove Cordova.addConstructor since it was not supported on PhoneGap 2.0 +if (!window.plugins) window.plugins = {} + +if (!window.plugins.barcodeScanner) { + window.plugins.barcodeScanner = new BarcodeScanner() +} })(); diff --git a/iOS/BarcodeScanner/build/Makefile b/iOS/BarcodeScanner/build/Makefile old mode 100644 new mode 100755 index 793d622..446e7c6 --- a/iOS/BarcodeScanner/build/Makefile +++ b/iOS/BarcodeScanner/build/Makefile @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------- -ZXING_VERS = 1.7 +ZXING_VERS = 2.0 ZXING_URL = http://zxing.googlecode.com/files/ZXing-$(ZXING_VERS).zip #------------------------------------------------------------------------------- @@ -9,7 +9,7 @@ all: help build: tmp/zxing.zip -@rm -rf zxing mkdir zxing - cp -R tmp/zip/zxing/cpp/core/src/zxing/* zxing + cp -R tmp/zip/zxing-$(ZXING_VERS)/cpp/core/src/zxing/* zxing python onefile-zxing.py zxing .. # rm -rf zxing diff --git a/iOS/BarcodeScanner/build/onefile-zxing.py b/iOS/BarcodeScanner/build/onefile-zxing.py index 45d8484..578c4bc 100755 --- a/iOS/BarcodeScanner/build/onefile-zxing.py +++ b/iOS/BarcodeScanner/build/onefile-zxing.py @@ -275,14 +275,28 @@ def getIncludeOrder(foundIncludes): zxing/qrcode/decoder/DataBlock.h zxing/qrcode/decoder/DataMask.h zxing/qrcode/decoder/Mode.h + zxing/common/ECI.h + zxing/common/CharacterSetECI.h zxing/qrcode/decoder/DecodedBitStreamParser.h zxing/qrcode/detector/AlignmentPattern.h zxing/qrcode/detector/AlignmentPatternFinder.h - zxing/qrcode/detector/Detector.h zxing/qrcode/detector/FinderPattern.h zxing/qrcode/detector/FinderPatternInfo.h + zxing/qrcode/detector/Detector.h zxing/qrcode/detector/FinderPatternFinder.h zxing/qrcode/detector/QREdgeDetector.h + zxing/FormatException.h + zxing/NotFoundException.h + zxing/common/StringUtils.h + zxing/common/detector/MonochromeRectangleDetector.h + zxing/common/detector/WhiteRectangleDetector.h + zxing/datamatrix/detector/DetectorException.h + zxing/multi/ByQuadrantReader.h + zxing/multi/MultipleBarcodeReader.h + zxing/multi/GenericMultipleBarcodeReader.h + zxing/multi/qrcode/QRCodeMultiReader.h + zxing/multi/qrcode/detector/MultiDetector.h + zxing/multi/qrcode/detector/MultiFinderPatternFinder.h """.split() foundError = False diff --git a/iOS/BarcodeScanner/zxing-all-in-one.cpp b/iOS/BarcodeScanner/zxing-all-in-one.cpp index f148ae5..2958a34 100644 --- a/iOS/BarcodeScanner/zxing-all-in-one.cpp +++ b/iOS/BarcodeScanner/zxing-all-in-one.cpp @@ -3,10 +3,8 @@ // file: zxing/BarcodeFormat.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * BarcodeFormat.cpp - * zxing - * * Created by Christian Brunschen on 13/05/2008. * Copyright 2008 ZXing authors All rights reserved. * @@ -26,24 +24,25 @@ // #include namespace zxing { - - const char *barcodeFormatNames[] = { - "None", - "QR_CODE", - "DATA_MATRIX", - "UPC_E", - "UPC_A", - "EAN_8", - "EAN_13", - "CODE_128", - "CODE_39", - "ITF" - }; - + +const char *barcodeFormatNames[] = { + "None", + "QR_CODE", + "DATA_MATRIX", + "UPC_E", + "UPC_A", + "EAN_8", + "EAN_13", + "CODE_128", + "CODE_39", + "ITF" +}; + } // file: zxing/Binarizer.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * Binarizer.cpp * zxing @@ -68,17 +67,17 @@ namespace zxing { // #include namespace zxing { - + Binarizer::Binarizer(Ref source) : source_(source) { - } - + } + Binarizer::~Binarizer() { } - + Ref Binarizer::getLuminanceSource() const { return source_; } - + } // file: zxing/BinaryBitmap.cpp @@ -105,49 +104,49 @@ namespace zxing { // #include namespace zxing { - + BinaryBitmap::BinaryBitmap(Ref binarizer) : binarizer_(binarizer) { - + } - + BinaryBitmap::~BinaryBitmap() { } - + Ref BinaryBitmap::getBlackRow(int y, Ref row) { return binarizer_->getBlackRow(y, row); } - + Ref BinaryBitmap::getBlackMatrix() { return binarizer_->getBlackMatrix(); } - + int BinaryBitmap::getWidth() const { return getLuminanceSource()->getWidth(); } - + int BinaryBitmap::getHeight() const { return getLuminanceSource()->getHeight(); } - + Ref BinaryBitmap::getLuminanceSource() const { return binarizer_->getLuminanceSource(); } - - + + bool BinaryBitmap::isCropSupported() const { - return getLuminanceSource()->isCropSupported(); + return getLuminanceSource()->isCropSupported(); } - + Ref BinaryBitmap::crop(int left, int top, int width, int height) { - return Ref (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->crop(left, top, width, height)))); + return Ref (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->crop(left, top, width, height)))); } - + bool BinaryBitmap::isRotateSupported() const { - return getLuminanceSource()->isRotateSupported(); + return getLuminanceSource()->isRotateSupported(); } - + Ref BinaryBitmap::rotateCounterClockwise() { - return Ref (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->rotateCounterClockwise()))); + return Ref (new BinaryBitmap(binarizer_->createBinarizer(getLuminanceSource()->rotateCounterClockwise()))); } } @@ -175,126 +174,181 @@ namespace zxing { // #include // #include namespace zxing { - - const DecodeHints DecodeHints::PRODUCT_HINT( - BARCODEFORMAT_UPC_E_HINT | - BARCODEFORMAT_UPC_A_HINT | - BARCODEFORMAT_EAN_8_HINT | - BARCODEFORMAT_EAN_13_HINT); - - const DecodeHints DecodeHints::ONED_HINT( - BARCODEFORMAT_UPC_E_HINT | - BARCODEFORMAT_UPC_A_HINT | - BARCODEFORMAT_EAN_8_HINT | - BARCODEFORMAT_EAN_13_HINT | - BARCODEFORMAT_CODE_128_HINT | - BARCODEFORMAT_CODE_39_HINT | - BARCODEFORMAT_ITF_HINT); - - const DecodeHints DecodeHints::DEFAULT_HINT( - BARCODEFORMAT_UPC_E_HINT | - BARCODEFORMAT_UPC_A_HINT | - BARCODEFORMAT_EAN_8_HINT | - BARCODEFORMAT_EAN_13_HINT | - BARCODEFORMAT_CODE_128_HINT | - BARCODEFORMAT_CODE_39_HINT | - BARCODEFORMAT_ITF_HINT | - // TODO: uncomment once this passes QA - // BARCODEFORMAT_DATA_MATRIX_HINT | - BARCODEFORMAT_QR_CODE_HINT); - - DecodeHints::DecodeHints() { - hints = 0; - } - - DecodeHints::DecodeHints(DecodeHintType init) { - hints = init; - } - - void DecodeHints::addFormat(BarcodeFormat toadd) { - switch (toadd) { - case BarcodeFormat_QR_CODE: hints |= BARCODEFORMAT_QR_CODE_HINT; break; - case BarcodeFormat_DATA_MATRIX: hints |= BARCODEFORMAT_DATA_MATRIX_HINT; break; - case BarcodeFormat_UPC_E: hints |= BARCODEFORMAT_UPC_E_HINT; break; - case BarcodeFormat_UPC_A: hints |= BARCODEFORMAT_UPC_A_HINT; break; - case BarcodeFormat_EAN_8: hints |= BARCODEFORMAT_EAN_8_HINT; break; - case BarcodeFormat_EAN_13: hints |= BARCODEFORMAT_EAN_13_HINT; break; - case BarcodeFormat_CODE_128: hints |= BARCODEFORMAT_CODE_128_HINT; break; - case BarcodeFormat_CODE_39: hints |= BARCODEFORMAT_CODE_39_HINT; break; - case BarcodeFormat_ITF: hints |= BARCODEFORMAT_ITF_HINT; break; - default: throw IllegalArgumentException("Unrecognizd barcode format"); - } - } - - bool DecodeHints::containsFormat(BarcodeFormat tocheck) const { - DecodeHintType checkAgainst; - switch (tocheck) { - case BarcodeFormat_QR_CODE: checkAgainst = BARCODEFORMAT_QR_CODE_HINT; break; - case BarcodeFormat_DATA_MATRIX: checkAgainst = BARCODEFORMAT_DATA_MATRIX_HINT; break; - case BarcodeFormat_UPC_E: checkAgainst = BARCODEFORMAT_UPC_E_HINT; break; - case BarcodeFormat_UPC_A: checkAgainst = BARCODEFORMAT_UPC_A_HINT; break; - case BarcodeFormat_EAN_8: checkAgainst = BARCODEFORMAT_EAN_8_HINT; break; - case BarcodeFormat_EAN_13: checkAgainst = BARCODEFORMAT_EAN_13_HINT; break; - case BarcodeFormat_CODE_128: checkAgainst = BARCODEFORMAT_CODE_128_HINT; break; - case BarcodeFormat_CODE_39: checkAgainst = BARCODEFORMAT_CODE_39_HINT; break; - case BarcodeFormat_ITF: checkAgainst = BARCODEFORMAT_ITF_HINT; break; - default: throw IllegalArgumentException("Unrecognizd barcode format"); - } - return (hints & checkAgainst); - } - - void DecodeHints::setTryHarder(bool toset) { - if (toset) { - hints |= TRYHARDER_HINT; - } else { - hints &= ~TRYHARDER_HINT; - } - } - - bool DecodeHints::getTryHarder() const { - return (hints & TRYHARDER_HINT); - } - - void DecodeHints::setResultPointCallback(Ref const& _callback) { - callback = _callback; - } - - Ref DecodeHints::getResultPointCallback() const { - return callback; - } - + +const DecodeHintType DecodeHints::CHARACTER_SET; + +const DecodeHints DecodeHints::PRODUCT_HINT( + BARCODEFORMAT_UPC_E_HINT | + BARCODEFORMAT_UPC_A_HINT | + BARCODEFORMAT_EAN_8_HINT | + BARCODEFORMAT_EAN_13_HINT); + +const DecodeHints DecodeHints::ONED_HINT( + BARCODEFORMAT_UPC_E_HINT | + BARCODEFORMAT_UPC_A_HINT | + BARCODEFORMAT_EAN_8_HINT | + BARCODEFORMAT_EAN_13_HINT | + BARCODEFORMAT_CODE_128_HINT | + BARCODEFORMAT_CODE_39_HINT | + BARCODEFORMAT_ITF_HINT); + +const DecodeHints DecodeHints::DEFAULT_HINT( + BARCODEFORMAT_UPC_E_HINT | + BARCODEFORMAT_UPC_A_HINT | + BARCODEFORMAT_EAN_8_HINT | + BARCODEFORMAT_EAN_13_HINT | + BARCODEFORMAT_CODE_128_HINT | + BARCODEFORMAT_CODE_39_HINT | + BARCODEFORMAT_ITF_HINT | + BARCODEFORMAT_DATA_MATRIX_HINT | + BARCODEFORMAT_QR_CODE_HINT); + +DecodeHints::DecodeHints() { + hints = 0; +} + +DecodeHints::DecodeHints(DecodeHintType init) { + hints = init; +} + +void DecodeHints::addFormat(BarcodeFormat toadd) { + switch (toadd) { + case BarcodeFormat_QR_CODE: hints |= BARCODEFORMAT_QR_CODE_HINT; break; + case BarcodeFormat_DATA_MATRIX: hints |= BARCODEFORMAT_DATA_MATRIX_HINT; break; + case BarcodeFormat_UPC_E: hints |= BARCODEFORMAT_UPC_E_HINT; break; + case BarcodeFormat_UPC_A: hints |= BARCODEFORMAT_UPC_A_HINT; break; + case BarcodeFormat_EAN_8: hints |= BARCODEFORMAT_EAN_8_HINT; break; + case BarcodeFormat_EAN_13: hints |= BARCODEFORMAT_EAN_13_HINT; break; + case BarcodeFormat_CODE_128: hints |= BARCODEFORMAT_CODE_128_HINT; break; + case BarcodeFormat_CODE_39: hints |= BARCODEFORMAT_CODE_39_HINT; break; + case BarcodeFormat_ITF: hints |= BARCODEFORMAT_ITF_HINT; break; + default: throw IllegalArgumentException("Unrecognizd barcode format"); + } +} + +bool DecodeHints::containsFormat(BarcodeFormat tocheck) const { + DecodeHintType checkAgainst; + switch (tocheck) { + case BarcodeFormat_QR_CODE: checkAgainst = BARCODEFORMAT_QR_CODE_HINT; break; + case BarcodeFormat_DATA_MATRIX: checkAgainst = BARCODEFORMAT_DATA_MATRIX_HINT; break; + case BarcodeFormat_UPC_E: checkAgainst = BARCODEFORMAT_UPC_E_HINT; break; + case BarcodeFormat_UPC_A: checkAgainst = BARCODEFORMAT_UPC_A_HINT; break; + case BarcodeFormat_EAN_8: checkAgainst = BARCODEFORMAT_EAN_8_HINT; break; + case BarcodeFormat_EAN_13: checkAgainst = BARCODEFORMAT_EAN_13_HINT; break; + case BarcodeFormat_CODE_128: checkAgainst = BARCODEFORMAT_CODE_128_HINT; break; + case BarcodeFormat_CODE_39: checkAgainst = BARCODEFORMAT_CODE_39_HINT; break; + case BarcodeFormat_ITF: checkAgainst = BARCODEFORMAT_ITF_HINT; break; + default: throw IllegalArgumentException("Unrecognizd barcode format"); + } + return (hints & checkAgainst); +} + +void DecodeHints::setTryHarder(bool toset) { + if (toset) { + hints |= TRYHARDER_HINT; + } else { + hints &= ~TRYHARDER_HINT; + } +} + +bool DecodeHints::getTryHarder() const { + return (hints & TRYHARDER_HINT); +} + +void DecodeHints::setResultPointCallback(Ref const& _callback) { + callback = _callback; +} + +Ref DecodeHints::getResultPointCallback() const { + return callback; +} + } /* namespace */ // file: zxing/Exception.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * Exception.cpp * ZXing * * Created by Christian Brunschen on 03/06/2008. - * Copyright 2008 ZXing authors All rights reserved. + * Copyright 2008-2011 ZXing authors 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. + */ // #include namespace zxing { - - Exception::Exception(const char *msg) : + +Exception::Exception() {} + +Exception::Exception(const char *msg) : message(msg) { - } - - const char* Exception::what() const throw() { - return message.c_str(); - } - - Exception::~Exception() throw() { - } - +} + +const char* Exception::what() const throw() { + return message.c_str(); +} + +Exception::~Exception() throw() { +} + +} + +// file: zxing/FormatException.cpp + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- +/* + * FormatException.cpp + * zxing + * + * Created by Christian Brunschen on 13/05/2008. + * Copyright 2008 ZXing authors 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. + */ + +// #include + +namespace zxing { + +FormatException::FormatException() {} + +FormatException::FormatException(const char *msg) : + ReaderException(msg) { +} + +FormatException::~FormatException() throw() { +} + } // file: zxing/LuminanceSource.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * LuminanceSource.cpp * zxing @@ -314,33 +368,65 @@ namespace zxing { * limitations under the License. */ +// #include // #include // #include namespace zxing { - - LuminanceSource::LuminanceSource() { + +LuminanceSource::LuminanceSource() { +} + +LuminanceSource::~LuminanceSource() { +} + +bool LuminanceSource::isCropSupported() const { + return false; +} + +Ref LuminanceSource::crop(int left, int top, int width, int height) { + (void)left; + (void)top; + (void)width; + (void)height; + throw IllegalArgumentException("This luminance source does not support cropping."); +} + +bool LuminanceSource::isRotateSupported() const { + return false; +} + +Ref LuminanceSource::rotateCounterClockwise() { + throw IllegalArgumentException("This luminance source does not support rotation."); +} + +LuminanceSource::operator std::string() { + unsigned char* row = 0; + std::ostringstream oss; + for (int y = 0; y < getHeight(); y++) { + row = getRow(y, row); + for (int x = 0; x < getWidth(); x++) { + int luminance = row[x] & 0xFF; + char c; + if (luminance < 0x40) { + c = '#'; + } else if (luminance < 0x80) { + c = '+'; + } else if (luminance < 0xC0) { + c = '.'; + } else { + c = ' '; + } + oss << c; } - - LuminanceSource::~LuminanceSource() { - } - - bool LuminanceSource::isCropSupported() const { - return false; - } - - Ref LuminanceSource::crop(int left, int top, int width, int height) { - throw IllegalArgumentException("This luminance source does not support cropping."); - } - - bool LuminanceSource::isRotateSupported() const { - return false; - } - - Ref LuminanceSource::rotateCounterClockwise() { - throw IllegalArgumentException("This luminance source does not support rotation."); - } - + oss << '\n'; + } + delete [] row; + return oss.str(); +} + + + } // file: zxing/MultiFormatReader.cpp @@ -374,78 +460,109 @@ namespace zxing { // #include namespace zxing { - MultiFormatReader::MultiFormatReader() { - + MultiFormatReader::MultiFormatReader() { + + } + + Ref MultiFormatReader::decode(Ref image) { + setHints(DecodeHints::DEFAULT_HINT); + return decodeInternal(image); + } + + Ref MultiFormatReader::decode(Ref image, DecodeHints hints) { + setHints(hints); + return decodeInternal(image); + } + + Ref MultiFormatReader::decodeWithState(Ref image) { + // Make sure to set up the default state so we don't crash + if (readers_.size() == 0) { + setHints(DecodeHints::DEFAULT_HINT); } - - Ref MultiFormatReader::decode(Ref image) { - setHints(DecodeHints::DEFAULT_HINT); - return decodeInternal(image); + return decodeInternal(image); + } + + void MultiFormatReader::setHints(DecodeHints hints) { + hints_ = hints; + readers_.clear(); + bool tryHarder = hints.getTryHarder(); + + bool addOneDReader = hints.containsFormat(BarcodeFormat_UPC_E) || + hints.containsFormat(BarcodeFormat_UPC_A) || + hints.containsFormat(BarcodeFormat_EAN_8) || + hints.containsFormat(BarcodeFormat_EAN_13) || + hints.containsFormat(BarcodeFormat_CODE_128) || + hints.containsFormat(BarcodeFormat_CODE_39) || + hints.containsFormat(BarcodeFormat_ITF); + if (addOneDReader && !tryHarder) { + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); } - - Ref MultiFormatReader::decode(Ref image, DecodeHints hints) { - setHints(hints); - return decodeInternal(image); + if (hints.containsFormat(BarcodeFormat_QR_CODE)) { + readers_.push_back(Ref(new zxing::qrcode::QRCodeReader())); } - - Ref MultiFormatReader::decodeWithState(Ref image) { - // Make sure to set up the default state so we don't crash - if (readers_.size() == 0) { - setHints(DecodeHints::DEFAULT_HINT); - } - return decodeInternal(image); + if (hints.containsFormat(BarcodeFormat_DATA_MATRIX)) { + readers_.push_back(Ref(new zxing::datamatrix::DataMatrixReader())); } - - void MultiFormatReader::setHints(DecodeHints hints) { - hints_ = hints; - readers_.clear(); - bool tryHarder = hints.getTryHarder(); - - bool addOneDReader = hints.containsFormat(BarcodeFormat_UPC_E) || - hints.containsFormat(BarcodeFormat_UPC_A) || - hints.containsFormat(BarcodeFormat_EAN_8) || - hints.containsFormat(BarcodeFormat_EAN_13) || - hints.containsFormat(BarcodeFormat_CODE_128) || - hints.containsFormat(BarcodeFormat_CODE_39) || - hints.containsFormat(BarcodeFormat_ITF); - if (addOneDReader && !tryHarder) { - readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); - } - if (hints.containsFormat(BarcodeFormat_QR_CODE)) { - readers_.push_back(Ref(new zxing::qrcode::QRCodeReader())); - } - if (hints.containsFormat(BarcodeFormat_DATA_MATRIX)) { - readers_.push_back(Ref(new zxing::datamatrix::DataMatrixReader())); - } - //TODO: add PDF417 here once PDF417 reader is implemented - if (addOneDReader && tryHarder) { - readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); - } - if (readers_.size() == 0) { - if (!tryHarder) { - readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); - } - readers_.push_back(Ref(new zxing::qrcode::QRCodeReader())); - if (tryHarder) { - readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); - } - } + //TODO: add PDF417 here once PDF417 reader is implemented + if (addOneDReader && tryHarder) { + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); } - - Ref MultiFormatReader::decodeInternal(Ref image) { - for (unsigned int i = 0; i < readers_.size(); i++) { - try { - return readers_[i]->decode(image, hints_); - } catch (ReaderException re) { - // continue - } - } - throw ReaderException("No code detected"); + if (readers_.size() == 0) { + if (!tryHarder) { + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); + } + readers_.push_back(Ref(new zxing::qrcode::QRCodeReader())); + if (tryHarder) { + readers_.push_back(Ref(new zxing::oned::MultiFormatOneDReader(hints))); + } } - - MultiFormatReader::~MultiFormatReader() { - + } + + Ref MultiFormatReader::decodeInternal(Ref image) { + for (unsigned int i = 0; i < readers_.size(); i++) { + try { + return readers_[i]->decode(image, hints_); + } catch (ReaderException const& re) { + // continue + } } + throw ReaderException("No code detected"); + } + + MultiFormatReader::~MultiFormatReader() { + + } +} + +// file: zxing/NotFoundException.cpp + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- +/* + * Copyright 20011 ZXing authors + * + * 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. + */ + +// #include + +namespace zxing { + + NotFoundException::NotFoundException(const char *msg) + : ReaderException(msg) {} + + NotFoundException::~NotFoundException() throw() { + } + } // file: zxing/Reader.cpp @@ -473,23 +590,24 @@ namespace zxing { // #include namespace zxing { - - Reader::~Reader() { } - - Ref Reader::decode(Ref image) { - return decode(image, DecodeHints::DEFAULT_HINT); - } - + +Reader::~Reader() { } + +Ref Reader::decode(Ref image) { + return decode(image, DecodeHints::DEFAULT_HINT); +} + } // file: zxing/ReaderException.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * ReaderException.cpp * zxing * * Created by Christian Brunschen on 13/05/2008. - * Copyright 2008 ZXing authors All rights reserved. + * Copyright 2008-2011 ZXing authors 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. @@ -507,18 +625,21 @@ namespace zxing { // #include namespace zxing { - - ReaderException::ReaderException(const char *msg) : + +ReaderException::ReaderException() {} + +ReaderException::ReaderException(const char *msg) : Exception(msg) { - } - - ReaderException::~ReaderException() throw() { - } - +} + +ReaderException::~ReaderException() throw() { +} + } // file: zxing/Result.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * Result.cpp * zxing @@ -542,41 +663,45 @@ namespace zxing { // #include namespace zxing { - using namespace std; - - Result::Result(Ref text, ArrayRef rawBytes, std::vector > resultPoints, - BarcodeFormat format) : - text_(text), rawBytes_(rawBytes), resultPoints_(resultPoints), format_(format) { - } - - Result::~Result() { - } - - Ref Result::getText() { - return text_; - } - - ArrayRef Result::getRawBytes() { - return rawBytes_; - } - - const std::vector >& Result::getResultPoints() const { - return resultPoints_; - } - - BarcodeFormat Result::getBarcodeFormat() const { - return format_; - } - - ostream& operator<<(ostream &out, Result& result) { - if (result.text_ != 0) { - out << result.text_->getText(); - } else { - out << "[" << result.rawBytes_->size() << " bytes]"; - } - return out; - } - +using namespace std; + +Result::Result(Ref text, ArrayRef rawBytes, std::vector > resultPoints, + BarcodeFormat format) : + text_(text), rawBytes_(rawBytes), resultPoints_(resultPoints), format_(format) { +} + +Result::~Result() { +} + +Ref Result::getText() { + return text_; +} + +ArrayRef Result::getRawBytes() { + return rawBytes_; +} + +const std::vector >& Result::getResultPoints() const { + return resultPoints_; +} + +std::vector >& Result::getResultPoints() { + return resultPoints_; +} + +BarcodeFormat Result::getBarcodeFormat() const { + return format_; +} + +ostream& operator<<(ostream &out, Result& result) { + if (result.text_ != 0) { + out << result.text_->getText(); + } else { + out << "[" << result.rawBytes_->size() << " bytes]"; + } + return out; +} + } // file: zxing/ResultPoint.cpp @@ -602,11 +727,84 @@ namespace zxing { */ // #include +// #include namespace zxing { - - ResultPoint::~ResultPoint() {} - + +ResultPoint::ResultPoint() : posX_(0), posY_(0) {} + +ResultPoint::ResultPoint(float x, float y) : posX_(x), posY_(y) {} + +ResultPoint::~ResultPoint() {} + +float ResultPoint::getX() const { + return posX_; +} + +float ResultPoint::getY() const { + return posY_; +} + +bool ResultPoint::equals(Ref other) { + return posX_ == other->getX() && posY_ == other->getY(); +} + +/** + *

Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and + * BC < AC and the angle between BC and BA is less than 180 degrees. + */ +void ResultPoint::orderBestPatterns(std::vector > &patterns) { + // Find distances between pattern centers + float zeroOneDistance = distance(patterns[0]->getX(), patterns[1]->getX(),patterns[0]->getY(), patterns[1]->getY()); + float oneTwoDistance = distance(patterns[1]->getX(), patterns[2]->getX(),patterns[1]->getY(), patterns[2]->getY()); + float zeroTwoDistance = distance(patterns[0]->getX(), patterns[2]->getX(),patterns[0]->getY(), patterns[2]->getY()); + + Ref pointA, pointB, pointC; + // Assume one closest to other two is B; A and C will just be guesses at first + if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) { + pointB = patterns[0]; + pointA = patterns[1]; + pointC = patterns[2]; + } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) { + pointB = patterns[1]; + pointA = patterns[0]; + pointC = patterns[2]; + } else { + pointB = patterns[2]; + pointA = patterns[0]; + pointC = patterns[1]; + } + + // Use cross product to figure out whether A and C are correct or flipped. + // This asks whether BC x BA has a positive z component, which is the arrangement + // we want for A, B, C. If it's negative, then we've got it flipped around and + // should swap A and C. + if (crossProductZ(pointA, pointB, pointC) < 0.0f) { + Ref temp = pointA; + pointA = pointC; + pointC = temp; + } + + patterns[0] = pointA; + patterns[1] = pointB; + patterns[2] = pointC; +} + +float ResultPoint::distance(Ref point1, Ref point2) { + return distance(point1->getX(), point1->getY(), point2->getX(), point2->getY()); +} + +float ResultPoint::distance(float x1, float x2, float y1, float y2) { + float xDiff = x1 - x2; + float yDiff = y1 - y2; + return (float) sqrt((double) (xDiff * xDiff + yDiff * yDiff)); +} + +float ResultPoint::crossProductZ(Ref pointA, Ref pointB, Ref pointC) { + float bX = pointB->getX(); + float bY = pointB->getY(); + return ((pointC->getX() - bX) * (pointA->getY() - bY)) - ((pointC->getY() - bY) * (pointA->getX() - bX)); +} } // file: zxing/ResultPointCallback.cpp @@ -633,9 +831,9 @@ namespace zxing { // #include namespace zxing { - - ResultPointCallback::~ResultPointCallback() {} - + +ResultPointCallback::~ResultPointCallback() {} + } // file: zxing/common/Array.cpp @@ -665,10 +863,8 @@ namespace zxing { // file: zxing/common/BitArray.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * BitArray.cpp - * zxing - * * Copyright 2010 ZXing authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -685,122 +881,120 @@ namespace zxing { */ // #include -// #include -// #include using namespace std; namespace zxing { - - static unsigned int logDigits_14(unsigned digits) { - unsigned log = 0; - unsigned val = 1; - while (val < digits) { - log++; - val <<= 1; - } - return log; - } - - const unsigned int BitArray::bitsPerWord_ = numeric_limits::digits; - const unsigned int BitArray::logBits_ = logDigits_14(bitsPerWord_); - const unsigned int BitArray::bitsMask_ = (1 << logBits_) - 1; - - size_t BitArray::wordsForBits(size_t bits) { - int arraySize = bits >> logBits_; - if (bits - (arraySize << logBits_) != 0) { - arraySize++; - } - return arraySize; - } - - BitArray::BitArray(size_t size) : + + +size_t BitArray::wordsForBits(size_t bits) { + int arraySize = (bits + bitsPerWord_ - 1) >> logBits_; + return arraySize; +} + +BitArray::BitArray(size_t size) : size_(size), bits_(wordsForBits(size), (const unsigned int)0) { +} + +BitArray::~BitArray() { +} + +size_t BitArray::getSize() { + return size_; +} + +void BitArray::setBulk(size_t i, unsigned int newBits) { + bits_[i >> logBits_] = newBits; +} + +void BitArray::setRange(int start, int end) { + if (end < start) { + throw IllegalArgumentException("invalid call to BitArray::setRange"); + } + if (end == start) { + return; + } + end--; // will be easier to treat this as the last actually set bit -- inclusive + int firstInt = start >> 5; + int lastInt = end >> 5; + for (int i = firstInt; i <= lastInt; i++) { + int firstBit = i > firstInt ? 0 : start & 0x1F; + int lastBit = i < lastInt ? 31 : end & 0x1F; + int mask; + if (firstBit == 0 && lastBit == 31) { + mask = -1; + } else { + mask = 0; + for (int j = firstBit; j <= lastBit; j++) { + mask |= 1 << j; + } } - - BitArray::~BitArray() { + bits_[i] |= mask; + } +} + +void BitArray::clear() { + size_t max = bits_.size(); + for (size_t i = 0; i < max; i++) { + bits_[i] = 0; + } +} + +bool BitArray::isRange(size_t start, size_t end, bool value) { + if (end < start) { + throw IllegalArgumentException("end must be after start"); + } + if (end == start) { + return true; + } + // treat the 'end' as inclusive, rather than exclusive + end--; + size_t firstWord = start >> logBits_; + size_t lastWord = end >> logBits_; + for (size_t i = firstWord; i <= lastWord; i++) { + size_t firstBit = i > firstWord ? 0 : start & bitsMask_; + size_t lastBit = i < lastWord ? bitsPerWord_ - 1: end & bitsMask_; + unsigned int mask; + if (firstBit == 0 && lastBit == bitsPerWord_ - 1) { + mask = numeric_limits::max(); + } else { + mask = 0; + for (size_t j = firstBit; j <= lastBit; j++) { + mask |= 1 << j; + } } - - size_t BitArray::getSize() { - return size_; + if (value) { + if ((bits_[i] & mask) != mask) { + return false; + } + } else { + if ((bits_[i] & mask) != 0) { + return false; + } } - - bool BitArray::get(size_t i) { - return (bits_[i >> logBits_] & (1 << (i & bitsMask_))) != 0; - } - - void BitArray::set(size_t i) { - bits_[i >> logBits_] |= 1 << (i & bitsMask_); - } - - void BitArray::setBulk(size_t i, unsigned int newBits) { - bits_[i >> logBits_] = newBits; - } - - void BitArray::clear() { - size_t max = bits_.size(); - for (size_t i = 0; i < max; i++) { - bits_[i] = 0; - } - } - - bool BitArray::isRange(size_t start, size_t end, bool value) { - if (end < start) { - throw IllegalArgumentException("end must be after start"); - } - if (end == start) { - return true; - } - // treat the 'end' as inclusive, rather than exclusive - end--; - size_t firstWord = start >> logBits_; - size_t lastWord = end >> logBits_; - for (size_t i = firstWord; i <= lastWord; i++) { - size_t firstBit = i > firstWord ? 0 : start & bitsMask_; - size_t lastBit = i < lastWord ? bitsPerWord_ - 1: end & bitsMask_; - unsigned int mask; - if (firstBit == 0 && lastBit == bitsPerWord_ - 1) { - mask = numeric_limits::max(); - } else { - mask = 0; - for (size_t j = firstBit; j <= lastBit; j++) { - mask |= 1 << j; - } - } - if (value) { - if ((bits_[i] & mask) != mask) { - return false; - } - } else { - if ((bits_[i] & mask) != 0) { - return false; - } - } - } - return true; - } - - vector& BitArray::getBitArray() { - return bits_; - } - - void BitArray::reverse() { - std::vector newBits(bits_.size(),(const unsigned int) 0); - for (size_t i = 0; i < size_; i++) { - if (get(size_ - i - 1)) { - newBits[i >> logBits_] |= 1<< (i & bitsMask_); - } - } - bits_ = newBits; + } + return true; +} + +vector& BitArray::getBitArray() { + return bits_; +} + +void BitArray::reverse() { + std::vector newBits(bits_.size(),(const unsigned int) 0); + for (size_t i = 0; i < size_; i++) { + if (get(size_ - i - 1)) { + newBits[i >> logBits_] |= 1<< (i & bitsMask_); } + } + bits_ = newBits; +} } // file: zxing/common/BitMatrix.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * BitMatrix.cpp - * zxing - * * Copyright 2010 ZXing authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -823,157 +1017,138 @@ namespace zxing { // #include // #include +using std::ostream; +using std::ostringstream; + +using zxing::BitMatrix; +using zxing::BitArray; +using zxing::Ref; + +namespace { + size_t wordsForSize(size_t width, + size_t height, + unsigned int bitsPerWord, + unsigned int logBits) { + size_t bits = width * height; + int arraySize = (bits + bitsPerWord - 1) >> logBits; + return arraySize; + } +} + +BitMatrix::BitMatrix(size_t dimension) : + width_(dimension), height_(dimension), words_(0), bits_(NULL) { + words_ = wordsForSize(width_, height_, bitsPerWord, logBits); + bits_ = new unsigned int[words_]; + clear(); +} + +BitMatrix::BitMatrix(size_t width, size_t height) : + width_(width), height_(height), words_(0), bits_(NULL) { + words_ = wordsForSize(width_, height_, bitsPerWord, logBits); + bits_ = new unsigned int[words_]; + clear(); +} + +BitMatrix::~BitMatrix() { + delete[] bits_; +} + + +void BitMatrix::flip(size_t x, size_t y) { + size_t offset = x + width_ * y; + bits_[offset >> logBits] ^= 1 << (offset & bitsMask); +} + +void BitMatrix::clear() { + std::fill(bits_, bits_+words_, 0); +} + +void BitMatrix::setRegion(size_t left, size_t top, size_t width, size_t height) { + if ((long)top < 0 || (long)left < 0) { + throw IllegalArgumentException("topI and leftJ must be nonnegative"); + } + if (height < 1 || width < 1) { + throw IllegalArgumentException("height and width must be at least 1"); + } + size_t right = left + width; + size_t bottom = top + height; + if (right > width_ || bottom > height_) { + throw IllegalArgumentException("top + height and left + width must be <= matrix dimension"); + } + for (size_t y = top; y < bottom; y++) { + int yOffset = width_ * y; + for (size_t x = left; x < right; x++) { + size_t offset = x + yOffset; + bits_[offset >> logBits] |= 1 << (offset & bitsMask); + } + } +} + +Ref BitMatrix::getRow(int y, Ref row) { + if (row.empty() || row->getSize() < width_) { + row = new BitArray(width_); + } else { + row->clear(); + } + size_t start = y * width_; + size_t end = start + width_ - 1; // end is inclusive + size_t firstWord = start >> logBits; + size_t lastWord = end >> logBits; + size_t bitOffset = start & bitsMask; + for (size_t i = firstWord; i <= lastWord; i++) { + size_t firstBit = i > firstWord ? 0 : start & bitsMask; + size_t lastBit = i < lastWord ? bitsPerWord - 1 : end & bitsMask; + unsigned int mask; + if (firstBit == 0 && lastBit == logBits) { + mask = std::numeric_limits::max(); + } else { + mask = 0; + for (size_t j = firstBit; j <= lastBit; j++) { + mask |= 1 << j; + } + } + row->setBulk((i - firstWord) << logBits, (bits_[i] & mask) >> bitOffset); + if (firstBit == 0 && bitOffset != 0) { + unsigned int prevBulk = row->getBitArray()[i - firstWord - 1]; + prevBulk |= (bits_[i] & mask) << (bitsPerWord - bitOffset); + row->setBulk((i - firstWord - 1) << logBits, prevBulk); + } + } + return row; +} + +size_t BitMatrix::getWidth() const { + return width_; +} + +size_t BitMatrix::getHeight() const { + return height_; +} + +size_t BitMatrix::getDimension() const { + return width_; +} + +unsigned int* BitMatrix::getBits() const { + return bits_; +} + namespace zxing { - using namespace std; - - unsigned int logDigits_15(unsigned digits) { - unsigned log = 0; - unsigned val = 1; - while (val < digits) { - log++; - val <<= 1; - } - return log; + ostream& operator<<(ostream &out, const BitMatrix &bm) { + for (size_t y = 0; y < bm.height_; y++) { + for (size_t x = 0; x < bm.width_; x++) { + out << (bm.get(x, y) ? "X " : " "); + } + out << "\n"; } - - const unsigned int bitsPerWord = numeric_limits::digits; - const unsigned int logBits = logDigits_15(bitsPerWord); - const unsigned int bitsMask = (1 << logBits) - 1; - - static size_t wordsForSize(size_t width, size_t height) { - size_t bits = width * height; - int arraySize = bits >> logBits; - if (bits - (arraySize << logBits) != 0) { - arraySize++; - } - return arraySize; - } - - BitMatrix::BitMatrix(size_t dimension) : - width_(dimension), height_(dimension), words_(0), bits_(NULL) { - - words_ = wordsForSize(width_, height_); - bits_ = new unsigned int[words_]; - clear(); - } - - BitMatrix::BitMatrix(size_t width, size_t height) : - width_(width), height_(height), words_(0), bits_(NULL) { - - words_ = wordsForSize(width_, height_); - bits_ = new unsigned int[words_]; - clear(); - } - - BitMatrix::~BitMatrix() { - delete[] bits_; - } - - - bool BitMatrix::get(size_t x, size_t y) const { - size_t offset = x + width_ * y; - return ((bits_[offset >> logBits] >> (offset & bitsMask)) & 0x01) != 0; - } - - void BitMatrix::set(size_t x, size_t y) { - size_t offset = x + width_ * y; - bits_[offset >> logBits] |= 1 << (offset & bitsMask); - } - - void BitMatrix::flip(size_t x, size_t y) { - size_t offset = x + width_ * y; - bits_[offset >> logBits] ^= 1 << (offset & bitsMask); - } - - void BitMatrix::clear() { - std::fill(bits_, bits_+words_, 0); - } - - void BitMatrix::setRegion(size_t left, size_t top, size_t width, size_t height) { - if ((long)top < 0 || (long)left < 0) { - throw IllegalArgumentException("topI and leftJ must be nonnegative"); - } - if (height < 1 || width < 1) { - throw IllegalArgumentException("height and width must be at least 1"); - } - size_t right = left + width; - size_t bottom = top + height; - if (right > width_ || bottom > height_) { - throw IllegalArgumentException("top + height and left + width must be <= matrix dimension"); - } - for (size_t y = top; y < bottom; y++) { - int yOffset = width_ * y; - for (size_t x = left; x < right; x++) { - size_t offset = x + yOffset; - bits_[offset >> logBits] |= 1 << (offset & bitsMask); - } - } - } - - Ref BitMatrix::getRow(int y, Ref row) { - if (row.empty() || row->getSize() < width_) { - row = new BitArray(width_); - } else { - row->clear(); - } - size_t start = y * width_; - size_t end = start + width_ - 1; // end is inclusive - size_t firstWord = start >> logBits; - size_t lastWord = end >> logBits; - size_t bitOffset = start & bitsMask; - for (size_t i = firstWord; i <= lastWord; i++) { - size_t firstBit = i > firstWord ? 0 : start & bitsMask; - size_t lastBit = i < lastWord ? bitsPerWord - 1 : end & bitsMask; - unsigned int mask; - if (firstBit == 0 && lastBit == logBits) { - mask = numeric_limits::max(); - } else { - mask = 0; - for (size_t j = firstBit; j <= lastBit; j++) { - mask |= 1 << j; - } - } - row->setBulk((i - firstWord) << logBits, (bits_[i] & mask) >> bitOffset); - if (firstBit == 0 && bitOffset != 0) { - unsigned int prevBulk = row->getBitArray()[i - firstWord - 1]; - prevBulk |= (bits_[i] & mask) << (bitsPerWord - bitOffset); - row->setBulk((i - firstWord - 1) << logBits, prevBulk); - } - } - return row; - } - - size_t BitMatrix::getWidth() const { - return width_; - } - - size_t BitMatrix::getHeight() const { - return height_; - } - - size_t BitMatrix::getDimension() const { - return width_; - } - - unsigned int* BitMatrix::getBits() const { - return bits_; - } - - ostream& operator<<(ostream &out, const BitMatrix &bm) { - for (size_t y = 0; y < bm.height_; y++) { - for (size_t x = 0; x < bm.width_; x++) { - out << (bm.get(x, y) ? "X " : " "); - } - out << "\n"; - } - return out; - } - const char *BitMatrix::description() { - ostringstream out; - out << *this; - return out.str().c_str(); - } - + return out; + } +} + +const char* BitMatrix::description() { + ostringstream out; + out << *this; + return out.str().c_str(); } // file: zxing/common/BitSource.cpp @@ -1002,56 +1177,148 @@ namespace zxing { // #include namespace zxing { - - int BitSource::readBits(int numBits) { - if (numBits < 0 || numBits > 32) { - throw IllegalArgumentException("cannot read <1 or >32 bits"); - } else if (numBits > available()) { - throw IllegalArgumentException("reading more bits than are available"); - } - - int result = 0; - - - // First, read remainder from current byte - if (bitOffset_ > 0) { - int bitsLeft = 8 - bitOffset_; - int toRead = numBits < bitsLeft ? numBits : bitsLeft; - int bitsToNotRead = bitsLeft - toRead; - int mask = (0xFF >> (8 - toRead)) << bitsToNotRead; - result = (bytes_[byteOffset_] & mask) >> bitsToNotRead; - numBits -= toRead; - bitOffset_ += toRead; - if (bitOffset_ == 8) { - bitOffset_ = 0; - byteOffset_++; - } - } - - // Next read whole bytes - if (numBits > 0) { - while (numBits >= 8) { - result = (result << 8) | (bytes_[byteOffset_] & 0xFF); - byteOffset_++; - numBits -= 8; - } - - - // Finally read a partial byte - if (numBits > 0) { - int bitsToNotRead = 8 - numBits; - int mask = (0xFF >> bitsToNotRead) << bitsToNotRead; - result = (result << numBits) | ((bytes_[byteOffset_] & mask) >> bitsToNotRead); - bitOffset_ += numBits; - } - } - - return result; + +int BitSource::readBits(int numBits) { + if (numBits < 0 || numBits > 32) { + throw IllegalArgumentException("cannot read <1 or >32 bits"); + } else if (numBits > available()) { + throw IllegalArgumentException("reading more bits than are available"); + } + + int result = 0; + + // First, read remainder from current byte + if (bitOffset_ > 0) { + int bitsLeft = 8 - bitOffset_; + int toRead = numBits < bitsLeft ? numBits : bitsLeft; + int bitsToNotRead = bitsLeft - toRead; + int mask = (0xFF >> (8 - toRead)) << bitsToNotRead; + result = (bytes_[byteOffset_] & mask) >> bitsToNotRead; + numBits -= toRead; + bitOffset_ += toRead; + if (bitOffset_ == 8) { + bitOffset_ = 0; + byteOffset_++; } - - int BitSource::available() { - return 8 * (bytes_.size() - byteOffset_) - bitOffset_; + } + + // Next read whole bytes + if (numBits > 0) { + while (numBits >= 8) { + result = (result << 8) | (bytes_[byteOffset_] & 0xFF); + byteOffset_++; + numBits -= 8; } + + + // Finally read a partial byte + if (numBits > 0) { + int bitsToNotRead = 8 - numBits; + int mask = (0xFF >> bitsToNotRead) << bitsToNotRead; + result = (result << numBits) | ((bytes_[byteOffset_] & mask) >> bitsToNotRead); + bitOffset_ += numBits; + } + } + + return result; +} + +int BitSource::available() { + return 8 * (bytes_.size() - byteOffset_) - bitOffset_; +} +} + +// file: zxing/common/CharacterSetECI.cpp + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- +/* + * Copyright 2008-2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include + +using std::string; + +using zxing::common::CharacterSetECI; +using zxing::IllegalArgumentException; + +std::map CharacterSetECI::VALUE_TO_ECI; +std::map CharacterSetECI::NAME_TO_ECI; + +const bool CharacterSetECI::inited = CharacterSetECI::init_tables(); + +bool CharacterSetECI::init_tables() { + addCharacterSet(0, "Cp437"); + { char const* s[] = {"ISO8859_1", "ISO-8859-1", 0}; + addCharacterSet(1, s); } + addCharacterSet(2, "Cp437"); + { char const* s[] = {"ISO8859_1", "ISO-8859-1", 0}; + addCharacterSet(3, s); } + addCharacterSet(4, "ISO8859_2"); + addCharacterSet(5, "ISO8859_3"); + addCharacterSet(6, "ISO8859_4"); + addCharacterSet(7, "ISO8859_5"); + addCharacterSet(8, "ISO8859_6"); + addCharacterSet(9, "ISO8859_7"); + addCharacterSet(10, "ISO8859_8"); + addCharacterSet(11, "ISO8859_9"); + addCharacterSet(12, "ISO8859_10"); + addCharacterSet(13, "ISO8859_11"); + addCharacterSet(15, "ISO8859_13"); + addCharacterSet(16, "ISO8859_14"); + addCharacterSet(17, "ISO8859_15"); + addCharacterSet(18, "ISO8859_16"); + { char const* s[] = {"SJIS", "Shift_JIS", 0}; + addCharacterSet(20, s ); } + return true; +} + +CharacterSetECI::CharacterSetECI(int value, char const* encodingName_) + : ECI(value), encodingName(encodingName_) {} + +char const* CharacterSetECI::getEncodingName() { + return encodingName; +} + +void CharacterSetECI::addCharacterSet(int value, char const* encodingName) { + CharacterSetECI* eci = new CharacterSetECI(value, encodingName); + VALUE_TO_ECI[value] = eci; // can't use valueOf + NAME_TO_ECI[string(encodingName)] = eci; +} + +void CharacterSetECI::addCharacterSet(int value, char const* const* encodingNames) { + CharacterSetECI* eci = new CharacterSetECI(value, encodingNames[0]); + VALUE_TO_ECI[value] = eci; + for (int i = 0; encodingNames[i]; i++) { + NAME_TO_ECI[string(encodingNames[i])] = eci; + } +} + +CharacterSetECI* CharacterSetECI::getCharacterSetECIByValue(int value) { + if (value < 0 || value >= 900) { + std::ostringstream oss; + oss << "Bad ECI value: " << value; + throw IllegalArgumentException(oss.str().c_str()); + } + return VALUE_TO_ECI[value]; +} + +CharacterSetECI* CharacterSetECI::getCharacterSetECIByName(string const& name) { + return NAME_TO_ECI[name]; } // file: zxing/common/Counted.cpp @@ -1079,24 +1346,25 @@ namespace zxing { // #include namespace zxing { - - using namespace std; - - template - ostream& operator<<(ostream &out, Ref& ref) { - out << "Ref(" << (ref.object_ ? (*ref.object_) : "NULL") << ")"; - return out; - } + +using namespace std; + +template +ostream& operator<<(ostream &out, Ref& ref) { + out << "Ref(" << (ref.object_ ? (*ref.object_) : "NULL") << ")"; + return out; +} } // file: zxing/common/DecoderResult.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * DecoderResult.cpp * zxing * * Created by Christian Brunschen on 20/05/2008. - * Copyright 2008 ZXing authors All rights reserved. + * Copyright 2008-2011 ZXing authors 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. @@ -1113,24 +1381,33 @@ namespace zxing { // #include -namespace zxing { - - DecoderResult::DecoderResult(ArrayRef rawBytes, Ref text) : - rawBytes_(rawBytes), text_(text) { - } - - ArrayRef DecoderResult::getRawBytes() { - return rawBytes_; - } - - Ref DecoderResult::getText() { - return text_; - } - +using namespace std; +using namespace zxing; + +DecoderResult::DecoderResult(ArrayRef rawBytes, + Ref text, + ArrayRef< ArrayRef >& byteSegments, + string const& ecLevel) : + rawBytes_(rawBytes), + text_(text), + byteSegments_(byteSegments), + ecLevel_(ecLevel) {} + +DecoderResult::DecoderResult(ArrayRef rawBytes, + Ref text) + : rawBytes_(rawBytes), text_(text) {} + +ArrayRef DecoderResult::getRawBytes() { + return rawBytes_; +} + +Ref DecoderResult::getText() { + return text_; } // file: zxing/common/DetectorResult.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * DetectorResult.cpp * zxing @@ -1154,23 +1431,68 @@ namespace zxing { // #include namespace zxing { - - DetectorResult::DetectorResult(Ref bits, std::vector > points, Ref transform) : - bits_(bits), points_(points), transform_(transform) { - } - - Ref DetectorResult::getBits() { - return bits_; - } - - std::vector > DetectorResult::getPoints() { - return points_; - } - - Ref DetectorResult::getTransform() { - return transform_; - } - + +DetectorResult::DetectorResult(Ref bits, std::vector > points, Ref transform) : + bits_(bits), points_(points), transform_(transform) { +} + +Ref DetectorResult::getBits() { + return bits_; +} + +std::vector > DetectorResult::getPoints() { + return points_; +} + +Ref DetectorResult::getTransform() { + return transform_; +} + +} + +// file: zxing/common/ECI.cpp + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- +/* + * Copyright 2008-2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include +// #include + +using zxing::common::ECI; +using zxing::IllegalArgumentException; + +ECI::ECI(int value_) : value(value_) {} + +int ECI::getValue() const { + return value; +} + +ECI* ECI::getECIByValue(int value) { + if (value < 0 || value > 999999) { + std::ostringstream oss; + oss << "Bad ECI value: " << value; + throw IllegalArgumentException(oss.str().c_str()); + } + if (value < 900) { // Character set ECIs use 000000 - 000899 + return CharacterSetECI::getCharacterSetECIByValue(value); + } + return 0; } // file: zxing/common/EdgeDetector.cpp @@ -1202,173 +1524,174 @@ namespace zxing { using namespace std; namespace zxing { - namespace EdgeDetector { - - void findEdgePoints(std::vector& points, const BitMatrix& image, Point start, Point end, bool invert, int skip, float deviation) { - float xdist = end.x - start.x; - float ydist = end.y - start.y; - float length = sqrt(xdist * xdist + ydist * ydist); - - - int var; - - if (abs(xdist) > abs(ydist)) { - // Horizontal - if (xdist < 0) - skip = -skip; - - var = int(abs(deviation * length / xdist)); - - float dy = ydist / xdist * skip; - bool left = (skip < 0) ^ invert; - int x = int(start.x); - - int steps = int(xdist / skip); - for (int i = 0; i < steps; i++) { - x += skip; - if (x < 0 || x >= (int)image.getWidth()) - continue; // In case we start off the edge - int my = int(start.y + dy * i); - int ey = min(my + var + 1, (int)image.getHeight() - 1); - int sy = max(my - var, 0); - for (int y = sy + 1; y < ey; y++) { - if (left) { - if (image.get(x, y) && !image.get(x, y + 1)) { - points.push_back(Point(x, y + 0.5f)); - } - } else { - if (!image.get(x, y) && image.get(x, y + 1)) { - points.push_back(Point(x, y + 0.5f)); - } - } - } - } - } else { - // Vertical - if (ydist < 0) - skip = -skip; - - var = int(abs(deviation * length / ydist)); - - float dx = xdist / ydist * skip; - bool down = (skip > 0) ^ invert; - int y = int(start.y); - - int steps = int(ydist / skip); - for (int i = 0; i < steps; i++) { - y += skip; - if (y < 0 || y >= (int)image.getHeight()) - continue; // In case we start off the edge - int mx = int(start.x + dx * i); - int ex = min(mx + var + 1, (int)image.getWidth() - 1); - int sx = max(mx - var, 0); - for (int x = sx + 1; x < ex; x++) { - if (down) { - if (image.get(x, y) && !image.get(x + 1, y)) { - points.push_back(Point(x + 0.5f, y)); - } - - } else { - if (!image.get(x, y) && image.get(x + 1, y)) { - points.push_back(Point(x + 0.5f, y)); - } - } - - } - } - - } +namespace EdgeDetector { + +void findEdgePoints(std::vector& points, const BitMatrix& image, Point start, Point end, bool invert, int skip, float deviation) { + float xdist = end.x - start.x; + float ydist = end.y - start.y; + float length = sqrt(xdist * xdist + ydist * ydist); + + + int var; + + if (abs(xdist) > abs(ydist)) { + // Horizontal + if (xdist < 0) + skip = -skip; + + var = int(abs(deviation * length / xdist)); + + float dy = ydist / xdist * skip; + bool left = (skip < 0) ^ invert; + int x = int(start.x); + + int steps = int(xdist / skip); + for (int i = 0; i < steps; i++) { + x += skip; + if (x < 0 || x >= (int)image.getWidth()) + continue; // In case we start off the edge + int my = int(start.y + dy * i); + int ey = min(my + var + 1, (int)image.getHeight() - 1); + int sy = max(my - var, 0); + for (int y = sy + 1; y < ey; y++) { + if (left) { + if (image.get(x, y) && !image.get(x, y + 1)) { + points.push_back(Point(x, y + 0.5f)); + } + } else { + if (!image.get(x, y) && image.get(x, y + 1)) { + points.push_back(Point(x, y + 0.5f)); + } } - - Line findLine(const BitMatrix& image, Line estimate, bool invert, int deviation, float threshold, int skip) { - float t = threshold * threshold; - - Point start = estimate.start; - Point end = estimate.end; - - vector edges; - edges.clear(); - findEdgePoints(edges, image, start, end, invert, skip, deviation); - - int n = edges.size(); - - float xdist = end.x - start.x; - float ydist = end.y - start.y; - - bool horizontal = abs(xdist) > abs(ydist); - - float max = 0; - Line bestLine(start, end); // prepopulate with the given line, in case we can't find any line for some reason - - for (int i = -deviation; i < deviation; i++) { - float x1, y1; - if (horizontal) { - y1 = start.y + i; - x1 = start.x - i * ydist / xdist; - } else { - y1 = start.y - i * xdist / ydist; - x1 = start.x + i; - } - - for (int j = -deviation; j < deviation; j++) { - float x2, y2; - if (horizontal) { - y2 = end.y + j; - x2 = end.x - j * ydist / xdist; - } else { - y2 = end.y - j * xdist / ydist; - x2 = end.x + j; - } - - float dx = x1 - x2; - float dy = y1 - y2; - float length = sqrt(dx * dx + dy * dy); - - float score = 0; - - for(int k = 0; k < n; k++) { - const Point& edge = edges[k]; - float dist = ((x1 - edge.x) * dy - (y1 - edge.y) * dx) / length; - // Similar to least squares method - float s = t - dist * dist; - if (s > 0) - score += s; - } - - if (score > max) { - max = score; - bestLine.start = Point(x1, y1); - bestLine.end = Point(x2, y2); - } - } - } - - return bestLine; + } + } + } else { + // Vertical + if (ydist < 0) + skip = -skip; + + var = int(abs(deviation * length / ydist)); + + float dx = xdist / ydist * skip; + bool down = (skip > 0) ^ invert; + int y = int(start.y); + + int steps = int(ydist / skip); + for (int i = 0; i < steps; i++) { + y += skip; + if (y < 0 || y >= (int)image.getHeight()) + continue; // In case we start off the edge + int mx = int(start.x + dx * i); + int ex = min(mx + var + 1, (int)image.getWidth() - 1); + int sx = max(mx - var, 0); + for (int x = sx + 1; x < ex; x++) { + if (down) { + if (image.get(x, y) && !image.get(x + 1, y)) { + points.push_back(Point(x + 0.5f, y)); + } + + } else { + if (!image.get(x, y) && image.get(x + 1, y)) { + points.push_back(Point(x + 0.5f, y)); + } } - - Point intersection(Line a, Line b) { - float dxa = a.start.x - a.end.x; - float dxb = b.start.x - b.end.x; - float dya = a.start.y - a.end.y; - float dyb = b.start.y - b.end.y; - - float p = a.start.x * a.end.y - a.start.y * a.end.x; - float q = b.start.x * b.end.y - b.start.y * b.end.x; - float denom = dxa * dyb - dya * dxb; - if(denom == 0) // Lines don't intersect - return Point(INFINITY, INFINITY); - - float x = (p * dxb - dxa * q) / denom; - float y = (p * dyb - dya * q) / denom; - - return Point(x, y); - } - - } // namespace EdgeDetector + + } + } + + } +} + +Line findLine(const BitMatrix& image, Line estimate, bool invert, int deviation, float threshold, int skip) { + float t = threshold * threshold; + + Point start = estimate.start; + Point end = estimate.end; + + vector edges; + edges.clear(); + findEdgePoints(edges, image, start, end, invert, skip, deviation); + + int n = edges.size(); + + float xdist = end.x - start.x; + float ydist = end.y - start.y; + + bool horizontal = abs(xdist) > abs(ydist); + + float max = 0; + Line bestLine(start, end); // prepopulate with the given line, in case we can't find any line for some reason + + for (int i = -deviation; i < deviation; i++) { + float x1, y1; + if (horizontal) { + y1 = start.y + i; + x1 = start.x - i * ydist / xdist; + } else { + y1 = start.y - i * xdist / ydist; + x1 = start.x + i; + } + + for (int j = -deviation; j < deviation; j++) { + float x2, y2; + if (horizontal) { + y2 = end.y + j; + x2 = end.x - j * ydist / xdist; + } else { + y2 = end.y - j * xdist / ydist; + x2 = end.x + j; + } + + float dx = x1 - x2; + float dy = y1 - y2; + float length = sqrt(dx * dx + dy * dy); + + float score = 0; + + for(int k = 0; k < n; k++) { + const Point& edge = edges[k]; + float dist = ((x1 - edge.x) * dy - (y1 - edge.y) * dx) / length; + // Similar to least squares method + float s = t - dist * dist; + if (s > 0) + score += s; + } + + if (score > max) { + max = score; + bestLine.start = Point(x1, y1); + bestLine.end = Point(x2, y2); + } + } + } + + return bestLine; +} + +Point intersection(Line a, Line b) { + float dxa = a.start.x - a.end.x; + float dxb = b.start.x - b.end.x; + float dya = a.start.y - a.end.y; + float dyb = b.start.y - b.end.y; + + float p = a.start.x * a.end.y - a.start.y * a.end.x; + float q = b.start.x * b.end.y - b.start.y * b.end.x; + float denom = dxa * dyb - dya * dxb; + if(denom == 0) // Lines don't intersect + return Point(INFINITY, INFINITY); + + float x = (p * dxb - dxa * q) / denom; + float y = (p * dyb - dya * q) / denom; + + return Point(x, y); +} + +} // namespace EdgeDetector } // namespace zxing // file: zxing/common/GlobalHistogramBinarizer.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * GlobalHistogramBinarizer.cpp * zxing @@ -1389,194 +1712,193 @@ namespace zxing { */ // #include - // #include +// #include namespace zxing { - using namespace std; - - const int LUMINANCE_BITS_21 = 5; - const int LUMINANCE_SHIFT_21 = 8 - LUMINANCE_BITS_21; - const int LUMINANCE_BUCKETS_21 = 1 << LUMINANCE_BITS_21; - - GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref source) : - Binarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) { - +using namespace std; + +const int LUMINANCE_BITS_25 = 5; +const int LUMINANCE_SHIFT_25 = 8 - LUMINANCE_BITS_25; +const int LUMINANCE_BUCKETS_25 = 1 << LUMINANCE_BITS_25; + +GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref source) : + Binarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) { + +} + +GlobalHistogramBinarizer::~GlobalHistogramBinarizer() { +} + + +Ref GlobalHistogramBinarizer::getBlackRow(int y, Ref row) { + if (y == cached_row_num_) { + if (cached_row_ != NULL) { + return cached_row_; + } else { + throw IllegalArgumentException("Too little dynamic range in luminance"); } - - GlobalHistogramBinarizer::~GlobalHistogramBinarizer() { + } + + vector histogram(LUMINANCE_BUCKETS_25, 0); + LuminanceSource& source = *getLuminanceSource(); + int width = source.getWidth(); + if (row == NULL || static_cast(row->getSize()) < width) { + row = new BitArray(width); + } else { + row->clear(); + } + + //TODO(flyashi): cache this instead of allocating and deleting per row + unsigned char* row_pixels = NULL; + try { + row_pixels = new unsigned char[width]; + row_pixels = source.getRow(y, row_pixels); + for (int x = 0; x < width; x++) { + histogram[row_pixels[x] >> LUMINANCE_SHIFT_25]++; } - - - Ref GlobalHistogramBinarizer::getBlackRow(int y, Ref row) { - if (y == cached_row_num_) { - if (cached_row_ != NULL) { - return cached_row_; - } else { - throw IllegalArgumentException("Too little dynamic range in luminance"); - } - } - - vector histogram(LUMINANCE_BUCKETS_21, 0); - LuminanceSource& source = *getLuminanceSource(); - int width = source.getWidth(); - if (row == NULL || static_cast(row->getSize()) < width) { - row = new BitArray(width); - } else { - row->clear(); - } - - //TODO(flyashi): cache this instead of allocating and deleting per row - unsigned char* row_pixels = NULL; - try { - row_pixels = new unsigned char[width]; - row_pixels = source.getRow(y, row_pixels); - for (int x = 0; x < width; x++) { - histogram[row_pixels[x] >> LUMINANCE_SHIFT_21]++; - } - int blackPoint = estimate(histogram) << LUMINANCE_SHIFT_21; - - BitArray& array = *row; - int left = row_pixels[0]; - int center = row_pixels[1]; - for (int x = 1; x < width - 1; x++) { - int right = row_pixels[x + 1]; - // A simple -1 4 -1 box filter with a weight of 2. - int luminance = ((center << 2) - left - right) >> 1; - if (luminance < blackPoint) { - array.set(x); - } - left = center; - center = right; - } - - cached_row_ = row; - cached_row_num_ = y; - delete [] row_pixels; - return row; - } catch (IllegalArgumentException const& iae) { - // Cache the fact that this row failed. - cached_row_ = NULL; - cached_row_num_ = y; - delete [] row_pixels; - throw iae; - } + int blackPoint = estimate(histogram); + + BitArray& array = *row; + int left = row_pixels[0]; + int center = row_pixels[1]; + for (int x = 1; x < width - 1; x++) { + int right = row_pixels[x + 1]; + // A simple -1 4 -1 box filter with a weight of 2. + int luminance = ((center << 2) - left - right) >> 1; + if (luminance < blackPoint) { + array.set(x); + } + left = center; + center = right; } - - Ref GlobalHistogramBinarizer::getBlackMatrix() { - if (cached_matrix_ != NULL) { - return cached_matrix_; - } - - // Faster than working with the reference - LuminanceSource& source = *getLuminanceSource(); - int width = source.getWidth(); - int height = source.getHeight(); - vector histogram(LUMINANCE_BUCKETS_21, 0); - - // Quickly calculates the histogram by sampling four rows from the image. - // This proved to be more robust on the blackbox tests than sampling a - // diagonal as we used to do. - unsigned char* row = new unsigned char[width]; - for (int y = 1; y < 5; y++) { - int rownum = height * y / 5; - int right = (width << 2) / 5; - int sdf; - row = source.getRow(rownum, row); - for (int x = width / 5; x < right; x++) { - histogram[row[x] >> LUMINANCE_SHIFT_21]++; - sdf = histogram[row[x] >> LUMINANCE_SHIFT_21]; - } - } - - int blackPoint = estimate(histogram) << LUMINANCE_SHIFT_21; - - Ref matrix_ref(new BitMatrix(width, height)); - BitMatrix& matrix = *matrix_ref; - for (int y = 0; y < height; y++) { - row = source.getRow(y, row); - for (int x = 0; x < width; x++) { - if (row[x] <= blackPoint) - matrix.set(x, y); - } - } - - cached_matrix_ = matrix_ref; - delete [] row; - return matrix_ref; + + cached_row_ = row; + cached_row_num_ = y; + delete [] row_pixels; + return row; + } catch (IllegalArgumentException const& iae) { + // Cache the fact that this row failed. + cached_row_ = NULL; + cached_row_num_ = y; + delete [] row_pixels; + throw iae; + } +} + +Ref GlobalHistogramBinarizer::getBlackMatrix() { + if (cached_matrix_ != NULL) { + return cached_matrix_; + } + + // Faster than working with the reference + LuminanceSource& source = *getLuminanceSource(); + int width = source.getWidth(); + int height = source.getHeight(); + vector histogram(LUMINANCE_BUCKETS_25, 0); + + // Quickly calculates the histogram by sampling four rows from the image. + // This proved to be more robust on the blackbox tests than sampling a + // diagonal as we used to do. + ArrayRef ref (width); + unsigned char* row = &ref[0]; + for (int y = 1; y < 5; y++) { + int rownum = height * y / 5; + int right = (width << 2) / 5; + row = source.getRow(rownum, row); + for (int x = width / 5; x < right; x++) { + histogram[row[x] >> LUMINANCE_SHIFT_25]++; } - - int GlobalHistogramBinarizer::estimate(vector &histogram) { - int numBuckets = histogram.size(); - int maxBucketCount = 0; - - // Find tallest peak in histogram - int firstPeak = 0; - int firstPeakSize = 0; - for (int i = 0; i < numBuckets; i++) { - if (histogram[i] > firstPeakSize) { - firstPeak = i; - firstPeakSize = histogram[i]; - } - if (histogram[i] > maxBucketCount) { - maxBucketCount = histogram[i]; - } - } - - // Find second-tallest peak -- well, another peak that is tall and not - // so close to the first one - int secondPeak = 0; - int secondPeakScore = 0; - for (int i = 0; i < numBuckets; i++) { - int distanceToBiggest = i - firstPeak; - // Encourage more distant second peaks by multiplying by square of distance - int score = histogram[i] * distanceToBiggest * distanceToBiggest; - if (score > secondPeakScore) { - secondPeak = i; - secondPeakScore = score; - } - } - - // Put firstPeak first - if (firstPeak > secondPeak) { - int temp = firstPeak; - firstPeak = secondPeak; - secondPeak = temp; - } - - // Kind of arbitrary; if the two peaks are very close, then we figure there is - // so little dynamic range in the image, that discriminating black and white - // is too error-prone. - // Decoding the image/line is either pointless, or may in some cases lead to - // a false positive for 1D formats, which are relatively lenient. - // We arbitrarily say "close" is - // "<= 1/16 of the total histogram buckets apart" - if (secondPeak - firstPeak <= numBuckets >> 4) { - throw IllegalArgumentException("Too little dynamic range in luminance"); - } - - // Find a valley between them that is low and closer to the white peak - int bestValley = secondPeak - 1; - int bestValleyScore = -1; - for (int i = secondPeak - 1; i > firstPeak; i--) { - int fromFirst = i - firstPeak; - // Favor a "valley" that is not too close to either peak -- especially not - // the black peak -- and that has a low value of course - int score = fromFirst * fromFirst * (secondPeak - i) * - (maxBucketCount - histogram[i]); - if (score > bestValleyScore) { - bestValley = i; - bestValleyScore = score; - } - } - - return bestValley; + } + + int blackPoint = estimate(histogram); + + Ref matrix_ref(new BitMatrix(width, height)); + BitMatrix& matrix = *matrix_ref; + for (int y = 0; y < height; y++) { + row = source.getRow(y, row); + for (int x = 0; x < width; x++) { + if (row[x] < blackPoint) + matrix.set(x, y); } - - Ref GlobalHistogramBinarizer::createBinarizer(Ref source) { - return Ref (new GlobalHistogramBinarizer(source)); + } + + cached_matrix_ = matrix_ref; + // delete [] row; + return matrix_ref; +} + +int GlobalHistogramBinarizer::estimate(vector &histogram) { + int numBuckets = histogram.size(); + int maxBucketCount = 0; + + // Find tallest peak in histogram + int firstPeak = 0; + int firstPeakSize = 0; + for (int i = 0; i < numBuckets; i++) { + if (histogram[i] > firstPeakSize) { + firstPeak = i; + firstPeakSize = histogram[i]; } - + if (histogram[i] > maxBucketCount) { + maxBucketCount = histogram[i]; + } + } + + // Find second-tallest peak -- well, another peak that is tall and not + // so close to the first one + int secondPeak = 0; + int secondPeakScore = 0; + for (int i = 0; i < numBuckets; i++) { + int distanceToBiggest = i - firstPeak; + // Encourage more distant second peaks by multiplying by square of distance + int score = histogram[i] * distanceToBiggest * distanceToBiggest; + if (score > secondPeakScore) { + secondPeak = i; + secondPeakScore = score; + } + } + + // Put firstPeak first + if (firstPeak > secondPeak) { + int temp = firstPeak; + firstPeak = secondPeak; + secondPeak = temp; + } + + // Kind of arbitrary; if the two peaks are very close, then we figure there is + // so little dynamic range in the image, that discriminating black and white + // is too error-prone. + // Decoding the image/line is either pointless, or may in some cases lead to + // a false positive for 1D formats, which are relatively lenient. + // We arbitrarily say "close" is + // "<= 1/16 of the total histogram buckets apart" + if (secondPeak - firstPeak <= numBuckets >> 4) { + throw IllegalArgumentException("Too little dynamic range in luminance"); + } + + // Find a valley between them that is low and closer to the white peak + int bestValley = secondPeak - 1; + int bestValleyScore = -1; + for (int i = secondPeak - 1; i > firstPeak; i--) { + int fromFirst = i - firstPeak; + // Favor a "valley" that is not too close to either peak -- especially not + // the black peak -- and that has a low value of course + int score = fromFirst * fromFirst * (secondPeak - i) * + (maxBucketCount - histogram[i]); + if (score > bestValleyScore) { + bestValley = i; + bestValleyScore = score; + } + } + + return bestValley << LUMINANCE_SHIFT_25; +} + +Ref GlobalHistogramBinarizer::createBinarizer(Ref source) { + return Ref (new GlobalHistogramBinarizer(source)); +} + } // namespace zxing // file: zxing/common/GreyscaleLuminanceSource.cpp @@ -1605,51 +1927,51 @@ namespace zxing { // #include namespace zxing { - - GreyscaleLuminanceSource::GreyscaleLuminanceSource(unsigned char* greyData, int dataWidth, - int dataHeight, int left, int top, int width, int height) : greyData_(greyData), + +GreyscaleLuminanceSource::GreyscaleLuminanceSource(unsigned char* greyData, int dataWidth, + int dataHeight, int left, int top, int width, int height) : greyData_(greyData), dataWidth_(dataWidth), dataHeight_(dataHeight), left_(left), top_(top), width_(width), height_(height) { - - if (left + width > dataWidth || top + height > dataHeight || top < 0 || left < 0) { - throw IllegalArgumentException("Crop rectangle does not fit within image data."); - } + + if (left + width > dataWidth || top + height > dataHeight || top < 0 || left < 0) { + throw IllegalArgumentException("Crop rectangle does not fit within image data."); + } +} + +unsigned char* GreyscaleLuminanceSource::getRow(int y, unsigned char* row) { + if (y < 0 || y >= this->getHeight()) { + throw IllegalArgumentException("Requested row is outside the image: " + y); + } + int width = getWidth(); + // TODO(flyashi): determine if row has enough size. + if (row == NULL) { + row = new unsigned char[width_]; + } + int offset = (y + top_) * dataWidth_ + left_; + memcpy(row, &greyData_[offset], width); + return row; +} + +unsigned char* GreyscaleLuminanceSource::getMatrix() { + int size = width_ * height_; + unsigned char* result = new unsigned char[size]; + if (left_ == 0 && top_ == 0 && dataWidth_ == width_ && dataHeight_ == height_) { + memcpy(result, greyData_, size); + } else { + for (int row = 0; row < height_; row++) { + memcpy(result + row * width_, greyData_ + (top_ + row) * dataWidth_ + left_, width_); } - - unsigned char* GreyscaleLuminanceSource::getRow(int y, unsigned char* row) { - if (y < 0 || y >= this->getHeight()) { - throw IllegalArgumentException("Requested row is outside the image: " + y); - } - int width = getWidth(); - // TODO(flyashi): determine if row has enough size. - if (row == NULL) { - row = new unsigned char[width_]; - } - int offset = (y + top_) * dataWidth_ + left_; - memcpy(row, &greyData_[offset], width); - return row; - } - - unsigned char* GreyscaleLuminanceSource::getMatrix() { - int size = width_ * height_; - unsigned char* result = new unsigned char[size]; - if (left_ == 0 && top_ == 0 && dataWidth_ == width_ && dataHeight_ == height_) { - memcpy(result, greyData_, size); - } else { - for (int row = 0; row < height_; row++) { - memcpy(result + row * width_, greyData_ + (top_ + row) * dataWidth_ + left_, width_); - } - } - return result; - } - - Ref GreyscaleLuminanceSource::rotateCounterClockwise() { - // Intentionally flip the left, top, width, and height arguments as needed. dataWidth and - // dataHeight are always kept unrotated. - return Ref (new GreyscaleRotatedLuminanceSource(greyData_, dataWidth_, - dataHeight_, top_, left_, height_, width_)); - } - + } + return result; +} + +Ref GreyscaleLuminanceSource::rotateCounterClockwise() { + // Intentionally flip the left, top, width, and height arguments as needed. dataWidth and + // dataHeight are always kept unrotated. + return Ref (new GreyscaleRotatedLuminanceSource(greyData_, dataWidth_, + dataHeight_, top_, left_, height_, width_)); +} + } /* namespace */ // file: zxing/common/GreyscaleRotatedLuminanceSource.cpp @@ -1678,46 +2000,46 @@ namespace zxing { // #include namespace zxing { - - // Note that dataWidth and dataHeight are not reversed, as we need to be able to traverse the - // greyData correctly, which does not get rotated. - GreyscaleRotatedLuminanceSource::GreyscaleRotatedLuminanceSource(unsigned char* greyData, - int dataWidth, int dataHeight, int left, int top, int width, int height) : greyData_(greyData), + +// Note that dataWidth and dataHeight are not reversed, as we need to be able to traverse the +// greyData correctly, which does not get rotated. +GreyscaleRotatedLuminanceSource::GreyscaleRotatedLuminanceSource(unsigned char* greyData, + int dataWidth, int dataHeight, int left, int top, int width, int height) : greyData_(greyData), dataWidth_(dataWidth), dataHeight_(dataHeight), left_(left), top_(top), width_(width), height_(height) { - - // Intentionally comparing to the opposite dimension since we're rotated. - if (left + width > dataHeight || top + height > dataWidth) { - throw IllegalArgumentException("Crop rectangle does not fit within image data."); - } - } - - // The API asks for rows, but we're rotated, so we return columns. - unsigned char* GreyscaleRotatedLuminanceSource::getRow(int y, unsigned char* row) { - if (y < 0 || y >= getHeight()) { - throw IllegalArgumentException("Requested row is outside the image: " + y); - } - int width = getWidth(); - if (row == NULL) { - row = new unsigned char[width]; - } - int offset = (left_ * dataWidth_) + (dataWidth_ - (y + top_)); - for (int x = 0; x < width; x++) { - row[x] = greyData_[offset]; - offset += dataWidth_; - } - return row; - } - - unsigned char* GreyscaleRotatedLuminanceSource::getMatrix() { - unsigned char* result = new unsigned char[width_ * height_]; - // This depends on getRow() honoring its second parameter. - for (int y = 0; y < height_; y++) { - getRow(y, &result[y * width_]); - } - return result; - } - + + // Intentionally comparing to the opposite dimension since we're rotated. + if (left + width > dataHeight || top + height > dataWidth) { + throw IllegalArgumentException("Crop rectangle does not fit within image data."); + } +} + +// The API asks for rows, but we're rotated, so we return columns. +unsigned char* GreyscaleRotatedLuminanceSource::getRow(int y, unsigned char* row) { + if (y < 0 || y >= getHeight()) { + throw IllegalArgumentException("Requested row is outside the image: " + y); + } + int width = getWidth(); + if (row == NULL) { + row = new unsigned char[width]; + } + int offset = (left_ * dataWidth_) + (dataWidth_ - (y + top_)); + for (int x = 0; x < width; x++) { + row[x] = greyData_[offset]; + offset += dataWidth_; + } + return row; +} + +unsigned char* GreyscaleRotatedLuminanceSource::getMatrix() { + unsigned char* result = new unsigned char[width_ * height_]; + // This depends on getRow() honoring its second parameter. + for (int y = 0; y < height_; y++) { + getRow(y, &result[y * width_]); + } + return result; +} + } // namespace // file: zxing/common/GridSampler.cpp @@ -1749,83 +2071,105 @@ namespace zxing { // #include namespace zxing { - using namespace std; - - GridSampler GridSampler::gridSampler; - - GridSampler::GridSampler() { +using namespace std; + +GridSampler GridSampler::gridSampler; + +GridSampler::GridSampler() { +} + +Ref GridSampler::sampleGrid(Ref image, int dimension, Ref transform) { + Ref bits(new BitMatrix(dimension)); + vector points(dimension << 1, (const float)0.0f); + for (int y = 0; y < dimension; y++) { + int max = points.size(); + float yValue = (float)y + 0.5f; + for (int x = 0; x < max; x += 2) { + points[x] = (float)(x >> 1) + 0.5f; + points[x + 1] = yValue; } - - Ref GridSampler::sampleGrid(Ref image, int dimension, Ref transform) { - Ref bits(new BitMatrix(dimension)); - vector points(dimension << 1, (const float)0.0f); - for (int y = 0; y < dimension; y++) { - int max = points.size(); - float yValue = (float)y + 0.5f; - for (int x = 0; x < max; x += 2) { - points[x] = (float)(x >> 1) + 0.5f; - points[x + 1] = yValue; - } - transform->transformPoints(points); - checkAndNudgePoints(image, points); - for (int x = 0; x < max; x += 2) { - if (image->get((int)points[x], (int)points[x + 1])) { - bits->set(x >> 1, y); - } - } - } - return bits; + transform->transformPoints(points); + checkAndNudgePoints(image, points); + for (int x = 0; x < max; x += 2) { + if (image->get((int)points[x], (int)points[x + 1])) { + bits->set(x >> 1, y); + } } - - Ref GridSampler::sampleGrid(Ref image, int dimension, float p1ToX, float p1ToY, float p2ToX, - float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX, - float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) { - Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, - p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY)); - - return sampleGrid(image, dimension, transform); - + } + return bits; +} + +Ref GridSampler::sampleGrid(Ref image, int dimensionX, int dimensionY, Ref transform) { + Ref bits(new BitMatrix(dimensionX, dimensionY)); + vector points(dimensionX << 1, (const float)0.0f); + for (int y = 0; y < dimensionY; y++) { + int max = points.size(); + float yValue = (float)y + 0.5f; + for (int x = 0; x < max; x += 2) { + points[x] = (float)(x >> 1) + 0.5f; + points[x + 1] = yValue; } - - void GridSampler::checkAndNudgePoints(Ref image, vector &points) { - int width = image->getWidth(); - int height = image->getHeight(); - - - // The Java code assumes that if the start and end points are in bounds, the rest will also be. - // However, in some unusual cases points in the middle may also be out of bounds. - // Since we can't rely on an ArrayIndexOutOfBoundsException like Java, we check every point. - - for (size_t offset = 0; offset < points.size(); offset += 2) { - int x = (int)points[offset]; - int y = (int)points[offset + 1]; - if (x < -1 || x > width || y < -1 || y > height) { - ostringstream s; - s << "Transformed point out of bounds at " << x << "," << y; - throw ReaderException(s.str().c_str()); - } - - if (x == -1) { - points[offset] = 0.0f; - } else if (x == width) { - points[offset] = width - 1; - } - if (y == -1) { - points[offset + 1] = 0.0f; - } else if (y == height) { - points[offset + 1] = height - 1; - } - } - + transform->transformPoints(points); + checkAndNudgePoints(image, points); + for (int x = 0; x < max; x += 2) { + if (image->get((int)points[x], (int)points[x + 1])) { + bits->set(x >> 1, y); + } } - - GridSampler &GridSampler::getInstance() { - return gridSampler; + } + return bits; +} + +Ref GridSampler::sampleGrid(Ref image, int dimension, float p1ToX, float p1ToY, float p2ToX, + float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX, + float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) { + Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, + p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY)); + + return sampleGrid(image, dimension, transform); + +} + +void GridSampler::checkAndNudgePoints(Ref image, vector &points) { + int width = image->getWidth(); + int height = image->getHeight(); + + + // The Java code assumes that if the start and end points are in bounds, the rest will also be. + // However, in some unusual cases points in the middle may also be out of bounds. + // Since we can't rely on an ArrayIndexOutOfBoundsException like Java, we check every point. + + for (size_t offset = 0; offset < points.size(); offset += 2) { + int x = (int)points[offset]; + int y = (int)points[offset + 1]; + if (x < -1 || x > width || y < -1 || y > height) { + ostringstream s; + s << "Transformed point out of bounds at " << x << "," << y; + throw ReaderException(s.str().c_str()); } + + if (x == -1) { + points[offset] = 0.0f; + } else if (x == width) { + points[offset] = width - 1; + } + if (y == -1) { + points[offset + 1] = 0.0f; + } else if (y == height) { + points[offset + 1] = height - 1; + } + } + +} + +GridSampler &GridSampler::getInstance() { + return gridSampler; +} } // file: zxing/common/HybridBinarizer.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * HybridBinarizer.cpp * zxing @@ -1849,150 +2193,188 @@ namespace zxing { // #include -namespace zxing { - using namespace std; - - static const int MINIMUM_DIMENSION = 40; - - static const int LUMINANCE_BITS_25 = 5; - static const int LUMINANCE_SHIFT_25 = 8 - LUMINANCE_BITS_25; - static const int LUMINANCE_BUCKETS_25 = 1 << LUMINANCE_BITS_25; - - HybridBinarizer::HybridBinarizer(Ref source) : - GlobalHistogramBinarizer(source), cached_matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) { - +using namespace std; +using namespace zxing; + +namespace { + const int BLOCK_SIZE_POWER = 3; + const int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; + const int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; + const int MINIMUM_DIMENSION = BLOCK_SIZE * 5; +} + +HybridBinarizer::HybridBinarizer(Ref source) : + GlobalHistogramBinarizer(source), matrix_(NULL), cached_row_(NULL), cached_row_num_(-1) { +} + +HybridBinarizer::~HybridBinarizer() { +} + + +Ref +HybridBinarizer::createBinarizer(Ref source) { + return Ref (new HybridBinarizer(source)); +} + +Ref HybridBinarizer::getBlackMatrix() { + // Calculates the final BitMatrix once for all requests. This could + // be called once from the constructor instead, but there are some + // advantages to doing it lazily, such as making profiling easier, + // and not doing heavy lifting when callers don't expect it. + if (matrix_) { + return matrix_; + } + LuminanceSource& source = *getLuminanceSource(); + if (source.getWidth() >= MINIMUM_DIMENSION && + source.getHeight() >= MINIMUM_DIMENSION) { + unsigned char* luminances = source.getMatrix(); + int width = source.getWidth(); + int height = source.getHeight(); + int subWidth = width >> BLOCK_SIZE_POWER; + if ((width & BLOCK_SIZE_MASK) != 0) { + subWidth++; } - - HybridBinarizer::~HybridBinarizer() { + int subHeight = height >> BLOCK_SIZE_POWER; + if ((height & BLOCK_SIZE_MASK) != 0) { + subHeight++; } - - - Ref HybridBinarizer::getBlackMatrix() { - binarizeEntireImage(); - return cached_matrix_; + int* blackPoints = + calculateBlackPoints(luminances, subWidth, subHeight, width, height); + + Ref newMatrix (new BitMatrix(width, height)); + calculateThresholdForBlock(luminances, + subWidth, + subHeight, + width, + height, + blackPoints, + newMatrix); + matrix_ = newMatrix; + + // N.B.: these deletes are inadequate if anything between the new + // and this point can throw. As of this writing, it doesn't look + // like they do. + + delete [] blackPoints; + delete [] luminances; + } else { + // If the image is too small, fall back to the global histogram approach. + matrix_ = GlobalHistogramBinarizer::getBlackMatrix(); + } + return matrix_; +} + +void +HybridBinarizer::calculateThresholdForBlock(unsigned char* luminances, + int subWidth, + int subHeight, + int width, + int height, + int blackPoints[], + Ref const& matrix) { + for (int y = 0; y < subHeight; y++) { + int yoffset = y << BLOCK_SIZE_POWER; + if (yoffset + BLOCK_SIZE >= height) { + yoffset = height - BLOCK_SIZE; } - - Ref HybridBinarizer::createBinarizer(Ref source) { - return Ref (new HybridBinarizer(source)); + for (int x = 0; x < subWidth; x++) { + int xoffset = x << BLOCK_SIZE_POWER; + if (xoffset + BLOCK_SIZE >= width) { + xoffset = width - BLOCK_SIZE; + } + int left = (x > 1) ? x : 2; + left = (left < subWidth - 2) ? left : subWidth - 3; + int top = (y > 1) ? y : 2; + top = (top < subHeight - 2) ? top : subHeight - 3; + int sum = 0; + for (int z = -2; z <= 2; z++) { + int *blackRow = &blackPoints[(top + z) * subWidth]; + sum += blackRow[left - 2]; + sum += blackRow[left - 1]; + sum += blackRow[left]; + sum += blackRow[left + 1]; + sum += blackRow[left + 2]; + } + int average = sum / 25; + threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix); } - - void HybridBinarizer::binarizeEntireImage() { - if (cached_matrix_ == NULL) { - Ref source = getLuminanceSource(); - if (source->getWidth() >= MINIMUM_DIMENSION && source->getHeight() >= MINIMUM_DIMENSION) { - unsigned char* luminances = source->getMatrix(); - int width = source->getWidth(); - int height = source->getHeight(); - int subWidth = width >> 3; - if (width & 0x07) { - subWidth++; - } - int subHeight = height >> 3; - if (height & 0x07) { - subHeight++; - } - int *blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height); - cached_matrix_.reset(new BitMatrix(width,height)); - calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, cached_matrix_); - delete [] blackPoints; - delete [] luminances; - } else { - // If the image is too small, fall back to the global histogram approach. - cached_matrix_.reset(GlobalHistogramBinarizer::getBlackMatrix()); - } + } +} + +void HybridBinarizer::threshold8x8Block(unsigned char* luminances, + int xoffset, + int yoffset, + int threshold, + int stride, + Ref const& matrix) { + for (int y = 0, offset = yoffset * stride + xoffset; + y < BLOCK_SIZE; + y++, offset += stride) { + for (int x = 0; x < BLOCK_SIZE; x++) { + int pixel = luminances[offset + x] & 0xff; + if (pixel <= threshold) { + matrix->set(xoffset + x, yoffset + y); + } + } + } +} + +namespace { + inline int getBlackPointFromNeighbors(int* blackPoints, int subWidth, int x, int y) { + return (blackPoints[(y-1)*subWidth+x] + + 2*blackPoints[y*subWidth+x-1] + + blackPoints[(y-1)*subWidth+x-1]) >> 2; + } +} + +int* HybridBinarizer::calculateBlackPoints(unsigned char* luminances, int subWidth, int subHeight, + int width, int height) { + int *blackPoints = new int[subHeight * subWidth]; + for (int y = 0; y < subHeight; y++) { + int yoffset = y << BLOCK_SIZE_POWER; + if (yoffset + BLOCK_SIZE >= height) { + yoffset = height - BLOCK_SIZE; + } + for (int x = 0; x < subWidth; x++) { + int xoffset = x << BLOCK_SIZE_POWER; + if (xoffset + BLOCK_SIZE >= width) { + xoffset = width - BLOCK_SIZE; + } + int sum = 0; + int min = 0xFF; + int max = 0; + for (int yy = 0, offset = yoffset * width + xoffset; + yy < BLOCK_SIZE; + yy++, offset += width) { + for (int xx = 0; xx < BLOCK_SIZE; xx++) { + int pixel = luminances[offset + xx] & 0xFF; + sum += pixel; + if (pixel < min) { + min = pixel; + } + if (pixel > max) { + max = pixel; + } } - } - - void HybridBinarizer::calculateThresholdForBlock(unsigned char* luminances, int subWidth, int subHeight, - int width, int height, int blackPoints[], Ref matrix) { - for (int y = 0; y < subHeight; y++) { - int yoffset = y << 3; - if (yoffset + 8 >= height) { - yoffset = height - 8; - } - for (int x = 0; x < subWidth; x++) { - int xoffset = x << 3; - if (xoffset + 8 >= width) { - xoffset = width - 8; - } - int left = (x > 1) ? x : 2; - left = (left < subWidth - 2) ? left : subWidth - 3; - int top = (y > 1) ? y : 2; - top = (top < subHeight - 2) ? top : subHeight - 3; - int sum = 0; - for (int z = -2; z <= 2; z++) { - int *blackRow = &blackPoints[(top + z) * subWidth]; - sum += blackRow[left - 2]; - sum += blackRow[left - 1]; - sum += blackRow[left]; - sum += blackRow[left + 1]; - sum += blackRow[left + 2]; - } - int average = sum / 25; - threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix); - } + } + + // See + // http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0 + int average = sum >> 6; + if (max - min <= 24) { + average = min >> 1; + if (y > 0 && x > 0) { + int bp = getBlackPointFromNeighbors(blackPoints, subWidth, x, y); + if (min < bp) { + average = bp; + } } + } + blackPoints[y * subWidth + x] = average; } - - void HybridBinarizer::threshold8x8Block(unsigned char* luminances, int xoffset, int yoffset, int threshold, - int stride, Ref matrix) { - for (int y = 0; y < 8; y++) { - int offset = (yoffset + y) * stride + xoffset; - for (int x = 0; x < 8; x++) { - int pixel = luminances[offset + x] & 0xff; - if (pixel < threshold) { - matrix->set(xoffset + x, yoffset + y); - } - } - } - } - - int* HybridBinarizer::calculateBlackPoints(unsigned char* luminances, int subWidth, int subHeight, - int width, int height) { - int *blackPoints = new int[subHeight * subWidth]; - for (int y = 0; y < subHeight; y++) { - int yoffset = y << 3; - if (yoffset + 8 >= height) { - yoffset = height - 8; - } - for (int x = 0; x < subWidth; x++) { - int xoffset = x << 3; - if (xoffset + 8 >= width) { - xoffset = width - 8; - } - int sum = 0; - int min = 255; - int max = 0; - for (int yy = 0; yy < 8; yy++) { - int offset = (yoffset + yy) * width + xoffset; - for (int xx = 0; xx < 8; xx++) { - int pixel = luminances[offset + xx] & 0xff; - sum += pixel; - if (pixel < min) { - min = pixel; - } - if (pixel > max) { - max = pixel; - } - } - } - - // If the contrast is inadequate, use half the minimum, so that this block will be - // treated as part of the white background, but won't drag down neighboring blocks - // too much. - int average; - if (max - min > 24) { - average = (sum >> 6); - } else { - average = max == 0 ? 1 : (min >> 1); - } - blackPoints[y * subWidth + x] = average; - } - } - return blackPoints; - } - -} // namespace zxing + } + return blackPoints; +} // file: zxing/common/IllegalArgumentException.cpp @@ -2020,13 +2402,13 @@ namespace zxing { // #include namespace zxing { - - IllegalArgumentException::IllegalArgumentException(const char *msg) : + +IllegalArgumentException::IllegalArgumentException(const char *msg) : Exception(msg) { - } - IllegalArgumentException::~IllegalArgumentException() throw() { - - } +} +IllegalArgumentException::~IllegalArgumentException() throw() { + +} } // file: zxing/common/PerspectiveTransform.cpp @@ -2054,89 +2436,89 @@ namespace zxing { // #include namespace zxing { - using namespace std; - - PerspectiveTransform::PerspectiveTransform(float inA11, float inA21, - float inA31, float inA12, - float inA22, float inA32, - float inA13, float inA23, - float inA33) : - a11(inA11), a12(inA12), a13(inA13), a21(inA21), a22(inA22), a23(inA23), - a31(inA31), a32(inA32), a33(inA33) {} - - Ref PerspectiveTransform::quadrilateralToQuadrilateral(float x0, float y0, float x1, float y1, - float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p, - float x3p, float y3p) { - Ref qToS = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); - Ref sToQ = - PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); - return sToQ->times(qToS); - } - - Ref PerspectiveTransform::squareToQuadrilateral(float x0, float y0, float x1, float y1, float x2, - float y2, float x3, float y3) { - float dy2 = y3 - y2; - float dy3 = y0 - y1 + y2 - y3; - if (dy2 == 0.0f && dy3 == 0.0f) { - Ref result(new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f, - 0.0f, 1.0f)); - return result; - } else { - float dx1 = x1 - x2; - float dx2 = x3 - x2; - float dx3 = x0 - x1 + x2 - x3; - float dy1 = y1 - y2; - float denominator = dx1 * dy2 - dx2 * dy1; - float a13 = (dx3 * dy2 - dx2 * dy3) / denominator; - float a23 = (dx1 * dy3 - dx3 * dy1) / denominator; - Ref result(new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 - + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0f)); - return result; - } - } - - Ref PerspectiveTransform::quadrilateralToSquare(float x0, float y0, float x1, float y1, float x2, - float y2, float x3, float y3) { - // Here, the adjoint serves as the inverse: - return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)->buildAdjoint(); - } - - Ref PerspectiveTransform::buildAdjoint() { - // Adjoint is the transpose of the cofactor matrix: - Ref result(new PerspectiveTransform(a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32 - - a22 * a31, a13 * a32 - a12 * a33, a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22, - a13 * a21 - a11 * a23, a11 * a22 - a12 * a21)); - return result; - } - - Ref PerspectiveTransform::times(Ref other) { - Ref result(new PerspectiveTransform(a11 * other->a11 + a21 * other->a12 + a31 * other->a13, - a11 * other->a21 + a21 * other->a22 + a31 * other->a23, a11 * other->a31 + a21 * other->a32 + a31 - * other->a33, a12 * other->a11 + a22 * other->a12 + a32 * other->a13, a12 * other->a21 + a22 - * other->a22 + a32 * other->a23, a12 * other->a31 + a22 * other->a32 + a32 * other->a33, a13 - * other->a11 + a23 * other->a12 + a33 * other->a13, a13 * other->a21 + a23 * other->a22 + a33 - * other->a23, a13 * other->a31 + a23 * other->a32 + a33 * other->a33)); - return result; - } - - void PerspectiveTransform::transformPoints(vector &points) { - int max = points.size(); - for (int i = 0; i < max; i += 2) { - float x = points[i]; - float y = points[i + 1]; - float denominator = a13 * x + a23 * y + a33; - points[i] = (a11 * x + a21 * y + a31) / denominator; - points[i + 1] = (a12 * x + a22 * y + a32) / denominator; - } - } - - ostream& operator<<(ostream& out, const PerspectiveTransform &pt) { - out << pt.a11 << ", " << pt.a12 << ", " << pt.a13 << ", \n"; - out << pt.a21 << ", " << pt.a22 << ", " << pt.a23 << ", \n"; - out << pt.a31 << ", " << pt.a32 << ", " << pt.a33 << "\n"; - return out; - } - +using namespace std; + +PerspectiveTransform::PerspectiveTransform(float inA11, float inA21, + float inA31, float inA12, + float inA22, float inA32, + float inA13, float inA23, + float inA33) : + a11(inA11), a12(inA12), a13(inA13), a21(inA21), a22(inA22), a23(inA23), + a31(inA31), a32(inA32), a33(inA33) {} + +Ref PerspectiveTransform::quadrilateralToQuadrilateral(float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p, + float x3p, float y3p) { + Ref qToS = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); + Ref sToQ = + PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); + return sToQ->times(qToS); +} + +Ref PerspectiveTransform::squareToQuadrilateral(float x0, float y0, float x1, float y1, float x2, + float y2, float x3, float y3) { + float dy2 = y3 - y2; + float dy3 = y0 - y1 + y2 - y3; + if (dy2 == 0.0f && dy3 == 0.0f) { + Ref result(new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f, + 0.0f, 1.0f)); + return result; + } else { + float dx1 = x1 - x2; + float dx2 = x3 - x2; + float dx3 = x0 - x1 + x2 - x3; + float dy1 = y1 - y2; + float denominator = dx1 * dy2 - dx2 * dy1; + float a13 = (dx3 * dy2 - dx2 * dy3) / denominator; + float a23 = (dx1 * dy3 - dx3 * dy1) / denominator; + Ref result(new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0f)); + return result; + } +} + +Ref PerspectiveTransform::quadrilateralToSquare(float x0, float y0, float x1, float y1, float x2, + float y2, float x3, float y3) { + // Here, the adjoint serves as the inverse: + return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)->buildAdjoint(); +} + +Ref PerspectiveTransform::buildAdjoint() { + // Adjoint is the transpose of the cofactor matrix: + Ref result(new PerspectiveTransform(a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32 + - a22 * a31, a13 * a32 - a12 * a33, a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22, + a13 * a21 - a11 * a23, a11 * a22 - a12 * a21)); + return result; +} + +Ref PerspectiveTransform::times(Ref other) { + Ref result(new PerspectiveTransform(a11 * other->a11 + a21 * other->a12 + a31 * other->a13, + a11 * other->a21 + a21 * other->a22 + a31 * other->a23, a11 * other->a31 + a21 * other->a32 + a31 + * other->a33, a12 * other->a11 + a22 * other->a12 + a32 * other->a13, a12 * other->a21 + a22 + * other->a22 + a32 * other->a23, a12 * other->a31 + a22 * other->a32 + a32 * other->a33, a13 + * other->a11 + a23 * other->a12 + a33 * other->a13, a13 * other->a21 + a23 * other->a22 + a33 + * other->a23, a13 * other->a31 + a23 * other->a32 + a33 * other->a33)); + return result; +} + +void PerspectiveTransform::transformPoints(vector &points) { + int max = points.size(); + for (int i = 0; i < max; i += 2) { + float x = points[i]; + float y = points[i + 1]; + float denominator = a13 * x + a23 * y + a33; + points[i] = (a11 * x + a21 * y + a31) / denominator; + points[i + 1] = (a12 * x + a22 * y + a32) / denominator; + } +} + +ostream& operator<<(ostream& out, const PerspectiveTransform &pt) { + out << pt.a11 << ", " << pt.a12 << ", " << pt.a13 << ", \n"; + out << pt.a21 << ", " << pt.a22 << ", " << pt.a23 << ", \n"; + out << pt.a31 << ", " << pt.a32 << ", " << pt.a33 << "\n"; + return out; +} + } // file: zxing/common/Str.cpp @@ -2164,20 +2546,690 @@ namespace zxing { // #include namespace zxing { - using namespace std; - - String::String(const std::string &text) : +using namespace std; + +String::String(const std::string &text) : text_(text) { +} +const std::string& String::getText() const { + return text_; +} + +ostream &operator<<(ostream &out, const String &s) { + out << s.text_; + return out; +} + +} + +// file: zxing/common/StringUtils.cpp + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + +/* + * Copyright (C) 2010-2011 ZXing authors + * + * 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. + */ + +// #include +// #include + +using namespace std; +using namespace zxing; +using namespace zxing::common; + +// N.B.: these are the iconv strings for at least some versions of iconv + +char const* const StringUtils::PLATFORM_DEFAULT_ENCODING = "UTF-8"; +char const* const StringUtils::ASCII = "ASCII"; +char const* const StringUtils::SHIFT_JIS = "SHIFT_JIS"; +char const* const StringUtils::GB2312 = "GBK"; +char const* const StringUtils::EUC_JP = "EUC-JP"; +char const* const StringUtils::UTF8 = "UTF-8"; +char const* const StringUtils::ISO88591 = "ISO8859-1"; +const bool StringUtils::ASSUME_SHIFT_JIS = false; + +string +StringUtils::guessEncoding(unsigned char* bytes, int length, Hashtable const& hints) { + Hashtable::const_iterator i = hints.find(DecodeHints::CHARACTER_SET); + if (i != hints.end()) { + return i->second; + } + // Does it start with the UTF-8 byte order mark? then guess it's UTF-8 + if (length > 3 && + bytes[0] == (unsigned char) 0xEF && + bytes[1] == (unsigned char) 0xBB && + bytes[2] == (unsigned char) 0xBF) { + return UTF8; + } + // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, + // which should be by far the most common encodings. ISO-8859-1 + // should not have bytes in the 0x80 - 0x9F range, while Shift_JIS + // uses this as a first byte of a two-byte character. If we see this + // followed by a valid second byte in Shift_JIS, assume it is Shift_JIS. + // If we see something else in that second byte, we'll make the risky guess + // that it's UTF-8. + bool canBeISO88591 = true; + bool canBeShiftJIS = true; + bool canBeUTF8 = true; + int utf8BytesLeft = 0; + int maybeDoubleByteCount = 0; + int maybeSingleByteKatakanaCount = 0; + bool sawLatin1Supplement = false; + bool sawUTF8Start = false; + bool lastWasPossibleDoubleByteStart = false; + + for (int i = 0; + i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); + i++) { + + int value = bytes[i] & 0xFF; + + // UTF-8 stuff + if (value >= 0x80 && value <= 0xBF) { + if (utf8BytesLeft > 0) { + utf8BytesLeft--; + } + } else { + if (utf8BytesLeft > 0) { + canBeUTF8 = false; + } + if (value >= 0xC0 && value <= 0xFD) { + sawUTF8Start = true; + int valueCopy = value; + while ((valueCopy & 0x40) != 0) { + utf8BytesLeft++; + valueCopy <<= 1; + } + } } - const std::string& String::getText() const { - return text_; + + // ISO-8859-1 stuff + + if ((value == 0xC2 || value == 0xC3) && i < length - 1) { + // This is really a poor hack. The slightly more exotic characters people might want to put in + // a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings + // that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF]. + int nextValue = bytes[i + 1] & 0xFF; + if (nextValue <= 0xBF && + ((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) { + sawLatin1Supplement = true; + } } - - ostream &operator<<(ostream &out, const String &s) { - out << s.text_; - return out; + if (value >= 0x7F && value <= 0x9F) { + canBeISO88591 = false; } - + + // Shift_JIS stuff + + if (value >= 0xA1 && value <= 0xDF) { + // count the number of characters that might be a Shift_JIS single-byte Katakana character + if (!lastWasPossibleDoubleByteStart) { + maybeSingleByteKatakanaCount++; + } + } + if (!lastWasPossibleDoubleByteStart && + ((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) { + canBeShiftJIS = false; + } + if ((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF)) { + // These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid + // second byte. + if (lastWasPossibleDoubleByteStart) { + // If we just checked this and the last byte for being a valid double-byte + // char, don't check starting on this byte. If this and the last byte + // formed a valid pair, then this shouldn't be checked to see if it starts + // a double byte pair of course. + lastWasPossibleDoubleByteStart = false; + } else { + // ... otherwise do check to see if this plus the next byte form a valid + // double byte pair encoding a character. + lastWasPossibleDoubleByteStart = true; + if (i >= length - 1) { + canBeShiftJIS = false; + } else { + int nextValue = bytes[i + 1] & 0xFF; + if (nextValue < 0x40 || nextValue > 0xFC) { + canBeShiftJIS = false; + } else { + maybeDoubleByteCount++; + } + // There is some conflicting information out there about which bytes can follow which in + // double-byte Shift_JIS characters. The rule above seems to be the one that matches practice. + } + } + } else { + lastWasPossibleDoubleByteStart = false; + } + } + if (utf8BytesLeft > 0) { + canBeUTF8 = false; + } + + // Easy -- if assuming Shift_JIS and no evidence it can't be, done + if (canBeShiftJIS && ASSUME_SHIFT_JIS) { + return SHIFT_JIS; + } + if (canBeUTF8 && sawUTF8Start) { + return UTF8; + } + // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is: + // - If we saw + // - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or + // - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1), + // - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS + if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) { + return SHIFT_JIS; + } + // Otherwise, we default to ISO-8859-1 unless we know it can't be + if (!sawLatin1Supplement && canBeISO88591) { + return ISO88591; + } + // Otherwise, we take a wild guess with platform encoding + return PLATFORM_DEFAULT_ENCODING; +} + +// file: zxing/common/detector/MonochromeRectangleDetector.cpp + +/* + * MonochromeRectangleDetector.cpp + * y_wmk + * + * Created by Luiz Silva on 09/02/2010. + * Copyright 2010 y_wmk authors 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. + */ + +// #include +// #include +// #include + +namespace zxing { +using namespace std; + +std::vector > MonochromeRectangleDetector::detect() { + int height = image_->getHeight(); + int width = image_->getWidth(); + int halfHeight = height >> 1; + int halfWidth = width >> 1; + int deltaY = max(1, height / (MAX_MODULES << 3)); + int deltaX = max(1, width / (MAX_MODULES << 3)); + + int top = 0; + int bottom = height; + int left = 0; + int right = width; + Ref pointA(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, -deltaY, top, bottom, halfWidth >> 1)); + top = (int) pointA->getY() - 1;; + Ref pointB(findCornerFromCenter(halfWidth, -deltaX, left, right, + halfHeight, 0, top, bottom, halfHeight >> 1)); + left = (int) pointB->getX() - 1; + Ref pointC(findCornerFromCenter(halfWidth, deltaX, left, right, + halfHeight, 0, top, bottom, halfHeight >> 1)); + right = (int) pointC->getX() + 1; + Ref pointD(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, deltaY, top, bottom, halfWidth >> 1)); + bottom = (int) pointD->getY() + 1; + + // Go try to find point A again with better information -- might have been off at first. + pointA.reset(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, -deltaY, top, bottom, halfWidth >> 2)); + + std::vector > corners(4); + corners[0].reset(pointA); + corners[1].reset(pointB); + corners[2].reset(pointC); + corners[3].reset(pointD); + return corners; + } + +Ref MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right, + int centerY, int deltaY, int top, int bottom, int maxWhiteRun) { + Ref lastRange(NULL); + for (int y = centerY, x = centerX; + y < bottom && y >= top && x < right && x >= left; + y += deltaY, x += deltaX) { + Ref range(NULL); + if (deltaX == 0) { + // horizontal slices, up and down + range = blackWhiteRange(y, maxWhiteRun, left, right, true); + } else { + // vertical slices, left and right + range = blackWhiteRange(x, maxWhiteRun, top, bottom, false); + } + if (range == NULL) { + if (lastRange == NULL) { + throw NotFoundException("Couldn't find corners (lastRange = NULL) "); + } else { + // lastRange was found + if (deltaX == 0) { + int lastY = y - deltaY; + if (lastRange->start < centerX) { + if (lastRange->end > centerX) { + // straddle, choose one or the other based on direction + Ref result(new ResultPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY)); + return result; + } + Ref result(new ResultPoint(lastRange->start, lastY)); + return result; + } else { + Ref result(new ResultPoint(lastRange->end, lastY)); + return result; + } + } else { + int lastX = x - deltaX; + if (lastRange->start < centerY) { + if (lastRange->end > centerY) { + Ref result(new ResultPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end)); + return result; + } + Ref result(new ResultPoint(lastX, lastRange->start)); + return result; + } else { + Ref result(new ResultPoint(lastX, lastRange->end)); + return result; + } + } + } + } + lastRange = range; + } + throw NotFoundException("Couldn't find corners"); + } + +Ref MonochromeRectangleDetector::blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, + bool horizontal) { + + int center = (minDim + maxDim) >> 1; + + // Scan left/up first + int start = center; + while (start >= minDim) { + if (horizontal ? image_->get(start, fixedDimension) : image_->get(fixedDimension, start)) { + start--; + } else { + int whiteRunStart = start; + do { + start--; + } while (start >= minDim && !(horizontal ? image_->get(start, fixedDimension) : + image_->get(fixedDimension, start))); + int whiteRunSize = whiteRunStart - start; + if (start < minDim || whiteRunSize > maxWhiteRun) { + start = whiteRunStart; + break; + } + } + } + start++; + + // Then try right/down + int end = center; + while (end < maxDim) { + if (horizontal ? image_->get(end, fixedDimension) : image_->get(fixedDimension, end)) { + end++; + } else { + int whiteRunStart = end; + do { + end++; + } while (end < maxDim && !(horizontal ? image_->get(end, fixedDimension) : + image_->get(fixedDimension, end))); + int whiteRunSize = end - whiteRunStart; + if (end >= maxDim || whiteRunSize > maxWhiteRun) { + end = whiteRunStart; + break; + } + } + } + end--; + Ref result(NULL); + if (end > start) { + result = new TwoInts; + result->start = start; + result->end = end; + } + return result; + } +} + +// file: zxing/common/detector/WhiteRectangleDetector.cpp + +/* + * WhiteRectangleDetector.cpp + * y_wmk + * + * Created by Luiz Silva on 09/02/2010. + * Copyright 2010 y_wmk authors 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. + */ + +// #include +// #include +// #include +// #include + +namespace zxing { +using namespace std; + +int WhiteRectangleDetector::INIT_SIZE = 30; +int WhiteRectangleDetector::CORR = 1; + + +WhiteRectangleDetector::WhiteRectangleDetector(Ref image) : image_(image) { + width_ = image->getWidth(); + height_ = image->getHeight(); +} + +/** + *

+ * Detects a candidate barcode-like rectangular region within an image. It + * starts around the center of the image, increases the size of the candidate + * region until it finds a white rectangular region. + *

+ * + * @return {@link vector >} describing the corners of the rectangular + * region. The first and last points are opposed on the diagonal, as + * are the second and third. The first point will be the topmost + * point and the last, the bottommost. The second point will be + * leftmost and the third, the rightmost + * @throws NotFoundException if no Data Matrix Code can be found +*/ +std::vector > WhiteRectangleDetector::detect() { + int left = (width_ - INIT_SIZE) >> 1; + int right = (width_ + INIT_SIZE) >> 1; + int up = (height_ - INIT_SIZE) >> 1; + int down = (height_ + INIT_SIZE) >> 1; + if (up < 0 || left < 0 || down >= height_ || right >= width_) { + throw NotFoundException("Invalid dimensions WhiteRectangleDetector"); + } + + bool sizeExceeded = false; + bool aBlackPointFoundOnBorder = true; + bool atLeastOneBlackPointFoundOnBorder = false; + + while (aBlackPointFoundOnBorder) { + aBlackPointFoundOnBorder = false; + + // ..... + // . | + // ..... + bool rightBorderNotWhite = true; + while (rightBorderNotWhite && right < width_) { + rightBorderNotWhite = containsBlackPoint(up, down, right, false); + if (rightBorderNotWhite) { + right++; + aBlackPointFoundOnBorder = true; + } + } + + if (right >= width_) { + sizeExceeded = true; + break; + } + + // ..... + // . . + // .___. + bool bottomBorderNotWhite = true; + while (bottomBorderNotWhite && down < height_) { + bottomBorderNotWhite = containsBlackPoint(left, right, down, true); + if (bottomBorderNotWhite) { + down++; + aBlackPointFoundOnBorder = true; + } + } + + if (down >= height_) { + sizeExceeded = true; + break; + } + + // ..... + // | . + // ..... + bool leftBorderNotWhite = true; + while (leftBorderNotWhite && left >= 0) { + leftBorderNotWhite = containsBlackPoint(up, down, left, false); + if (leftBorderNotWhite) { + left--; + aBlackPointFoundOnBorder = true; + } + } + + if (left < 0) { + sizeExceeded = true; + break; + } + + // .___. + // . . + // ..... + bool topBorderNotWhite = true; + while (topBorderNotWhite && up >= 0) { + topBorderNotWhite = containsBlackPoint(left, right, up, true); + if (topBorderNotWhite) { + up--; + aBlackPointFoundOnBorder = true; + } + } + + if (up < 0) { + sizeExceeded = true; + break; + } + + if (aBlackPointFoundOnBorder) { + atLeastOneBlackPointFoundOnBorder = true; + } + + } + if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) { + + int maxSize = right - left; + + Ref z(NULL); + //go up right + for (int i = 1; i < maxSize; i++) { + z = getBlackPointOnSegment(left, down - i, left + i, down); + if (z != NULL) { + break; + } + } + + if (z == NULL) { + throw NotFoundException("z == NULL"); + } + + Ref t(NULL); + //go down right + for (int i = 1; i < maxSize; i++) { + t = getBlackPointOnSegment(left, up + i, left + i, up); + if (t != NULL) { + break; + } + } + + if (t == NULL) { + throw NotFoundException("t == NULL"); + } + + Ref x(NULL); + //go down left + for (int i = 1; i < maxSize; i++) { + x = getBlackPointOnSegment(right, up + i, right - i, up); + if (x != NULL) { + break; + } + } + + if (x == NULL) { + throw NotFoundException("x == NULL"); + } + + Ref y(NULL); + //go up left + for (int i = 1; i < maxSize; i++) { + y = getBlackPointOnSegment(right, down - i, right - i, down); + if (y != NULL) { + break; + } + } + + if (y == NULL) { + throw NotFoundException("y == NULL"); + } + + return centerEdges(y, z, x, t); + + } else { + throw NotFoundException("No black point found on border"); + } +} + +/** + * Ends up being a bit faster than Math.round(). This merely rounds its + * argument to the nearest int, where x.5 rounds up. + */ +int WhiteRectangleDetector::round(float d) { + return (int) (d + 0.5f); +} + +Ref WhiteRectangleDetector::getBlackPointOnSegment(float aX, float aY, float bX, float bY) { + int dist = distanceL2(aX, aY, bX, bY); + float xStep = (bX - aX) / dist; + float yStep = (bY - aY) / dist; + for (int i = 0; i < dist; i++) { + int x = round(aX + i * xStep); + int y = round(aY + i * yStep); + if (image_->get(x, y)) { + Ref point(new ResultPoint(x, y)); + return point; + } + } + Ref point(NULL); + return point; +} + +int WhiteRectangleDetector::distanceL2(float aX, float aY, float bX, float bY) { + float xDiff = aX - bX; + float yDiff = aY - bY; + return round((float)sqrt(xDiff * xDiff + yDiff * yDiff)); +} + +/** + * recenters the points of a constant distance towards the center + * + * @param y bottom most point + * @param z left most point + * @param x right most point + * @param t top most point + * @return {@link vector >} describing the corners of the rectangular + * region. The first and last points are opposed on the diagonal, as + * are the second and third. The first point will be the topmost + * point and the last, the bottommost. The second point will be + * leftmost and the third, the rightmost + */ +vector > WhiteRectangleDetector::centerEdges(Ref y, Ref z, + Ref x, Ref t) { + + // + // t t + // z x + // x OR z + // y y + // + + float yi = y->getX(); + float yj = y->getY(); + float zi = z->getX(); + float zj = z->getY(); + float xi = x->getX(); + float xj = x->getY(); + float ti = t->getX(); + float tj = t->getY(); + + std::vector > corners(4); + if (yi < (float)width_/2) { + Ref pointA(new ResultPoint(ti - CORR, tj + CORR)); + Ref pointB(new ResultPoint(zi + CORR, zj + CORR)); + Ref pointC(new ResultPoint(xi - CORR, xj - CORR)); + Ref pointD(new ResultPoint(yi + CORR, yj - CORR)); + corners[0].reset(pointA); + corners[1].reset(pointB); + corners[2].reset(pointC); + corners[3].reset(pointD); + } else { + Ref pointA(new ResultPoint(ti + CORR, tj + CORR)); + Ref pointB(new ResultPoint(zi + CORR, zj - CORR)); + Ref pointC(new ResultPoint(xi - CORR, xj + CORR)); + Ref pointD(new ResultPoint(yi - CORR, yj - CORR)); + corners[0].reset(pointA); + corners[1].reset(pointB); + corners[2].reset(pointC); + corners[3].reset(pointD); + } + return corners; +} + +/** + * Determines whether a segment contains a black point + * + * @param a min value of the scanned coordinate + * @param b max value of the scanned coordinate + * @param fixed value of fixed coordinate + * @param horizontal set to true if scan must be horizontal, false if vertical + * @return true if a black point has been found, else false. + */ +bool WhiteRectangleDetector::containsBlackPoint(int a, int b, int fixed, bool horizontal) { + if (horizontal) { + for (int x = a; x <= b; x++) { + if (image_->get(x, fixed)) { + return true; + } + } + } else { + for (int y = a; y <= b; y++) { + if (image_->get(fixed, y)) { + return true; + } + } + } + + return false; +} } // file: zxing/common/reedsolomon/GF256.cpp @@ -2211,112 +3263,112 @@ namespace zxing { // #include namespace zxing { - using namespace std; - - static inline ArrayRef makeArray(int value) { - ArrayRef valuesRef(new Array (value, 1)); - return valuesRef; - } - - static inline Ref refPoly(GF256 &field, int value) { - ArrayRef values(makeArray(value)); - Ref result(new GF256Poly(field, values)); - return result; - } - - GF256::GF256(int primitive) : +using namespace std; + +static inline ArrayRef makeArray(int value) { + ArrayRef valuesRef(new Array (value, 1)); + return valuesRef; +} + +static inline Ref refPoly(GF256 &field, int value) { + ArrayRef values(makeArray(value)); + Ref result(new GF256Poly(field, values)); + return result; +} + +GF256::GF256(int primitive) : exp_(256, (const int)0), log_(256, (const int)0), zero_(refPoly(*this, 0)), one_(refPoly(*this, 1)) { - int x = 1; - for (int i = 0; i < 256; i++) { - exp_[i] = x; - x <<= 1; - if (x >= 0x100) { - x ^= primitive; - } - } - - // log(0) == 0, but should never be used - log_[0] = 0; - for (int i = 0; i < 255; i++) { - log_[exp_[i]] = i; - } + int x = 1; + for (int i = 0; i < 256; i++) { + exp_[i] = x; + x <<= 1; + if (x >= 0x100) { + x ^= primitive; } - - Ref GF256::getZero() { - return zero_; - } - - Ref GF256::getOne() { - return one_; - } - - Ref GF256::buildMonomial(int degree, int coefficient) { + } + + // log(0) == 0, but should never be used + log_[0] = 0; + for (int i = 0; i < 255; i++) { + log_[exp_[i]] = i; + } +} + +Ref GF256::getZero() { + return zero_; +} + +Ref GF256::getOne() { + return one_; +} + +Ref GF256::buildMonomial(int degree, int coefficient) { #ifdef DEBUG - cout << __FUNCTION__ << "\n"; + cout << __FUNCTION__ << "\n"; #endif - if (degree < 0) { - throw IllegalArgumentException("Degree must be non-negative"); - } - if (coefficient == 0) { - return zero_; - } - int nCoefficients = degree + 1; - ArrayRef coefficients(new Array (nCoefficients)); - coefficients[0] = coefficient; - Ref result(new GF256Poly(*this, coefficients)); - return result; - } - - int GF256::addOrSubtract(int a, int b) { - return a ^ b; - } - - int GF256::exp(int a) { - return exp_[a]; - } - - int GF256::log(int a) { - if (a == 0) { - throw IllegalArgumentException("Cannot take the logarithm of 0"); - } - return log_[a]; - } - - int GF256::inverse(int a) { - if (a == 0) { - throw IllegalArgumentException("Cannot calculate the inverse of 0"); - } - return exp_[255 - log_[a]]; - } - - int GF256::multiply(int a, int b) { - if (a == 0 || b == 0) { - return 0; - } - int logSum = log_[a] + log_[b]; - // index is a sped-up alternative to logSum % 255 since sum - // is in [0,510]. Thanks to jmsachs for the idea - return exp_[(logSum & 0xFF) + (logSum >> 8)]; - } - - GF256 GF256::QR_CODE_FIELD(0x011D); // x^8 + x^4 + x^3 + x^2 + 1 - GF256 GF256::DATA_MATRIX_FIELD(0x012D); // x^8 + x^5 + x^3 + x^2 + 1 - - ostream& operator<<(ostream& out, const GF256& field) { - out << "Field[\nexp=("; - out << field.exp_[0]; - for (int i = 1; i < 256; i++) { - out << "," << field.exp_[i]; - } - out << "),\nlog=("; - out << field.log_[0]; - for (int i = 1; i < 256; i++) { - out << "," << field.log_[i]; - } - out << ")\n]"; - return out; - } - + if (degree < 0) { + throw IllegalArgumentException("Degree must be non-negative"); + } + if (coefficient == 0) { + return zero_; + } + int nCoefficients = degree + 1; + ArrayRef coefficients(new Array (nCoefficients)); + coefficients[0] = coefficient; + Ref result(new GF256Poly(*this, coefficients)); + return result; +} + +int GF256::addOrSubtract(int a, int b) { + return a ^ b; +} + +int GF256::exp(int a) { + return exp_[a]; +} + +int GF256::log(int a) { + if (a == 0) { + throw IllegalArgumentException("Cannot take the logarithm of 0"); + } + return log_[a]; +} + +int GF256::inverse(int a) { + if (a == 0) { + throw IllegalArgumentException("Cannot calculate the inverse of 0"); + } + return exp_[255 - log_[a]]; +} + +int GF256::multiply(int a, int b) { + if (a == 0 || b == 0) { + return 0; + } + int logSum = log_[a] + log_[b]; + // index is a sped-up alternative to logSum % 255 since sum + // is in [0,510]. Thanks to jmsachs for the idea + return exp_[(logSum & 0xFF) + (logSum >> 8)]; +} + +GF256 GF256::QR_CODE_FIELD(0x011D); // x^8 + x^4 + x^3 + x^2 + 1 +GF256 GF256::DATA_MATRIX_FIELD(0x012D); // x^8 + x^5 + x^3 + x^2 + 1 + +ostream& operator<<(ostream& out, const GF256& field) { + out << "Field[\nexp=("; + out << field.exp_[0]; + for (int i = 1; i < 256; i++) { + out << "," << field.exp_[i]; + } + out << "),\nlog=("; + out << field.log_[0]; + for (int i = 1; i < 256; i++) { + out << "," << field.log_[i]; + } + out << ")\n]"; + return out; +} + } // file: zxing/common/reedsolomon/GF256Poly.cpp @@ -2348,176 +3400,176 @@ namespace zxing { // #include namespace zxing { - using namespace std; - - void GF256Poly::fixCoefficients() { - int coefficientsLength = coefficients.size(); - if (coefficientsLength > 1 && coefficients[0] == 0) { - // Leading term must be non-zero for anything except - // the constant polynomial "0" - int firstNonZero = 1; - while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { - firstNonZero++; - } - if (firstNonZero == coefficientsLength) { - coefficientsLength = field.getZero()->coefficients.size(); - coefficients.reset(new Array (coefficientsLength)); - *coefficients = *(field.getZero()->coefficients); - } else { - ArrayRef c(coefficients); - coefficientsLength -= firstNonZero; - coefficients.reset(new Array (coefficientsLength)); - for (int i = 0; i < coefficientsLength; i++) { - coefficients[i] = c[i + firstNonZero]; - } - } - } +using namespace std; + +void GF256Poly::fixCoefficients() { + int coefficientsLength = coefficients.size(); + if (coefficientsLength > 1 && coefficients[0] == 0) { + // Leading term must be non-zero for anything except + // the constant polynomial "0" + int firstNonZero = 1; + while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { + firstNonZero++; } - - GF256Poly::GF256Poly(GF256 &f, ArrayRef c) : + if (firstNonZero == coefficientsLength) { + coefficientsLength = field.getZero()->coefficients.size(); + coefficients.reset(new Array (coefficientsLength)); + *coefficients = *(field.getZero()->coefficients); + } else { + ArrayRef c(coefficients); + coefficientsLength -= firstNonZero; + coefficients.reset(new Array (coefficientsLength)); + for (int i = 0; i < coefficientsLength; i++) { + coefficients[i] = c[i + firstNonZero]; + } + } + } +} + +GF256Poly::GF256Poly(GF256 &f, ArrayRef c) : Counted(), field(f), coefficients(c) { - fixCoefficients(); + fixCoefficients(); +} + +GF256Poly::~GF256Poly() { + +} + +int GF256Poly::getDegree() { + return coefficients.size() - 1; +} + +bool GF256Poly::isZero() { + return coefficients[0] == 0; +} + +int GF256Poly::getCoefficient(int degree) { + return coefficients[coefficients.size() - 1 - degree]; +} + +int GF256Poly::evaluateAt(int a) { + if (a == 0) { + return getCoefficient(0); + } + int size = coefficients.size(); + if (a == 1) { + // Just the sum of the coefficients + int result = 0; + for (int i = 0; i < size; i++) { + result = GF256::addOrSubtract(result, coefficients[i]); } - - GF256Poly::~GF256Poly() { - + return result; + } + int result = coefficients[0]; + for (int i = 1; i < size; i++) { + result = GF256::addOrSubtract(field.multiply(a, result), coefficients[i]); + } + return result; +} + +Ref GF256Poly::addOrSubtract(Ref b) { + if (&field != &b->field) { + throw IllegalArgumentException("Fields must be the same"); + } + if (isZero()) { + return b; + } + if (b->isZero()) { + return Ref(this); + } + + ArrayRef largerCoefficients = coefficients; + ArrayRef smallerCoefficients = b->coefficients; + if (smallerCoefficients.size() > largerCoefficients.size()) { + ArrayRef tmp(smallerCoefficients); + smallerCoefficients = largerCoefficients; + largerCoefficients = tmp; + } + + ArrayRef sumDiff(new Array (largerCoefficients.size())); + + unsigned lengthDiff = largerCoefficients.size() - smallerCoefficients.size(); + for (unsigned i = 0; i < lengthDiff; i++) { + sumDiff[i] = largerCoefficients[i]; + } + for (unsigned i = lengthDiff; i < largerCoefficients.size(); i++) { + sumDiff[i] = GF256::addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); + } + return Ref(new GF256Poly(field, sumDiff)); +} + +Ref GF256Poly::multiply(Ref b) { + if (&field != &b->field) { + throw IllegalArgumentException("Fields must be the same"); + } + if (isZero() || b->isZero()) { + return field.getZero(); + } + ArrayRef aCoefficients = coefficients; + int aLength = aCoefficients.size(); + ArrayRef bCoefficients = b->coefficients; + int bLength = bCoefficients.size(); + int productLength = aLength + bLength - 1; + ArrayRef product(new Array (productLength)); + for (int i = 0; i < aLength; i++) { + int aCoeff = aCoefficients[i]; + for (int j = 0; j < bLength; j++) { + product[i + j] = GF256::addOrSubtract(product[i + j], field.multiply(aCoeff, bCoefficients[j])); } - - int GF256Poly::getDegree() { - return coefficients.size() - 1; + } + + return Ref(new GF256Poly(field, product)); +} + +Ref GF256Poly::multiply(int scalar) { + if (scalar == 0) { + return field.getZero(); + } + if (scalar == 1) { + return Ref(this); + } + int size = coefficients.size(); + ArrayRef product(new Array (size)); + for (int i = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], scalar); + } + return Ref(new GF256Poly(field, product)); +} + +Ref GF256Poly::multiplyByMonomial(int degree, int coefficient) { + if (degree < 0) { + throw IllegalArgumentException("Degree must be non-negative"); + } + if (coefficient == 0) { + return field.getZero(); + } + int size = coefficients.size(); + ArrayRef product(new Array (size + degree)); + for (int i = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], coefficient); + } + return Ref(new GF256Poly(field, product)); +} + +const char *GF256Poly::description() const { + ostringstream result; + result << *this; + return result.str().c_str(); +} + +ostream& operator<<(ostream& out, const GF256Poly& p) { + GF256Poly &poly = const_cast(p); + out << "Poly[" << poly.coefficients.size() << "]"; + if (poly.coefficients.size() > 0) { + out << "(" << poly.coefficients[0]; + for (unsigned i = 1; i < poly.coefficients.size(); i++) { + out << "," << poly.coefficients[i]; } - - bool GF256Poly::isZero() { - return coefficients[0] == 0; - } - - int GF256Poly::getCoefficient(int degree) { - return coefficients[coefficients.size() - 1 - degree]; - } - - int GF256Poly::evaluateAt(int a) { - if (a == 0) { - return getCoefficient(0); - } - int size = coefficients.size(); - if (a == 1) { - // Just the sum of the coefficients - int result = 0; - for (int i = 0; i < size; i++) { - result = GF256::addOrSubtract(result, coefficients[i]); - } - return result; - } - int result = coefficients[0]; - for (int i = 1; i < size; i++) { - result = GF256::addOrSubtract(field.multiply(a, result), coefficients[i]); - } - return result; - } - - Ref GF256Poly::addOrSubtract(Ref b) { - if (&field != &b->field) { - throw IllegalArgumentException("Fields must be the same"); - } - if (isZero()) { - return b; - } - if (b->isZero()) { - return Ref(this); - } - - ArrayRef largerCoefficients = coefficients; - ArrayRef smallerCoefficients = b->coefficients; - if (smallerCoefficients.size() > largerCoefficients.size()) { - ArrayRef tmp(smallerCoefficients); - smallerCoefficients = largerCoefficients; - largerCoefficients = tmp; - } - - ArrayRef sumDiff(new Array (largerCoefficients.size())); - - unsigned lengthDiff = largerCoefficients.size() - smallerCoefficients.size(); - for (unsigned i = 0; i < lengthDiff; i++) { - sumDiff[i] = largerCoefficients[i]; - } - for (unsigned i = lengthDiff; i < largerCoefficients.size(); i++) { - sumDiff[i] = GF256::addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); - } - return Ref(new GF256Poly(field, sumDiff)); - } - - Ref GF256Poly::multiply(Ref b) { - if (&field != &b->field) { - throw IllegalArgumentException("Fields must be the same"); - } - if (isZero() || b->isZero()) { - return field.getZero(); - } - ArrayRef aCoefficients = coefficients; - int aLength = aCoefficients.size(); - ArrayRef bCoefficients = b->coefficients; - int bLength = bCoefficients.size(); - int productLength = aLength + bLength - 1; - ArrayRef product(new Array (productLength)); - for (int i = 0; i < aLength; i++) { - int aCoeff = aCoefficients[i]; - for (int j = 0; j < bLength; j++) { - product[i + j] = GF256::addOrSubtract(product[i + j], field.multiply(aCoeff, bCoefficients[j])); - } - } - - return Ref(new GF256Poly(field, product)); - } - - Ref GF256Poly::multiply(int scalar) { - if (scalar == 0) { - return field.getZero(); - } - if (scalar == 1) { - return Ref(this); - } - int size = coefficients.size(); - ArrayRef product(new Array (size)); - for (int i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], scalar); - } - return Ref(new GF256Poly(field, product)); - } - - Ref GF256Poly::multiplyByMonomial(int degree, int coefficient) { - if (degree < 0) { - throw IllegalArgumentException("Degree must be non-negative"); - } - if (coefficient == 0) { - return field.getZero(); - } - int size = coefficients.size(); - ArrayRef product(new Array (size + degree)); - for (int i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], coefficient); - } - return Ref(new GF256Poly(field, product)); - } - - const char *GF256Poly::description() const { - ostringstream result; - result << *this; - return result.str().c_str(); - } - - ostream& operator<<(ostream& out, const GF256Poly& p) { - GF256Poly &poly = const_cast(p); - out << "Poly[" << poly.coefficients.size() << "]"; - if (poly.coefficients.size() > 0) { - out << "(" << poly.coefficients[0]; - for (unsigned i = 1; i < poly.coefficients.size(); i++) { - out << "," << poly.coefficients[i]; - } - out << ")"; - } - return out; - } - + out << ")"; + } + return out; +} + } // file: zxing/common/reedsolomon/ReedSolomonDecoder.cpp @@ -2554,166 +3606,171 @@ namespace zxing { using namespace std; namespace zxing { - - ReedSolomonDecoder::ReedSolomonDecoder(GF256 &fld) : + +ReedSolomonDecoder::ReedSolomonDecoder(GF256 &fld) : field(fld) { - } - - ReedSolomonDecoder::~ReedSolomonDecoder() { - } - - void ReedSolomonDecoder::decode(ArrayRef received, int twoS) { - - Ref poly(new GF256Poly(field, received)); - - +} + +ReedSolomonDecoder::~ReedSolomonDecoder() { +} + +void ReedSolomonDecoder::decode(ArrayRef received, int twoS) { + + Ref poly(new GF256Poly(field, received)); + + #ifdef DEBUG - cout << "decoding with poly " << *poly << "\n"; + cout << "decoding with poly " << *poly << "\n"; #endif - - ArrayRef syndromeCoefficients(new Array (twoS)); - - + + ArrayRef syndromeCoefficients(new Array (twoS)); + + #ifdef DEBUG - cout << "syndromeCoefficients array = " << - syndromeCoefficients.array_ << "\n"; + cout << "syndromeCoefficients array = " << + syndromeCoefficients.array_ << "\n"; #endif - - bool noError = true; - for (int i = 0; i < twoS; i++) { - int eval = poly->evaluateAt(field.exp(i)); - syndromeCoefficients[syndromeCoefficients->size() - 1 - i] = eval; - if (eval != 0) { - noError = false; - } - } - if (noError) { - return; - } - - Ref syndrome(new GF256Poly(field, syndromeCoefficients)); - Ref monomial(field.buildMonomial(twoS, 1)); - vector > sigmaOmega(runEuclideanAlgorithm(monomial, syndrome, twoS)); - ArrayRef errorLocations = findErrorLocations(sigmaOmega[0]); - ArrayRef errorMagitudes = findErrorMagnitudes(sigmaOmega[1], errorLocations); - for (unsigned i = 0; i < errorLocations->size(); i++) { - int position = received->size() - 1 - field.log(errorLocations[i]); - //TODO: check why the position would be invalid - if (position < 0 || (size_t)position >= received.size()) - throw IllegalArgumentException("Invalid position (ReedSolomonDecoder)"); - received[position] = GF256::addOrSubtract(received[position], errorMagitudes[i]); - } + + bool dataMatrix = (&field == &GF256::DATA_MATRIX_FIELD); + bool noError = true; + for (int i = 0; i < twoS; i++) { + int eval = poly->evaluateAt(field.exp(dataMatrix ? i + 1 : i)); + syndromeCoefficients[syndromeCoefficients->size() - 1 - i] = eval; + if (eval != 0) { + noError = false; } - - vector > ReedSolomonDecoder::runEuclideanAlgorithm(Ref a, Ref b, int R) { - // Assume a's degree is >= b's - if (a->getDegree() < b->getDegree()) { - Ref tmp = a; - a = b; - b = tmp; - } - - Ref rLast(a); - Ref r(b); - Ref sLast(field.getOne()); - Ref s(field.getZero()); - Ref tLast(field.getZero()); - Ref t(field.getOne()); - - - // Run Euclidean algorithm until r's degree is less than R/2 - while (r->getDegree() >= R / 2) { - Ref rLastLast(rLast); - Ref sLastLast(sLast); - Ref tLastLast(tLast); - rLast = r; - sLast = s; - tLast = t; - - - // Divide rLastLast by rLast, with quotient q and remainder r - if (rLast->isZero()) { - // Oops, Euclidean algorithm already terminated? - throw ReedSolomonException("r_{i-1} was zero"); - } - r = rLastLast; - Ref q(field.getZero()); - int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree()); - int dltInverse = field.inverse(denominatorLeadingTerm); - while (r->getDegree() >= rLast->getDegree() && !r->isZero()) { - int degreeDiff = r->getDegree() - rLast->getDegree(); - int scale = field.multiply(r->getCoefficient(r->getDegree()), dltInverse); - q = q->addOrSubtract(field.buildMonomial(degreeDiff, scale)); - r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale)); - } - - s = q->multiply(sLast)->addOrSubtract(sLastLast); - t = q->multiply(tLast)->addOrSubtract(tLastLast); - } - - int sigmaTildeAtZero = t->getCoefficient(0); - if (sigmaTildeAtZero == 0) { - throw ReedSolomonException("sigmaTilde(0) was zero"); - } - - int inverse = field.inverse(sigmaTildeAtZero); - Ref sigma(t->multiply(inverse)); - Ref omega(r->multiply(inverse)); - - + } + if (noError) { + return; + } + + Ref syndrome(new GF256Poly(field, syndromeCoefficients)); + Ref monomial(field.buildMonomial(twoS, 1)); + vector > sigmaOmega(runEuclideanAlgorithm(monomial, syndrome, twoS)); + ArrayRef errorLocations = findErrorLocations(sigmaOmega[0]); + ArrayRef errorMagitudes = findErrorMagnitudes(sigmaOmega[1], errorLocations, dataMatrix); + for (unsigned i = 0; i < errorLocations->size(); i++) { + int position = received->size() - 1 - field.log(errorLocations[i]); + //TODO: check why the position would be invalid + if (position < 0 || (size_t)position >= received.size()) + throw IllegalArgumentException("Invalid position (ReedSolomonDecoder)"); + received[position] = GF256::addOrSubtract(received[position], errorMagitudes[i]); + } +} + +vector > ReedSolomonDecoder::runEuclideanAlgorithm(Ref a, Ref b, int R) { + // Assume a's degree is >= b's + if (a->getDegree() < b->getDegree()) { + Ref tmp = a; + a = b; + b = tmp; + } + + Ref rLast(a); + Ref r(b); + Ref sLast(field.getOne()); + Ref s(field.getZero()); + Ref tLast(field.getZero()); + Ref t(field.getOne()); + + + // Run Euclidean algorithm until r's degree is less than R/2 + while (r->getDegree() >= R / 2) { + Ref rLastLast(rLast); + Ref sLastLast(sLast); + Ref tLastLast(tLast); + rLast = r; + sLast = s; + tLast = t; + + + // Divide rLastLast by rLast, with quotient q and remainder r + if (rLast->isZero()) { + // Oops, Euclidean algorithm already terminated? + throw ReedSolomonException("r_{i-1} was zero"); + } + r = rLastLast; + Ref q(field.getZero()); + int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree()); + int dltInverse = field.inverse(denominatorLeadingTerm); + while (r->getDegree() >= rLast->getDegree() && !r->isZero()) { + int degreeDiff = r->getDegree() - rLast->getDegree(); + int scale = field.multiply(r->getCoefficient(r->getDegree()), dltInverse); + q = q->addOrSubtract(field.buildMonomial(degreeDiff, scale)); + r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale)); + } + + s = q->multiply(sLast)->addOrSubtract(sLastLast); + t = q->multiply(tLast)->addOrSubtract(tLastLast); + } + + int sigmaTildeAtZero = t->getCoefficient(0); + if (sigmaTildeAtZero == 0) { + throw ReedSolomonException("sigmaTilde(0) was zero"); + } + + int inverse = field.inverse(sigmaTildeAtZero); + Ref sigma(t->multiply(inverse)); + Ref omega(r->multiply(inverse)); + + #ifdef DEBUG - cout << "t = " << *t << "\n"; - cout << "r = " << *r << "\n"; - cout << "sigma = " << *sigma << "\n"; - cout << "omega = " << *omega << "\n"; + cout << "t = " << *t << "\n"; + cout << "r = " << *r << "\n"; + cout << "sigma = " << *sigma << "\n"; + cout << "omega = " << *omega << "\n"; #endif - - vector > result(2); - result[0] = sigma; - result[1] = omega; - return result; + + vector > result(2); + result[0] = sigma; + result[1] = omega; + return result; +} + +ArrayRef ReedSolomonDecoder::findErrorLocations(Ref errorLocator) { + // This is a direct application of Chien's search + int numErrors = errorLocator->getDegree(); + if (numErrors == 1) { // shortcut + ArrayRef result(1); + result[0] = errorLocator->getCoefficient(1); + return result; + } + ArrayRef result(numErrors); + int e = 0; + for (int i = 1; i < 256 && e < numErrors; i++) { + // cout << "errorLocator(" << i << ") == " << errorLocator->evaluateAt(i) << "\n"; + if (errorLocator->evaluateAt(i) == 0) { + result[e] = field.inverse(i); + e++; } - - ArrayRef ReedSolomonDecoder::findErrorLocations(Ref errorLocator) { - // This is a direct application of Chien's search - int numErrors = errorLocator->getDegree(); - if (numErrors == 1) { // shortcut - ArrayRef result(1); - result[0] = errorLocator->getCoefficient(1); - return result; - } - ArrayRef result(numErrors); - int e = 0; - for (int i = 1; i < 256 && e < numErrors; i++) { - // cout << "errorLocator(" << i << ") == " << errorLocator->evaluateAt(i) << "\n"; - if (errorLocator->evaluateAt(i) == 0) { - result[e] = field.inverse(i); - e++; - } - } - if (e != numErrors) { - throw ReedSolomonException("Error locator degree does not match number of roots"); - } - return result; - } - - ArrayRef ReedSolomonDecoder::findErrorMagnitudes(Ref errorEvaluator, ArrayRef errorLocations) { - // This is directly applying Forney's Formula - int s = errorLocations.size(); - ArrayRef result(s); - for (int i = 0; i < s; i++) { - int xiInverse = field.inverse(errorLocations[i]); - int denominator = 1; - for (int j = 0; j < s; j++) { - if (i != j) { - denominator = field.multiply(denominator, GF256::addOrSubtract(1, field.multiply(errorLocations[j], - xiInverse))); - } - } - result[i] = field.multiply(errorEvaluator->evaluateAt(xiInverse), field.inverse(denominator)); - } - return result; + } + if (e != numErrors) { + throw ReedSolomonException("Error locator degree does not match number of roots"); + } + return result; +} + +ArrayRef ReedSolomonDecoder::findErrorMagnitudes(Ref errorEvaluator, ArrayRef errorLocations, bool dataMatrix) { + // This is directly applying Forney's Formula + int s = errorLocations.size(); + ArrayRef result(s); + for (int i = 0; i < s; i++) { + int xiInverse = field.inverse(errorLocations[i]); + int denominator = 1; + for (int j = 0; j < s; j++) { + if (i != j) { + denominator = field.multiply(denominator, GF256::addOrSubtract(1, field.multiply(errorLocations[j], + xiInverse))); + } } + result[i] = field.multiply(errorEvaluator->evaluateAt(xiInverse), field.inverse(denominator)); + + if (dataMatrix) { + result[i] = field.multiply(result[i], xiInverse); + } + } + return result; +} } // file: zxing/common/reedsolomon/ReedSolomonException.cpp @@ -2741,16 +3798,17 @@ namespace zxing { // #include namespace zxing { - ReedSolomonException::ReedSolomonException(const char *msg) throw() : +ReedSolomonException::ReedSolomonException(const char *msg) throw() : Exception(msg) { - } - ReedSolomonException::~ReedSolomonException() throw() { - } - +} +ReedSolomonException::~ReedSolomonException() throw() { +} + } // file: zxing/datamatrix/DataMatrixReader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * DataMatrixReader.cpp * zxing @@ -2776,62 +3834,63 @@ namespace zxing { // #include namespace zxing { - namespace datamatrix { - - using namespace std; - - DataMatrixReader::DataMatrixReader() : - decoder_() { - } - - Ref DataMatrixReader::decode(Ref image, DecodeHints hints) { +namespace datamatrix { + +using namespace std; + +DataMatrixReader::DataMatrixReader() : + decoder_() { +} + +Ref DataMatrixReader::decode(Ref image, DecodeHints hints) { + (void)hints; #ifdef DEBUG - cout << "decoding image " << image.object_ << ":\n" << flush; + cout << "decoding image " << image.object_ << ":\n" << flush; #endif - - Detector detector(image->getBlackMatrix()); - - + + Detector detector(image->getBlackMatrix()); + + #ifdef DEBUG - cout << "(1) created detector " << &detector << "\n" << flush; + cout << "(1) created detector " << &detector << "\n" << flush; #endif - - Ref detectorResult(detector.detect()); + + Ref detectorResult(detector.detect()); #ifdef DEBUG - cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush; + cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush; #endif - - std::vector > points(detectorResult->getPoints()); - - + + std::vector > points(detectorResult->getPoints()); + + #ifdef DEBUG - cout << "(3) extracted points " << &points << "\n" << flush; - cout << "found " << points.size() << " points:\n"; - for (size_t i = 0; i < points.size(); i++) { - cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n"; - } - cout << "bits:\n"; - cout << *(detectorResult->getBits()) << "\n"; + cout << "(3) extracted points " << &points << "\n" << flush; + cout << "found " << points.size() << " points:\n"; + for (size_t i = 0; i < points.size(); i++) { + cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n"; + } + cout << "bits:\n"; + cout << *(detectorResult->getBits()) << "\n"; #endif - - Ref decoderResult(decoder_.decode(detectorResult->getBits())); + + Ref decoderResult(decoder_.decode(detectorResult->getBits())); #ifdef DEBUG - cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush; + cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush; #endif - - Ref result( - new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_DATA_MATRIX)); + + Ref result( + new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_DATA_MATRIX)); #ifdef DEBUG - cout << "(5) created result " << result.object_ << ", returning\n" << flush; + cout << "(5) created result " << result.object_ << ", returning\n" << flush; #endif - - return result; - } - - DataMatrixReader::~DataMatrixReader() { - } - - } + + return result; +} + +DataMatrixReader::~DataMatrixReader() { +} + +} } // file: zxing/datamatrix/Version.cpp @@ -2861,179 +3920,179 @@ namespace zxing { // #include namespace zxing { - namespace datamatrix { - using namespace std; - - ECB::ECB(int count, int dataCodewords) : - count_(count), dataCodewords_(dataCodewords) { - } - - int ECB::getCount() { - return count_; - } - - int ECB::getDataCodewords() { - return dataCodewords_; - } - - ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) : - ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) { - } - - ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) : - ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) { - ecBlocks_.push_back(ecBlocks2); - } - - int ECBlocks::getECCodewords() { - return ecCodewords_; - } - - std::vector& ECBlocks::getECBlocks() { - return ecBlocks_; - } - - ECBlocks::~ECBlocks() { - for (size_t i = 0; i < ecBlocks_.size(); i++) { - delete ecBlocks_[i]; - } - } - - vector > Version::VERSIONS; - static int N_VERSIONS = Version::buildVersions(); - - Version::Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns, int dataRegionSizeRows, - int dataRegionSizeColumns, ECBlocks* ecBlocks) : versionNumber_(versionNumber), +namespace datamatrix { +using namespace std; + +ECB::ECB(int count, int dataCodewords) : + count_(count), dataCodewords_(dataCodewords) { +} + +int ECB::getCount() { + return count_; +} + +int ECB::getDataCodewords() { + return dataCodewords_; +} + +ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) : + ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) { +} + +ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) : + ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) { + ecBlocks_.push_back(ecBlocks2); +} + +int ECBlocks::getECCodewords() { + return ecCodewords_; +} + +std::vector& ECBlocks::getECBlocks() { + return ecBlocks_; +} + +ECBlocks::~ECBlocks() { + for (size_t i = 0; i < ecBlocks_.size(); i++) { + delete ecBlocks_[i]; + } +} + +vector > Version::VERSIONS; +static int N_VERSIONS = Version::buildVersions(); + +Version::Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns, int dataRegionSizeRows, + int dataRegionSizeColumns, ECBlocks* ecBlocks) : versionNumber_(versionNumber), symbolSizeRows_(symbolSizeRows), symbolSizeColumns_(symbolSizeColumns), dataRegionSizeRows_(dataRegionSizeRows), dataRegionSizeColumns_(dataRegionSizeColumns), ecBlocks_(ecBlocks), totalCodewords_(0) { - // Calculate the total number of codewords - int total = 0; - int ecCodewords = ecBlocks_->getECCodewords(); - vector &ecbArray = ecBlocks_->getECBlocks(); - for (unsigned int i = 0; i < ecbArray.size(); i++) { - ECB *ecBlock = ecbArray[i]; - total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords); - } - totalCodewords_ = total; - } - - Version::~Version() { - delete ecBlocks_; - } - - int Version::getVersionNumber() { - return versionNumber_; - } - - int Version::getSymbolSizeRows() { - return symbolSizeRows_; - } - - int Version::getSymbolSizeColumns() { - return symbolSizeColumns_; - } - - int Version::getDataRegionSizeRows() { - return dataRegionSizeRows_; - } - - int Version::getDataRegionSizeColumns() { - return dataRegionSizeColumns_; - } - - int Version::getTotalCodewords() { - return totalCodewords_; - } - - ECBlocks* Version::getECBlocks() { - return ecBlocks_; - } - - Ref Version::getVersionForDimensions(int numRows, int numColumns) { - if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) { - throw ReaderException("Number of rows and columns must be even"); - } - - // TODO(bbrown): This is doing a linear search through the array of versions. - // If we interleave the rectangular versions with the square versions we could - // do a binary search. - for (int i = 0; i < N_VERSIONS; ++i){ - Ref version(VERSIONS[i]); - if (version->getSymbolSizeRows() == numRows && version->getSymbolSizeColumns() == numColumns) { - return version; - } - } - throw ReaderException("Error version not found"); - } - - /** - * See ISO 16022:2006 5.5.1 Table 7 - */ - int Version::buildVersions() { - VERSIONS.push_back(Ref(new Version(1, 10, 10, 8, 8, - new ECBlocks(5, new ECB(1, 3))))); - VERSIONS.push_back(Ref(new Version(2, 12, 12, 10, 10, - new ECBlocks(7, new ECB(1, 5))))); - VERSIONS.push_back(Ref(new Version(3, 14, 14, 12, 12, - new ECBlocks(10, new ECB(1, 8))))); - VERSIONS.push_back(Ref(new Version(4, 16, 16, 14, 14, - new ECBlocks(12, new ECB(1, 12))))); - VERSIONS.push_back(Ref(new Version(5, 18, 18, 16, 16, - new ECBlocks(14, new ECB(1, 18))))); - VERSIONS.push_back(Ref(new Version(6, 20, 20, 18, 18, - new ECBlocks(18, new ECB(1, 22))))); - VERSIONS.push_back(Ref(new Version(7, 22, 22, 20, 20, - new ECBlocks(20, new ECB(1, 30))))); - VERSIONS.push_back(Ref(new Version(8, 24, 24, 22, 22, - new ECBlocks(24, new ECB(1, 36))))); - VERSIONS.push_back(Ref(new Version(9, 26, 26, 24, 24, - new ECBlocks(28, new ECB(1, 44))))); - VERSIONS.push_back(Ref(new Version(10, 32, 32, 14, 14, - new ECBlocks(36, new ECB(1, 62))))); - VERSIONS.push_back(Ref(new Version(11, 36, 36, 16, 16, - new ECBlocks(42, new ECB(1, 86))))); - VERSIONS.push_back(Ref(new Version(12, 40, 40, 18, 18, - new ECBlocks(48, new ECB(1, 114))))); - VERSIONS.push_back(Ref(new Version(13, 44, 44, 20, 20, - new ECBlocks(56, new ECB(1, 144))))); - VERSIONS.push_back(Ref(new Version(14, 48, 48, 22, 22, - new ECBlocks(68, new ECB(1, 174))))); - VERSIONS.push_back(Ref(new Version(15, 52, 52, 24, 24, - new ECBlocks(42, new ECB(2, 102))))); - VERSIONS.push_back(Ref(new Version(16, 64, 64, 14, 14, - new ECBlocks(56, new ECB(2, 140))))); - VERSIONS.push_back(Ref(new Version(17, 72, 72, 16, 16, - new ECBlocks(36, new ECB(4, 92))))); - VERSIONS.push_back(Ref(new Version(18, 80, 80, 18, 18, - new ECBlocks(48, new ECB(4, 114))))); - VERSIONS.push_back(Ref(new Version(19, 88, 88, 20, 20, - new ECBlocks(56, new ECB(4, 144))))); - VERSIONS.push_back(Ref(new Version(20, 96, 96, 22, 22, - new ECBlocks(68, new ECB(4, 174))))); - VERSIONS.push_back(Ref(new Version(21, 104, 104, 24, 24, - new ECBlocks(56, new ECB(6, 136))))); - VERSIONS.push_back(Ref(new Version(22, 120, 120, 18, 18, - new ECBlocks(68, new ECB(6, 175))))); - VERSIONS.push_back(Ref(new Version(23, 132, 132, 20, 20, - new ECBlocks(62, new ECB(8, 163))))); - VERSIONS.push_back(Ref(new Version(24, 144, 144, 22, 22, - new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))))); - VERSIONS.push_back(Ref(new Version(25, 8, 18, 6, 16, - new ECBlocks(7, new ECB(1, 5))))); - VERSIONS.push_back(Ref(new Version(26, 8, 32, 6, 14, - new ECBlocks(11, new ECB(1, 10))))); - VERSIONS.push_back(Ref(new Version(27, 12, 26, 10, 24, - new ECBlocks(14, new ECB(1, 16))))); - VERSIONS.push_back(Ref(new Version(28, 12, 36, 10, 16, - new ECBlocks(18, new ECB(1, 22))))); - VERSIONS.push_back(Ref(new Version(29, 16, 36, 10, 16, - new ECBlocks(24, new ECB(1, 32))))); - VERSIONS.push_back(Ref(new Version(30, 16, 48, 14, 22, - new ECBlocks(28, new ECB(1, 49))))); - return VERSIONS.size(); - } + // Calculate the total number of codewords + int total = 0; + int ecCodewords = ecBlocks_->getECCodewords(); + vector &ecbArray = ecBlocks_->getECBlocks(); + for (unsigned int i = 0; i < ecbArray.size(); i++) { + ECB *ecBlock = ecbArray[i]; + total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords); } + totalCodewords_ = total; +} + +Version::~Version() { + delete ecBlocks_; +} + +int Version::getVersionNumber() { + return versionNumber_; +} + +int Version::getSymbolSizeRows() { + return symbolSizeRows_; +} + +int Version::getSymbolSizeColumns() { + return symbolSizeColumns_; +} + +int Version::getDataRegionSizeRows() { + return dataRegionSizeRows_; +} + +int Version::getDataRegionSizeColumns() { + return dataRegionSizeColumns_; +} + +int Version::getTotalCodewords() { + return totalCodewords_; +} + +ECBlocks* Version::getECBlocks() { + return ecBlocks_; +} + +Ref Version::getVersionForDimensions(int numRows, int numColumns) { + if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) { + throw ReaderException("Number of rows and columns must be even"); + } + + // TODO(bbrown): This is doing a linear search through the array of versions. + // If we interleave the rectangular versions with the square versions we could + // do a binary search. + for (int i = 0; i < N_VERSIONS; ++i){ + Ref version(VERSIONS[i]); + if (version->getSymbolSizeRows() == numRows && version->getSymbolSizeColumns() == numColumns) { + return version; + } + } + throw ReaderException("Error version not found"); + } + +/** + * See ISO 16022:2006 5.5.1 Table 7 + */ +int Version::buildVersions() { + VERSIONS.push_back(Ref(new Version(1, 10, 10, 8, 8, + new ECBlocks(5, new ECB(1, 3))))); + VERSIONS.push_back(Ref(new Version(2, 12, 12, 10, 10, + new ECBlocks(7, new ECB(1, 5))))); + VERSIONS.push_back(Ref(new Version(3, 14, 14, 12, 12, + new ECBlocks(10, new ECB(1, 8))))); + VERSIONS.push_back(Ref(new Version(4, 16, 16, 14, 14, + new ECBlocks(12, new ECB(1, 12))))); + VERSIONS.push_back(Ref(new Version(5, 18, 18, 16, 16, + new ECBlocks(14, new ECB(1, 18))))); + VERSIONS.push_back(Ref(new Version(6, 20, 20, 18, 18, + new ECBlocks(18, new ECB(1, 22))))); + VERSIONS.push_back(Ref(new Version(7, 22, 22, 20, 20, + new ECBlocks(20, new ECB(1, 30))))); + VERSIONS.push_back(Ref(new Version(8, 24, 24, 22, 22, + new ECBlocks(24, new ECB(1, 36))))); + VERSIONS.push_back(Ref(new Version(9, 26, 26, 24, 24, + new ECBlocks(28, new ECB(1, 44))))); + VERSIONS.push_back(Ref(new Version(10, 32, 32, 14, 14, + new ECBlocks(36, new ECB(1, 62))))); + VERSIONS.push_back(Ref(new Version(11, 36, 36, 16, 16, + new ECBlocks(42, new ECB(1, 86))))); + VERSIONS.push_back(Ref(new Version(12, 40, 40, 18, 18, + new ECBlocks(48, new ECB(1, 114))))); + VERSIONS.push_back(Ref(new Version(13, 44, 44, 20, 20, + new ECBlocks(56, new ECB(1, 144))))); + VERSIONS.push_back(Ref(new Version(14, 48, 48, 22, 22, + new ECBlocks(68, new ECB(1, 174))))); + VERSIONS.push_back(Ref(new Version(15, 52, 52, 24, 24, + new ECBlocks(42, new ECB(2, 102))))); + VERSIONS.push_back(Ref(new Version(16, 64, 64, 14, 14, + new ECBlocks(56, new ECB(2, 140))))); + VERSIONS.push_back(Ref(new Version(17, 72, 72, 16, 16, + new ECBlocks(36, new ECB(4, 92))))); + VERSIONS.push_back(Ref(new Version(18, 80, 80, 18, 18, + new ECBlocks(48, new ECB(4, 114))))); + VERSIONS.push_back(Ref(new Version(19, 88, 88, 20, 20, + new ECBlocks(56, new ECB(4, 144))))); + VERSIONS.push_back(Ref(new Version(20, 96, 96, 22, 22, + new ECBlocks(68, new ECB(4, 174))))); + VERSIONS.push_back(Ref(new Version(21, 104, 104, 24, 24, + new ECBlocks(56, new ECB(6, 136))))); + VERSIONS.push_back(Ref(new Version(22, 120, 120, 18, 18, + new ECBlocks(68, new ECB(6, 175))))); + VERSIONS.push_back(Ref(new Version(23, 132, 132, 20, 20, + new ECBlocks(62, new ECB(8, 163))))); + VERSIONS.push_back(Ref(new Version(24, 144, 144, 22, 22, + new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))))); + VERSIONS.push_back(Ref(new Version(25, 8, 18, 6, 16, + new ECBlocks(7, new ECB(1, 5))))); + VERSIONS.push_back(Ref(new Version(26, 8, 32, 6, 14, + new ECBlocks(11, new ECB(1, 10))))); + VERSIONS.push_back(Ref(new Version(27, 12, 26, 10, 24, + new ECBlocks(14, new ECB(1, 16))))); + VERSIONS.push_back(Ref(new Version(28, 12, 36, 10, 16, + new ECBlocks(18, new ECB(1, 22))))); + VERSIONS.push_back(Ref(new Version(29, 16, 36, 14, 16, + new ECBlocks(24, new ECB(1, 32))))); + VERSIONS.push_back(Ref(new Version(30, 16, 48, 14, 22, + new ECBlocks(28, new ECB(1, 49))))); + return VERSIONS.size(); +} +} } // file: zxing/datamatrix/decoder/BitMatrixParser.cpp @@ -3061,346 +4120,343 @@ namespace zxing { // #include // #include +// #include + namespace zxing { - namespace datamatrix { - - int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) { - return bitMatrix_->get(x, y) ? (versionBits << 1) | 0x1 : versionBits << 1; - } - - BitMatrixParser::BitMatrixParser(Ref bitMatrix) : bitMatrix_(NULL), - parsedVersion_(NULL), - readBitMatrix_(NULL) { - size_t dimension = bitMatrix->getDimension(); - if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) - throw ReaderException("Dimension must be even, > 10 < 144"); - - parsedVersion_ = readVersion(bitMatrix); - bitMatrix_ = extractDataRegion(bitMatrix); - // TODO(bbrown): Make this work for rectangular symbols - readBitMatrix_ = new BitMatrix(bitMatrix_->getDimension()); - } - - Ref BitMatrixParser::readVersion(Ref bitMatrix) { - if (parsedVersion_ != 0) { - return parsedVersion_; - } - - // TODO(bbrown): make this work for rectangular dimensions as well. - int numRows = bitMatrix->getDimension(); - int numColumns = numRows; - - Ref version = parsedVersion_->getVersionForDimensions(numRows, numColumns); - if (version != 0) { - return version; - } - throw ReaderException("Couldn't decode version"); - } - - ArrayRef BitMatrixParser::readCodewords() { - ArrayRef result(parsedVersion_->getTotalCodewords()); - int resultOffset = 0; - int row = 4; - int column = 0; - - // TODO(bbrown): Data Matrix can be rectangular, assuming square for now - int numRows = bitMatrix_->getDimension(); - int numColumns = numRows; - - bool corner1Read = false; - bool corner2Read = false; - bool corner3Read = false; - bool corner4Read = false; - - // Read all of the codewords - do { - // Check the four corner cases - if ((row == numRows) && (column == 0) && !corner1Read) { - result[resultOffset++] = (unsigned char) readCorner1(numRows, numColumns); - row -= 2; - column +=2; - corner1Read = true; - } else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) { - result[resultOffset++] = (unsigned char) readCorner2(numRows, numColumns); - row -= 2; - column +=2; - corner2Read = true; - } else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) { - result[resultOffset++] = (unsigned char) readCorner3(numRows, numColumns); - row -= 2; - column +=2; - corner3Read = true; - } else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) { - result[resultOffset++] = (unsigned char) readCorner4(numRows, numColumns); - row -= 2; - column +=2; - corner4Read = true; - } else { - // Sweep upward diagonally to the right - do { - if ((row < numRows) && (column >= 0) && !readBitMatrix_->get(column, row)) { - result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns); - } - row -= 2; - column +=2; - } while ((row >= 0) && (column < numColumns)); - row += 1; - column +=3; - - // Sweep downward diagonally to the left - do { - if ((row >= 0) && (column < numColumns) && !readBitMatrix_->get(column, row)) { - result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns); - } - row += 2; - column -=2; - } while ((row < numRows) && (column >= 0)); - row += 3; - column +=1; - } - } while ((row < numRows) || (column < numColumns)); - - if (resultOffset != parsedVersion_->getTotalCodewords()) { - throw ReaderException("Did not read all codewords"); - } - return result; - } - - bool BitMatrixParser::readModule(int row, int column, int numRows, int numColumns) { - // Adjust the row and column indices based on boundary wrapping - if (row < 0) { - row += numRows; - column += 4 - ((numRows + 4) & 0x07); - } - if (column < 0) { - column += numColumns; - row += 4 - ((numColumns + 4) & 0x07); - } - readBitMatrix_->set(column, row); - return bitMatrix_->get(column, row); - } - - int BitMatrixParser::readUtah(int row, int column, int numRows, int numColumns) { - int currentByte = 0; - if (readModule(row - 2, column - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row - 2, column - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row - 1, column - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row - 1, column - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row - 1, column, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row, column - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row, column - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(row, column, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - - int BitMatrixParser::readCorner1(int numRows, int numColumns) { - int currentByte = 0; - if (readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 1, 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 1, 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(2, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(3, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - - int BitMatrixParser::readCorner2(int numRows, int numColumns) { - int currentByte = 0; - if (readModule(numRows - 3, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 2, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 4, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 3, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - - int BitMatrixParser::readCorner3(int numRows, int numColumns) { - int currentByte = 0; - if (readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 3, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(1, numColumns - 3, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(1, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - - int BitMatrixParser::readCorner4(int numRows, int numColumns) { - int currentByte = 0; - if (readModule(numRows - 3, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 2, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(2, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (readModule(3, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - - Ref BitMatrixParser::extractDataRegion(Ref bitMatrix) { - int symbolSizeRows = parsedVersion_->getSymbolSizeRows(); - int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns(); - - // TODO(bbrown): Make this work with rectangular codes - if ((int)bitMatrix->getDimension() != symbolSizeRows) { - throw IllegalArgumentException("Dimension of bitMarix must match the version size"); - } - - int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows(); - int dataRegionSizeColumns = parsedVersion_->getDataRegionSizeColumns(); - - int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows; - int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; - - int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; - //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; - - // TODO(bbrown): Make this work with rectangular codes - Ref bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionRow)); - for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { - int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; - for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { - int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; - for (int i = 0; i < dataRegionSizeRows; ++i) { - int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; - int writeRowOffset = dataRegionRowOffset + i; - for (int j = 0; j < dataRegionSizeColumns; ++j) { - int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; - if (bitMatrix->get(readColumnOffset, readRowOffset)) { - int writeColumnOffset = dataRegionColumnOffset + j; - bitMatrixWithoutAlignment->set(writeColumnOffset, writeRowOffset); - } - } - } - } - } - return bitMatrixWithoutAlignment; - } - +namespace datamatrix { + +int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) { + return bitMatrix_->get(x, y) ? (versionBits << 1) | 0x1 : versionBits << 1; +} + +BitMatrixParser::BitMatrixParser(Ref bitMatrix) : bitMatrix_(NULL), + parsedVersion_(NULL), + readBitMatrix_(NULL) { + size_t dimension = bitMatrix->getDimension(); + if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) + throw ReaderException("Dimension must be even, > 8 < 144"); + + parsedVersion_ = readVersion(bitMatrix); + bitMatrix_ = extractDataRegion(bitMatrix); + readBitMatrix_ = new BitMatrix(bitMatrix_->getWidth(), bitMatrix_->getHeight()); +} + +Ref BitMatrixParser::readVersion(Ref bitMatrix) { + if (parsedVersion_ != 0) { + return parsedVersion_; + } + + int numRows = bitMatrix->getHeight();//getDimension(); + int numColumns = bitMatrix->getWidth();//numRows; + + Ref version = parsedVersion_->getVersionForDimensions(numRows, numColumns); + if (version != 0) { + return version; + } + throw ReaderException("Couldn't decode version"); +} + +ArrayRef BitMatrixParser::readCodewords() { + ArrayRef result(parsedVersion_->getTotalCodewords()); + int resultOffset = 0; + int row = 4; + int column = 0; + + int numRows = bitMatrix_->getHeight(); + int numColumns = bitMatrix_->getWidth(); + + bool corner1Read = false; + bool corner2Read = false; + bool corner3Read = false; + bool corner4Read = false; + + // Read all of the codewords + do { + // Check the four corner cases + if ((row == numRows) && (column == 0) && !corner1Read) { + result[resultOffset++] = (unsigned char) readCorner1(numRows, numColumns); + row -= 2; + column +=2; + corner1Read = true; + } else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) { + result[resultOffset++] = (unsigned char) readCorner2(numRows, numColumns); + row -= 2; + column +=2; + corner2Read = true; + } else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) { + result[resultOffset++] = (unsigned char) readCorner3(numRows, numColumns); + row -= 2; + column +=2; + corner3Read = true; + } else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) { + result[resultOffset++] = (unsigned char) readCorner4(numRows, numColumns); + row -= 2; + column +=2; + corner4Read = true; + } else { + // Sweep upward diagonally to the right + do { + if ((row < numRows) && (column >= 0) && !readBitMatrix_->get(column, row)) { + result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns); + } + row -= 2; + column +=2; + } while ((row >= 0) && (column < numColumns)); + row += 1; + column +=3; + + // Sweep downward diagonally to the left + do { + if ((row >= 0) && (column < numColumns) && !readBitMatrix_->get(column, row)) { + result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns); + } + row += 2; + column -=2; + } while ((row < numRows) && (column >= 0)); + row += 3; + column +=1; + } + } while ((row < numRows) || (column < numColumns)); + + if (resultOffset != parsedVersion_->getTotalCodewords()) { + throw ReaderException("Did not read all codewords"); } + return result; +} + +bool BitMatrixParser::readModule(int row, int column, int numRows, int numColumns) { + // Adjust the row and column indices based on boundary wrapping + if (row < 0) { + row += numRows; + column += 4 - ((numRows + 4) & 0x07); + } + if (column < 0) { + column += numColumns; + row += 4 - ((numColumns + 4) & 0x07); + } + readBitMatrix_->set(column, row); + return bitMatrix_->get(column, row); + } + +int BitMatrixParser::readUtah(int row, int column, int numRows, int numColumns) { + int currentByte = 0; + if (readModule(row - 2, column - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 2, column - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 1, column - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 1, column - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row - 1, column, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row, column - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row, column - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(row, column, numRows, numColumns)) { + currentByte |= 1; + } + return currentByte; + } + +int BitMatrixParser::readCorner1(int numRows, int numColumns) { + int currentByte = 0; + if (readModule(numRows - 1, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(2, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(3, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + return currentByte; + } + +int BitMatrixParser::readCorner2(int numRows, int numColumns) { + int currentByte = 0; + if (readModule(numRows - 3, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 2, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 4, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 3, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + return currentByte; + } + +int BitMatrixParser::readCorner3(int numRows, int numColumns) { + int currentByte = 0; + if (readModule(numRows - 1, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 3, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 3, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + return currentByte; + } + +int BitMatrixParser::readCorner4(int numRows, int numColumns) { + int currentByte = 0; + if (readModule(numRows - 3, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 2, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(numRows - 1, 0, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 2, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(0, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(1, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(2, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + currentByte <<= 1; + if (readModule(3, numColumns - 1, numRows, numColumns)) { + currentByte |= 1; + } + return currentByte; + } + +Ref BitMatrixParser::extractDataRegion(Ref bitMatrix) { + int symbolSizeRows = parsedVersion_->getSymbolSizeRows(); + int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns(); + + if ((int)bitMatrix->getHeight() != symbolSizeRows) { + throw IllegalArgumentException("Dimension of bitMatrix must match the version size"); + } + + int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows(); + int dataRegionSizeColumns = parsedVersion_->getDataRegionSizeColumns(); + + int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows; + int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; + + int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; + int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; + + Ref bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow)); + for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { + int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; + for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { + int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; + for (int i = 0; i < dataRegionSizeRows; ++i) { + int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; + int writeRowOffset = dataRegionRowOffset + i; + for (int j = 0; j < dataRegionSizeColumns; ++j) { + int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; + if (bitMatrix->get(readColumnOffset, readRowOffset)) { + int writeColumnOffset = dataRegionColumnOffset + j; + bitMatrixWithoutAlignment->set(writeColumnOffset, writeRowOffset); + } + } + } + } + } + return bitMatrixWithoutAlignment; +} + +} } // file: zxing/datamatrix/decoder/DataBlock.cpp @@ -3429,94 +4485,94 @@ namespace zxing { // #include namespace zxing { - namespace datamatrix { - - using namespace std; - - DataBlock::DataBlock(int numDataCodewords, ArrayRef codewords) : - numDataCodewords_(numDataCodewords), codewords_(codewords) { - } - - int DataBlock::getNumDataCodewords() { - return numDataCodewords_; - } - - ArrayRef DataBlock::getCodewords() { - return codewords_; - } - - std::vector > DataBlock::getDataBlocks(ArrayRef rawCodewords, Version *version) { - // Figure out the number and size of data blocks used by this version and - // error correction level - ECBlocks* ecBlocks = version->getECBlocks(); - - // First count the total number of data blocks - int totalBlocks = 0; - vector ecBlockArray = ecBlocks->getECBlocks(); - for (size_t i = 0; i < ecBlockArray.size(); i++) { - totalBlocks += ecBlockArray[i]->getCount(); - } - - // Now establish DataBlocks of the appropriate size and number of data codewords - std::vector > result(totalBlocks); - int numResultBlocks = 0; - for (size_t j = 0; j < ecBlockArray.size(); j++) { - ECB *ecBlock = ecBlockArray[j]; - for (int i = 0; i < ecBlock->getCount(); i++) { - int numDataCodewords = ecBlock->getDataCodewords(); - int numBlockCodewords = ecBlocks->getECCodewords() + numDataCodewords; - ArrayRef buffer(numBlockCodewords); - Ref blockRef(new DataBlock(numDataCodewords, buffer)); - result[numResultBlocks++] = blockRef; - } - } - - // All blocks have the same amount of data, except that the last n - // (where n may be 0) have 1 more byte. Figure out where these start. - int shorterBlocksTotalCodewords = result[0]->codewords_.size(); - int longerBlocksStartAt = result.size() - 1; - while (longerBlocksStartAt >= 0) { - int numCodewords = result[longerBlocksStartAt]->codewords_.size(); - if (numCodewords == shorterBlocksTotalCodewords) { - break; - } - if (numCodewords != shorterBlocksTotalCodewords + 1) { - throw IllegalArgumentException("Data block sizes differ by more than 1"); - } - longerBlocksStartAt--; - } - longerBlocksStartAt++; - - int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks->getECCodewords(); - // The last elements of result may be 1 element longer; - // first fill out as many elements as all of them have - int rawCodewordsOffset = 0; - for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { - for (int j = 0; j < numResultBlocks; j++) { - result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++]; - } - } - // Fill out the last data block in the longer ones - for (int j = longerBlocksStartAt; j < numResultBlocks; j++) { - result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; - } - // Now add in error correction blocks - int max = result[0]->codewords_.size(); - for (int i = shorterBlocksNumDataCodewords; i < max; i++) { - for (int j = 0; j < numResultBlocks; j++) { - int iOffset = j < longerBlocksStartAt ? i : i + 1; - result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++]; - } - } - - if ((size_t)rawCodewordsOffset != rawCodewords.size()) { - throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length"); - } - - return result; - } - +namespace datamatrix { + +using namespace std; + +DataBlock::DataBlock(int numDataCodewords, ArrayRef codewords) : + numDataCodewords_(numDataCodewords), codewords_(codewords) { +} + +int DataBlock::getNumDataCodewords() { + return numDataCodewords_; +} + +ArrayRef DataBlock::getCodewords() { + return codewords_; +} + +std::vector > DataBlock::getDataBlocks(ArrayRef rawCodewords, Version *version) { + // Figure out the number and size of data blocks used by this version and + // error correction level + ECBlocks* ecBlocks = version->getECBlocks(); + + // First count the total number of data blocks + int totalBlocks = 0; + vector ecBlockArray = ecBlocks->getECBlocks(); + for (size_t i = 0; i < ecBlockArray.size(); i++) { + totalBlocks += ecBlockArray[i]->getCount(); + } + + // Now establish DataBlocks of the appropriate size and number of data codewords + std::vector > result(totalBlocks); + int numResultBlocks = 0; + for (size_t j = 0; j < ecBlockArray.size(); j++) { + ECB *ecBlock = ecBlockArray[j]; + for (int i = 0; i < ecBlock->getCount(); i++) { + int numDataCodewords = ecBlock->getDataCodewords(); + int numBlockCodewords = ecBlocks->getECCodewords() + numDataCodewords; + ArrayRef buffer(numBlockCodewords); + Ref blockRef(new DataBlock(numDataCodewords, buffer)); + result[numResultBlocks++] = blockRef; } + } + + // All blocks have the same amount of data, except that the last n + // (where n may be 0) have 1 more byte. Figure out where these start. + int shorterBlocksTotalCodewords = result[0]->codewords_.size(); + int longerBlocksStartAt = result.size() - 1; + while (longerBlocksStartAt >= 0) { + int numCodewords = result[longerBlocksStartAt]->codewords_.size(); + if (numCodewords == shorterBlocksTotalCodewords) { + break; + } + if (numCodewords != shorterBlocksTotalCodewords + 1) { + throw IllegalArgumentException("Data block sizes differ by more than 1"); + } + longerBlocksStartAt--; + } + longerBlocksStartAt++; + + int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks->getECCodewords(); + // The last elements of result may be 1 element longer; + // first fill out as many elements as all of them have + int rawCodewordsOffset = 0; + for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { + for (int j = 0; j < numResultBlocks; j++) { + result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++]; + } + } + // Fill out the last data block in the longer ones + for (int j = longerBlocksStartAt; j < numResultBlocks; j++) { + result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; + } + // Now add in error correction blocks + int max = result[0]->codewords_.size(); + for (int i = shorterBlocksNumDataCodewords; i < max; i++) { + for (int j = 0; j < numResultBlocks; j++) { + int iOffset = j < longerBlocksStartAt ? i : i + 1; + result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++]; + } + } + + if ((size_t)rawCodewordsOffset != rawCodewords.size()) { + throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length"); + } + + return result; +} + +} } // file: zxing/datamatrix/decoder/DecodedBitStreamParser.cpp @@ -3541,388 +4597,401 @@ namespace zxing { * limitations under the License. */ -// #include +// #include // #include // #include +// #include namespace zxing { - namespace datamatrix { - - using namespace std; - - const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = { - '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - }; - - const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = { - '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', - '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' - }; - - const char DecodedBitStreamParser::TEXT_BASIC_SET_CHARS[] = { - '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' - }; - - const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = { - '\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127 - }; - - std::string DecodedBitStreamParser::decode(ArrayRef bytes) { - Ref bits(new BitSource(bytes)); - ostringstream result; - ostringstream resultTrailer; - // bool trailer = false; - int mode = ASCII_ENCODE; - do { - if (mode == ASCII_ENCODE) { - mode = decodeAsciiSegment(bits, result, resultTrailer); - } else { - switch (mode) { - case C40_ENCODE: - decodeC40Segment(bits, result); - break; - case TEXT_ENCODE: - decodeTextSegment(bits, result); - break; - case ANSIX12_ENCODE: - decodeAnsiX12Segment(bits, result); - break; - case EDIFACT_ENCODE: - decodeEdifactSegment(bits, result); - break; - case BASE256_ENCODE: - decodeBase256Segment(bits, result); - break; - default: - throw ReaderException("Unsupported mode indicator"); - } - mode = ASCII_ENCODE; - } - } while (mode != PAD_ENCODE && bits->available() > 0); - /* if (trailer) { - result << resultTrailer; - } - */ - return result.str(); - } - - int DecodedBitStreamParser::decodeAsciiSegment(Ref bits, ostringstream & result, - ostringstream & resultTrailer) { - bool upperShift = false; - do { - int oneByte = bits->readBits(8); - if (oneByte == 0) { - throw ReaderException("Not enough bits to decode"); - } else if (oneByte <= 128) { // ASCII data (ASCII value + 1) - oneByte = upperShift ? (oneByte + 128) : oneByte; - upperShift = false; - result << (char) (oneByte - 1); - return ASCII_ENCODE; - } else if (oneByte == 129) { // Pad - return PAD_ENCODE; - } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) - int value = oneByte - 130; - if (value < 10) { // padd with '0' for single digit values - result << '0'; - } - result << value; - } else if (oneByte == 230) { // Latch to C40 encodation - return C40_ENCODE; - } else if (oneByte == 231) { // Latch to Base 256 encodation - return BASE256_ENCODE; - } else if (oneByte == 232) { // FNC1 - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte == 233) { // Structured Append - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte == 234) { // Reader Programming - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII) - upperShift = true; - } else if (oneByte == 236) { // 05 Macro - /* trailer = false; - result << "[)>\u001E05\u001D"; - resultTrailer << "\u001E\u0004"; - // Ignore this symbol for now - */ } else if (oneByte == 237) { // 06 Macro - /* trailer = false; - result << "[)>\u001E06\u001D"; - resultTrailer << "\u001E\u0004"; - // Ignore this symbol for now - */ } else if (oneByte == 238) { // Latch to ANSI X12 encodation - return ANSIX12_ENCODE; - } else if (oneByte == 239) { // Latch to Text encodation - return TEXT_ENCODE; - } else if (oneByte == 240) { // Latch to EDIFACT encodation - return EDIFACT_ENCODE; - } else if (oneByte == 241) { // ECI Character - // TODO(bbrown): I think we need to support ECI - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte >= 242) { // Not to be used in ASCII encodation - throw ReaderException("Not to be used in ASCII encodation"); - } - } while (bits->available() > 0); - return ASCII_ENCODE; - } - - void DecodedBitStreamParser::decodeC40Segment(Ref bits, ostringstream & result) { - // Three C40 values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time - bool upperShift = false; - - int* cValues = new int[3]; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits->available() == 8) { - return; - } - int firstByte = bits->readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } - - parseTwoBytes(firstByte, bits->readBits(8), cValues); - - int shift = 0; - for (int i = 0; i < 3; i++) { - int cValue = cValues[i]; - switch (shift) { - case 0: - if (cValue < 3) { - shift = cValue + 1; - } else { - if (upperShift) { - result << (char) (C40_BASIC_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << C40_BASIC_SET_CHARS[cValue]; - } - } - break; - case 1: - if (upperShift) { - result << cValue + 128; - upperShift = false; - } else { - result << cValue; - } - shift = 0; - break; - case 2: - if (cValue < 27) { - if (upperShift) { - result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << C40_SHIFT2_SET_CHARS[cValue]; - } - } else if (cValue == 27) { // FNC1 - throw ReaderException("FNC1"); - } else if (cValue == 30) { // Upper Shift - upperShift = true; - } else { - throw ReaderException("Upper Shift"); - } - shift = 0; - break; - case 3: - if (upperShift) { - result << (char) (cValue + 224); - upperShift = false; - } else { - result << (char) (cValue + 96); - } - shift = 0; - break; - default: - throw ReaderException(""); - } - } - } while (bits->available() > 0); - } - - void DecodedBitStreamParser::decodeTextSegment(Ref bits, ostringstream & result) { - // Three Text values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time - bool upperShift = false; - - int* cValues = new int[3]; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits->available() == 8) { - return; - } - int firstByte = bits->readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } - - parseTwoBytes(firstByte, bits->readBits(8), cValues); - - int shift = 0; - for (int i = 0; i < 3; i++) { - int cValue = cValues[i]; - switch (shift) { - case 0: - if (cValue < 3) { - shift = cValue + 1; - } else { - if (upperShift) { - result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << (TEXT_BASIC_SET_CHARS[cValue]); - } - } - break; - case 1: - if (upperShift) { - result << (char) (cValue + 128); - upperShift = false; - } else { - result << (cValue); - } - shift = 0; - break; - case 2: - // Shift 2 for Text is the same encoding as C40 - if (cValue < 27) { - if (upperShift) { - result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << (C40_SHIFT2_SET_CHARS[cValue]); - } - } else if (cValue == 27) { // FNC1 - throw ReaderException("FNC1"); - } else if (cValue == 30) { // Upper Shift - upperShift = true; - } else { - throw ReaderException("Upper Shift"); - } - shift = 0; - break; - case 3: - if (upperShift) { - result << (char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << (TEXT_SHIFT3_SET_CHARS[cValue]); - } - shift = 0; - break; - default: - throw ReaderException(""); - } - } - } while (bits->available() > 0); - } - - void DecodedBitStreamParser::decodeAnsiX12Segment(Ref bits, ostringstream & result) { - // Three ANSI X12 values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - - int* cValues = new int[3]; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits->available() == 8) { - return; - } - int firstByte = bits->readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } - - parseTwoBytes(firstByte, bits->readBits(8), cValues); - - for (int i = 0; i < 3; i++) { - int cValue = cValues[i]; - if (cValue == 0) { // X12 segment terminator - result << '\r'; - } else if (cValue == 1) { // X12 segment separator * - result << '*'; - } else if (cValue == 2) { // X12 sub-element separator > - result << '>'; - } else if (cValue == 3) { // space - result << ' '; - } else if (cValue < 14) { // 0 - 9 - result << (char) (cValue + 44); - } else if (cValue < 40) { // A - Z - result << (char) (cValue + 51); - } else { - throw ReaderException(""); - } - } - } while (bits->available() > 0); - } - - void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) { - int fullBitValue = (firstByte << 8) + secondByte - 1; - int temp = fullBitValue / 1600; - result[0] = temp; - fullBitValue -= temp * 1600; - temp = fullBitValue / 40; - result[1] = temp; - result[2] = fullBitValue - temp * 40; - } - - void DecodedBitStreamParser::decodeEdifactSegment(Ref bits, ostringstream & result) { - bool unlatch = false; - do { - // If there is only two or less bytes left then it will be encoded as ASCII - if (bits->available() <= 16) { - return; - } - - for (int i = 0; i < 4; i++) { - int edifactValue = bits->readBits(6); - - // Check for the unlatch character - if (edifactValue == 0x2B67) { // 011111 - unlatch = true; - // If we encounter the unlatch code then continue reading because the Codeword triple - // is padded with 0's - } - - if (!unlatch) { - if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit - edifactValue |= 64; // Add a leading 01 to the 6 bit binary value - } - result << (edifactValue); - } - } - } while (!unlatch && bits->available() > 0); - } - - void DecodedBitStreamParser::decodeBase256Segment(Ref bits, ostringstream & result){//, vector byteSegments) - // Figure out how long the Base 256 Segment is. - int d1 = bits->readBits(8); - int count; - if (d1 == 0) { // Read the remainder of the symbol - count = bits->available() / 8; - } else if (d1 < 250) { - count = d1; - } else { - count = 250 * (d1 - 249) + bits->readBits(8); - } - unsigned char* bytes = new unsigned char[count]; - for (int i = 0; i < count; i++) { - bytes[i] = unrandomize255State(bits->readBits(8), i); - } - //byteSegments.push_back(bytes); - result << bytes; - } +namespace datamatrix { + +using namespace std; + +const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = { + '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +}; + +const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = { + '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', + '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_' +}; + +const char DecodedBitStreamParser::TEXT_BASIC_SET_CHARS[] = { + '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' +}; + +const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = { + '\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127 +}; + +Ref DecodedBitStreamParser::decode(ArrayRef bytes) { + Ref bits(new BitSource(bytes)); + ostringstream result; + ostringstream resultTrailer; + vector byteSegments; + int mode = ASCII_ENCODE; + do { + if (mode == ASCII_ENCODE) { + mode = decodeAsciiSegment(bits, result, resultTrailer); + } else { + switch (mode) { + case C40_ENCODE: + decodeC40Segment(bits, result); + break; + case TEXT_ENCODE: + decodeTextSegment(bits, result); + break; + case ANSIX12_ENCODE: + decodeAnsiX12Segment(bits, result); + break; + case EDIFACT_ENCODE: + decodeEdifactSegment(bits, result); + break; + case BASE256_ENCODE: + decodeBase256Segment(bits, result, byteSegments); + break; + default: + throw FormatException("Unsupported mode indicator"); + } + mode = ASCII_ENCODE; } + } while (mode != PAD_ENCODE && bits->available() > 0); + + if (resultTrailer.str().size() > 0) { + result << resultTrailer.str(); + } + ArrayRef rawBytes(bytes); + Ref text(new String(result.str())); + return Ref(new DecoderResult(rawBytes, text)); +} + +int DecodedBitStreamParser::decodeAsciiSegment(Ref bits, ostringstream & result, + ostringstream & resultTrailer) { + bool upperShift = false; + do { + int oneByte = bits->readBits(8); + if (oneByte == 0) { + throw FormatException("Not enough bits to decode"); + } else if (oneByte <= 128) { // ASCII data (ASCII value + 1) + oneByte = upperShift ? (oneByte + 128) : oneByte; + // upperShift = false; + result << (char) (oneByte - 1); + return ASCII_ENCODE; + } else if (oneByte == 129) { // Pad + return PAD_ENCODE; + } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) + int value = oneByte - 130; + if (value < 10) { // padd with '0' for single digit values + result << '0'; + } + result << value; + } else if (oneByte == 230) { // Latch to C40 encodation + return C40_ENCODE; + } else if (oneByte == 231) { // Latch to Base 256 encodation + return BASE256_ENCODE; + } else if (oneByte == 232) { // FNC1 + result << ((char) 29); // translate as ASCII 29 + } else if (oneByte == 233 || oneByte == 234) { + // Structured Append, Reader Programming + // Ignore these symbols for now + // throw FormatException.getInstance(); + } else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII) + upperShift = true; + } else if (oneByte == 236) { // 05 Macro + result << ("[)>RS05GS"); + resultTrailer << ("RSEOT"); + } else if (oneByte == 237) { // 06 Macro + result << ("[)>RS06GS"); + resultTrailer << ("RSEOT"); + } else if (oneByte == 238) { // Latch to ANSI X12 encodation + return ANSIX12_ENCODE; + } else if (oneByte == 239) { // Latch to Text encodation + return TEXT_ENCODE; + } else if (oneByte == 240) { // Latch to EDIFACT encodation + return EDIFACT_ENCODE; + } else if (oneByte == 241) { // ECI Character + // TODO(bbrown): I think we need to support ECI + // throw FormatException.getInstance(); + // Ignore this symbol for now + } else if (oneByte >= 242) { // Not to be used in ASCII encodation + // ... but work around encoders that end with 254, latch back to ASCII + if (oneByte == 254 && bits->available() == 0) { + // Ignore + } else { + throw FormatException("Not to be used in ASCII encodation"); + } + } + } while (bits->available() > 0); + return ASCII_ENCODE; +} + +void DecodedBitStreamParser::decodeC40Segment(Ref bits, ostringstream & result) { + // Three C40 values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time + bool upperShift = false; + + int* cValues = new int[3]; + int shift = 0; + do { + // If there is only one byte left then it will be encoded as ASCII + if (bits->available() == 8) { + return; + } + int firstByte = bits->readBits(8); + if (firstByte == 254) { // Unlatch codeword + return; + } + + parseTwoBytes(firstByte, bits->readBits(8), cValues); + + for (int i = 0; i < 3; i++) { + int cValue = cValues[i]; + switch (shift) { + case 0: + if (cValue < 3) { + shift = cValue + 1; + } else { + if (upperShift) { + result << (char) (C40_BASIC_SET_CHARS[cValue] + 128); + upperShift = false; + } else { + result << C40_BASIC_SET_CHARS[cValue]; + } + } + break; + case 1: + if (upperShift) { + result << (char) (cValue + 128); + upperShift = false; + } else { + result << (char) cValue; + } + shift = 0; + break; + case 2: + if (cValue < 27) { + if (upperShift) { + result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); + upperShift = false; + } else { + result << C40_SHIFT2_SET_CHARS[cValue]; + } + } else if (cValue == 27) { // FNC1 + result << ((char) 29); // translate as ASCII 29 + } else if (cValue == 30) { // Upper Shift + upperShift = true; + } else { + throw FormatException("decodeC40Segment: Upper Shift"); + } + shift = 0; + break; + case 3: + if (upperShift) { + result << (char) (cValue + 224); + upperShift = false; + } else { + result << (char) (cValue + 96); + } + shift = 0; + break; + default: + throw FormatException("decodeC40Segment: no case"); + } + } + } while (bits->available() > 0); +} + +void DecodedBitStreamParser::decodeTextSegment(Ref bits, ostringstream & result) { + // Three Text values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time + bool upperShift = false; + + int* cValues = new int[3]; + int shift = 0; + do { + // If there is only one byte left then it will be encoded as ASCII + if (bits->available() == 8) { + return; + } + int firstByte = bits->readBits(8); + if (firstByte == 254) { // Unlatch codeword + return; + } + + parseTwoBytes(firstByte, bits->readBits(8), cValues); + + for (int i = 0; i < 3; i++) { + int cValue = cValues[i]; + switch (shift) { + case 0: + if (cValue < 3) { + shift = cValue + 1; + } else { + if (upperShift) { + result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128); + upperShift = false; + } else { + result << (TEXT_BASIC_SET_CHARS[cValue]); + } + } + break; + case 1: + if (upperShift) { + result << (char) (cValue + 128); + upperShift = false; + } else { + result << (char) (cValue); + } + shift = 0; + break; + case 2: + // Shift 2 for Text is the same encoding as C40 + if (cValue < 27) { + if (upperShift) { + result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); + upperShift = false; + } else { + result << (C40_SHIFT2_SET_CHARS[cValue]); + } + } else if (cValue == 27) { // FNC1 + result << ((char) 29); // translate as ASCII 29 + } else if (cValue == 30) { // Upper Shift + upperShift = true; + } else { + throw FormatException("decodeTextSegment: Upper Shift"); + } + shift = 0; + break; + case 3: + if (upperShift) { + result << (char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128); + upperShift = false; + } else { + result << (TEXT_SHIFT3_SET_CHARS[cValue]); + } + shift = 0; + break; + default: + throw FormatException("decodeTextSegment: no case"); + } + } + } while (bits->available() > 0); +} + +void DecodedBitStreamParser::decodeAnsiX12Segment(Ref bits, ostringstream & result) { + // Three ANSI X12 values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + + int* cValues = new int[3]; + do { + // If there is only one byte left then it will be encoded as ASCII + if (bits->available() == 8) { + return; + } + int firstByte = bits->readBits(8); + if (firstByte == 254) { // Unlatch codeword + return; + } + + parseTwoBytes(firstByte, bits->readBits(8), cValues); + + for (int i = 0; i < 3; i++) { + int cValue = cValues[i]; + if (cValue == 0) { // X12 segment terminator + result << '\r'; + } else if (cValue == 1) { // X12 segment separator * + result << '*'; + } else if (cValue == 2) { // X12 sub-element separator > + result << '>'; + } else if (cValue == 3) { // space + result << ' '; + } else if (cValue < 14) { // 0 - 9 + result << (char) (cValue + 44); + } else if (cValue < 40) { // A - Z + result << (char) (cValue + 51); + } else { + throw FormatException("decodeAnsiX12Segment: no case"); + } + } + } while (bits->available() > 0); +} + +void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) { + int fullBitValue = (firstByte << 8) + secondByte - 1; + int temp = fullBitValue / 1600; + result[0] = temp; + fullBitValue -= temp * 1600; + temp = fullBitValue / 40; + result[1] = temp; + result[2] = fullBitValue - temp * 40; +} + +void DecodedBitStreamParser::decodeEdifactSegment(Ref bits, ostringstream & result) { + bool unlatch = false; + do { + // If there is only two or less bytes left then it will be encoded as ASCII + if (bits->available() <= 16) { + return; + } + + for (int i = 0; i < 4; i++) { + int edifactValue = bits->readBits(6); + + // Check for the unlatch character + if (edifactValue == 0x2B67) { // 011111 + unlatch = true; + // If we encounter the unlatch code then continue reading because the Codeword triple + // is padded with 0's + } + + if (!unlatch) { + if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit + edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value + } + result << (char)(edifactValue); + } + } + } while (!unlatch && bits->available() > 0); +} + +void DecodedBitStreamParser::decodeBase256Segment(Ref bits, ostringstream& result, vector byteSegments) { + // Figure out how long the Base 256 Segment is. + int codewordPosition = 1 + bits->getByteOffset(); // position is 1-indexed + int d1 = unrandomize255State(bits->readBits(8), codewordPosition++); + int count; + if (d1 == 0) { // Read the remainder of the symbol + count = bits->available() / 8; + } else if (d1 < 250) { + count = d1; + } else { + count = 250 * (d1 - 249) + unrandomize255State(bits->readBits(8), codewordPosition++); + } + + // We're seeing NegativeArraySizeException errors from users. + if (count < 0) { + throw FormatException("NegativeArraySizeException"); + } + + unsigned char* bytes = new unsigned char[count]; + for (int i = 0; i < count; i++) { + // Have seen this particular error in the wild, such as at + // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2 + if (bits->available() < 8) { + throw FormatException("byteSegments"); + } + bytes[i] = unrandomize255State(bits->readBits(8), codewordPosition++); + byteSegments.push_back(bytes[i]); + result << (char)bytes[i]; + } +} +} } @@ -3957,72 +5026,70 @@ namespace zxing { // #include namespace zxing { - namespace datamatrix { - - using namespace std; - - Decoder::Decoder() : - rsDecoder_(GF256::DATA_MATRIX_FIELD) { - } - - - void Decoder::correctErrors(ArrayRef codewordBytes, int numDataCodewords) { - int numCodewords = codewordBytes->size(); - ArrayRef codewordInts(numCodewords); - for (int i = 0; i < numCodewords; i++) { - codewordInts[i] = codewordBytes[i] & 0xff; - } - int numECCodewords = numCodewords - numDataCodewords; - - try { - rsDecoder_.decode(codewordInts, numECCodewords); - } catch (ReedSolomonException ex) { - ReaderException rex(ex.what()); - throw rex; - } - - for (int i = 0; i < numDataCodewords; i++) { - codewordBytes[i] = (unsigned char)codewordInts[i]; - } - } - - Ref Decoder::decode(Ref bits) { - // Construct a parser and read version, error-correction level - BitMatrixParser parser(bits); - Version *version = parser.readVersion(bits); - - // Read codewords - ArrayRef codewords(parser.readCodewords()); - // Separate into data blocks - std::vector > dataBlocks = DataBlock::getDataBlocks(codewords, version); - - // Count total number of data bytes - int totalBytes = 0; - for (unsigned int i = 0; i < dataBlocks.size(); i++) { - totalBytes += dataBlocks[i]->getNumDataCodewords(); - } - ArrayRef resultBytes(totalBytes); - int resultOffset = 0; - - // Error-correct and copy data blocks together into a stream of bytes - for (unsigned int j = 0; j < dataBlocks.size(); j++) { - Ref dataBlock(dataBlocks[j]); - ArrayRef codewordBytes = dataBlock->getCodewords(); - int numDataCodewords = dataBlock->getNumDataCodewords(); - correctErrors(codewordBytes, numDataCodewords); - for (int i = 0; i < numDataCodewords; i++) { - resultBytes[resultOffset++] = codewordBytes[i]; - } - } - - // Decode the contents of that stream of bytes - DecodedBitStreamParser decodedBSParser; - Ref text(new String(decodedBSParser.decode(resultBytes))); - - Ref result(new DecoderResult(resultBytes, text)); - return result; - } +namespace datamatrix { + +using namespace std; + +Decoder::Decoder() : + rsDecoder_(GF256::DATA_MATRIX_FIELD) { +} + + +void Decoder::correctErrors(ArrayRef codewordBytes, int numDataCodewords) { + int numCodewords = codewordBytes->size(); + ArrayRef codewordInts(numCodewords); + for (int i = 0; i < numCodewords; i++) { + codewordInts[i] = codewordBytes[i] & 0xff; + } + int numECCodewords = numCodewords - numDataCodewords; + try { + rsDecoder_.decode(codewordInts, numECCodewords); + } catch (ReedSolomonException const& ex) { + ReaderException rex(ex.what()); + throw rex; + } + // Copy back into array of bytes -- only need to worry about the bytes that were data + // We don't care about errors in the error-correction codewords + for (int i = 0; i < numDataCodewords; i++) { + codewordBytes[i] = (unsigned char)codewordInts[i]; + } +} + +Ref Decoder::decode(Ref bits) { + // Construct a parser and read version, error-correction level + BitMatrixParser parser(bits); + Version *version = parser.readVersion(bits); + + // Read codewords + ArrayRef codewords(parser.readCodewords()); + // Separate into data blocks + std::vector > dataBlocks = DataBlock::getDataBlocks(codewords, version); + + int dataBlocksCount = dataBlocks.size(); + + // Count total number of data bytes + int totalBytes = 0; + for (int i = 0; i < dataBlocksCount; i++) { + totalBytes += dataBlocks[i]->getNumDataCodewords(); + } + ArrayRef resultBytes(totalBytes); + + // Error-correct and copy data blocks together into a stream of bytes + for (int j = 0; j < dataBlocksCount; j++) { + Ref dataBlock(dataBlocks[j]); + ArrayRef codewordBytes = dataBlock->getCodewords(); + int numDataCodewords = dataBlock->getNumDataCodewords(); + correctErrors(codewordBytes, numDataCodewords); + for (int i = 0; i < numDataCodewords; i++) { + // De-interlace data blocks. + resultBytes[i * dataBlocksCount + j] = codewordBytes[i]; } + } + // Decode the contents of that stream of bytes + DecodedBitStreamParser decodedBSParser; + return Ref (decodedBSParser.decode(resultBytes)); +} +} } // file: zxing/datamatrix/detector/CornerPoint.cpp @@ -4052,38 +5119,31 @@ namespace zxing { namespace zxing { namespace datamatrix { - + using namespace std; - + CornerPoint::CornerPoint(float posX, float posY) : - posX_(posX), posY_(posY), counter_(0) { + ResultPoint(posX,posY), counter_(0) { } - - float CornerPoint::getX() const { - return posX_; - } - - float CornerPoint::getY() const { - return posY_; - } - + int CornerPoint::getCount() const { return counter_; } - + void CornerPoint::incrementCount() { counter_++; } - + bool CornerPoint::equals(Ref other) const { return posX_ == other->getX() && posY_ == other->getY(); } - + } } // file: zxing/datamatrix/detector/Detector.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * Detector.cpp * zxing @@ -4104,6 +5164,7 @@ namespace zxing { * limitations under the License. */ +// #include // #include // #include // #include @@ -4111,295 +5172,438 @@ namespace zxing { // #include namespace zxing { - namespace datamatrix { - - using namespace std; - - ResultPointsAndTransitions::ResultPointsAndTransitions() : to_(), from_(), transitions_(0) { - Ref ref(new CornerPoint(0,0)); - from_ = ref; - to_ = ref; - } - - ResultPointsAndTransitions::ResultPointsAndTransitions(Ref from, Ref to, int transitions) : - to_(to), from_(from), transitions_(transitions) { - } - - Ref ResultPointsAndTransitions::getFrom() { - return from_; - } - - Ref ResultPointsAndTransitions::getTo() { - return to_; - } - - int ResultPointsAndTransitions::getTransitions() { - return transitions_; - } - - Detector::Detector(Ref image) : image_(image) { } - - Ref Detector::getImage() { - return image_; - } - - Ref Detector::detect() { - Ref rectangleDetector_(new MonochromeRectangleDetector(image_)); - std::vector > cornerPoints = rectangleDetector_->detect(); - Ref pointA = cornerPoints[0]; - Ref pointB = cornerPoints[1]; - Ref pointC = cornerPoints[2]; - Ref pointD = cornerPoints[3]; - - // Point A and D are across the diagonal from one another, - // as are B and C. Figure out which are the solid black lines - // by counting transitions - std::vector > transitions(4); - transitions[0].reset(transitionsBetween(pointA, pointB)); - transitions[1].reset(transitionsBetween(pointA, pointC)); - transitions[2].reset(transitionsBetween(pointB, pointD)); - transitions[3].reset(transitionsBetween(pointC, pointD)); - insertionSort(transitions); - - // Sort by number of transitions. First two will be the two solid sides; last two - // will be the two alternating black/white sides - Ref lSideOne(transitions[0]); - Ref lSideTwo(transitions[1]); - - // Figure out which point is their intersection by tallying up the number of times we see the - // endpoints in the four endpoints. One will show up twice. - Ref maybeTopLeft; - Ref bottomLeft; - Ref maybeBottomRight; - if (lSideOne->getFrom()->equals(lSideOne->getTo())) { - bottomLeft = lSideOne->getFrom(); - maybeTopLeft = lSideTwo->getFrom(); - maybeBottomRight = lSideTwo->getTo(); - } - else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) { - bottomLeft = lSideOne->getFrom(); - maybeTopLeft = lSideOne->getTo(); - maybeBottomRight = lSideTwo->getTo(); - } - else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) { - bottomLeft = lSideOne->getFrom(); - maybeTopLeft = lSideOne->getTo(); - maybeBottomRight = lSideTwo->getFrom(); - } - else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) { - bottomLeft = lSideOne->getTo(); - maybeTopLeft = lSideOne->getFrom(); - maybeBottomRight = lSideTwo->getTo(); - } - else if (lSideOne->getTo()->equals(lSideTwo->getTo())) { - bottomLeft = lSideOne->getTo(); - maybeTopLeft = lSideOne->getFrom(); - maybeBottomRight = lSideTwo->getFrom(); - } - else { - bottomLeft = lSideTwo->getFrom(); - maybeTopLeft = lSideOne->getTo(); - maybeBottomRight = lSideOne->getFrom(); - } - - // Bottom left is correct but top left and bottom right might be switched - std::vector > corners(3); - corners[0].reset(maybeTopLeft); - corners[1].reset(bottomLeft); - corners[2].reset(maybeBottomRight); - // Use the dot product trick to sort them out - orderBestPatterns(corners); - - // Now we know which is which: - Ref bottomRight(corners[0]); - bottomLeft = corners[1]; - Ref topLeft(corners[2]); - - // Which point didn't we find in relation to the "L" sides? that's the top right corner - Ref topRight; - if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) { - topRight = pointA; - } else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft) || pointB->equals(topLeft))) { - topRight = pointB; - } else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft) || pointC->equals(topLeft))) { - topRight = pointC; - } else { - topRight = pointD; - } - - float topRightX = (bottomRight->getX() - bottomLeft->getX()) + topLeft->getX(); - float topRightY = (bottomRight->getY() - bottomLeft->getY()) + topLeft->getY(); - Ref topR(new CornerPoint(topRightX,topRightY)); - - // Next determine the dimension by tracing along the top or right side and counting black/white - // transitions. Since we start inside a black module, we should see a number of transitions - // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to - // end on a black module: - // The top right point is actually the corner of a module, which is one of the two black modules - // adjacent to the white module at the top right. Tracing to that corner from either the top left - // or bottom right should work here. The number of transitions could be higher than it should be - // due to noise. So we try both and take the min. - int dimension = min(transitionsBetween(topLeft, topRight)->getTransitions(), - transitionsBetween(bottomRight, topRight)->getTransitions()); - if ((dimension & 0x01) == 1) { - // it can't be odd, so, round... up? - dimension++; - } - dimension += 2; - - Ref transform = createTransform(topLeft, topR, bottomLeft, bottomRight, dimension); - Ref bits(sampleGrid(image_, dimension, transform)); - std::vector > points(4); - points[0].reset(pointA); - points[1].reset(pointB); - points[2].reset(pointC); - points[3].reset(pointD); - Ref detectorResult(new DetectorResult(bits, points, transform)); - return detectorResult; - } - - Ref Detector::transitionsBetween(Ref from, Ref to) { - // See QR Code Detector, sizeOfBlackWhiteBlackRun() - int fromX = (int) from->getX(); - int fromY = (int) from->getY(); - int toX = (int) to->getX(); - int toY = (int) to->getY(); - bool steep = abs(toY - fromY) > abs(toX - fromX); - if (steep) { - int temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - - int dx = abs(toX - fromX); - int dy = abs(toY - fromY); - int error = -dx >> 1; - int ystep = fromY < toY ? 1 : -1; - int xstep = fromX < toX ? 1 : -1; - int transitions = 0; - bool inBlack = image_->get(steep ? fromY : fromX, steep ? fromX : fromY); - for (int x = fromX, y = fromY; x != toX; x += xstep) { - bool isBlack = image_->get(steep ? y : x, steep ? x : y); - if (isBlack != inBlack) { - transitions++; - inBlack = isBlack; - } - error += dy; - if (error > 0) { - if (y == toY) { - break; - } - y += ystep; - error -= dx; - } - } - Ref result(new ResultPointsAndTransitions(from, to, transitions)); - return result; - } - - Ref Detector::createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref bottomRight, int dimension) { - - Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral( - 0.0f, - 0.0f, - dimension, - 0.0f, - dimension, - dimension, - 0.0f, - dimension, - topLeft->getX(), - topLeft->getY(), - topRight->getX(), - topRight->getY(), - bottomRight->getX(), - bottomRight->getY(), - bottomLeft->getX(), - bottomLeft->getY())); - return transform; - } - - Ref Detector::sampleGrid(Ref image, int dimension, Ref transform) { - GridSampler &sampler = GridSampler::getInstance(); - return sampler.sampleGrid(image, dimension, transform); - } - - void Detector::insertionSort(std::vector > &vector) { - int max = vector.size(); - bool swapped = true; - Ref value; - Ref valueB; - do { - swapped = false; - for (int i = 1; i < max; i++) { - value = vector[i-1]; - if (compare(value, (valueB = vector[i])) > 0) { - swapped = true; - vector[i-1].reset(valueB); - vector[i].reset(value); - } - } - } while (swapped); - } - void Detector::orderBestPatterns(std::vector > &patterns) { - // Find distances between pattern centers - float zeroOneDistance = distance(patterns[0]->getX(), patterns[1]->getX(),patterns[0]->getY(), patterns[1]->getY()); - float oneTwoDistance = distance(patterns[1]->getX(), patterns[2]->getX(),patterns[1]->getY(), patterns[2]->getY()); - float zeroTwoDistance = distance(patterns[0]->getX(), patterns[2]->getX(),patterns[0]->getY(), patterns[2]->getY()); - - Ref pointA, pointB, pointC; - // Assume one closest to other two is B; A and C will just be guesses at first - if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) { - pointB = patterns[0]; - pointA = patterns[1]; - pointC = patterns[2]; - } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) { - pointB = patterns[1]; - pointA = patterns[0]; - pointC = patterns[2]; - } else { - pointB = patterns[2]; - pointA = patterns[0]; - pointC = patterns[1]; - } - - // Use cross product to figure out whether A and C are correct or flipped. - // This asks whether BC x BA has a positive z component, which is the arrangement - // we want for A, B, C. If it's negative, then we've got it flipped around and - // should swap A and C. - if (crossProductZ(pointA, pointB, pointC) < 0.0f) { - Ref temp = pointA; - pointA = pointC; - pointC = temp; - } - - patterns[0] = pointA; - patterns[1] = pointB; - patterns[2] = pointC; - } - - float Detector::distance(float x1, float x2, float y1, float y2) { - float xDiff = x1 - x2; - float yDiff = y1 - y2; - return (float) sqrt((double) (xDiff * xDiff + yDiff * yDiff)); - } - - int Detector::compare(Ref a, Ref b) { - return a->getTransitions() - b->getTransitions(); - } - - float Detector::crossProductZ(Ref pointA, Ref pointB, Ref pointC) { - float bX = pointB->getX(); - float bY = pointB->getY(); - return ((pointC->getX() - bX) * (pointA->getY() - bY)) - ((pointC->getY() - bY) * (pointA->getX() - bX)); - } - } +namespace datamatrix { + +using namespace std; + +ResultPointsAndTransitions::ResultPointsAndTransitions() { + Ref ref(new ResultPoint(0, 0)); + from_ = ref; + to_ = ref; + transitions_ = 0; } +ResultPointsAndTransitions::ResultPointsAndTransitions(Ref from, Ref to, + int transitions) + : to_(to), from_(from), transitions_(transitions) { +} + +Ref ResultPointsAndTransitions::getFrom() { + return from_; +} + +Ref ResultPointsAndTransitions::getTo() { + return to_; +} + +int ResultPointsAndTransitions::getTransitions() { + return transitions_; +} + +Detector::Detector(Ref image) + : image_(image) { +} + +Ref Detector::getImage() { + return image_; +} + +Ref Detector::detect() { + Ref rectangleDetector_(new WhiteRectangleDetector(image_)); + std::vector > ResultPoints = rectangleDetector_->detect(); + Ref pointA = ResultPoints[0]; + Ref pointB = ResultPoints[1]; + Ref pointC = ResultPoints[2]; + Ref pointD = ResultPoints[3]; + + // Point A and D are across the diagonal from one another, + // as are B and C. Figure out which are the solid black lines + // by counting transitions + std::vector > transitions(4); + transitions[0].reset(transitionsBetween(pointA, pointB)); + transitions[1].reset(transitionsBetween(pointA, pointC)); + transitions[2].reset(transitionsBetween(pointB, pointD)); + transitions[3].reset(transitionsBetween(pointC, pointD)); + insertionSort(transitions); + + // Sort by number of transitions. First two will be the two solid sides; last two + // will be the two alternating black/white sides + Ref lSideOne(transitions[0]); + Ref lSideTwo(transitions[1]); + + // Figure out which point is their intersection by tallying up the number of times we see the + // endpoints in the four endpoints. One will show up twice. + Ref maybeTopLeft; + Ref bottomLeft; + Ref maybeBottomRight; + if (lSideOne->getFrom()->equals(lSideOne->getTo())) { + bottomLeft = lSideOne->getFrom(); + maybeTopLeft = lSideTwo->getFrom(); + maybeBottomRight = lSideTwo->getTo(); + } else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) { + bottomLeft = lSideOne->getFrom(); + maybeTopLeft = lSideOne->getTo(); + maybeBottomRight = lSideTwo->getTo(); + } else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) { + bottomLeft = lSideOne->getFrom(); + maybeTopLeft = lSideOne->getTo(); + maybeBottomRight = lSideTwo->getFrom(); + } else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) { + bottomLeft = lSideOne->getTo(); + maybeTopLeft = lSideOne->getFrom(); + maybeBottomRight = lSideTwo->getTo(); + } else if (lSideOne->getTo()->equals(lSideTwo->getTo())) { + bottomLeft = lSideOne->getTo(); + maybeTopLeft = lSideOne->getFrom(); + maybeBottomRight = lSideTwo->getFrom(); + } else { + bottomLeft = lSideTwo->getFrom(); + maybeTopLeft = lSideOne->getTo(); + maybeBottomRight = lSideOne->getFrom(); + } + + // Bottom left is correct but top left and bottom right might be switched + std::vector > corners(3); + corners[0].reset(maybeTopLeft); + corners[1].reset(bottomLeft); + corners[2].reset(maybeBottomRight); + + // Use the dot product trick to sort them out + ResultPoint::orderBestPatterns(corners); + + // Now we know which is which: + Ref bottomRight(corners[0]); + bottomLeft = corners[1]; + Ref topLeft(corners[2]); + + // Which point didn't we find in relation to the "L" sides? that's the top right corner + Ref topRight; + if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) { + topRight = pointA; + } else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft) + || pointB->equals(topLeft))) { + topRight = pointB; + } else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft) + || pointC->equals(topLeft))) { + topRight = pointC; + } else { + topRight = pointD; + } + + // Next determine the dimension by tracing along the top or right side and counting black/white + // transitions. Since we start inside a black module, we should see a number of transitions + // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to + // end on a black module: + + // The top right point is actually the corner of a module, which is one of the two black modules + // adjacent to the white module at the top right. Tracing to that corner from either the top left + // or bottom right should work here. + + int dimensionTop = transitionsBetween(topLeft, topRight)->getTransitions(); + int dimensionRight = transitionsBetween(bottomRight, topRight)->getTransitions(); + + //dimensionTop++; + if ((dimensionTop & 0x01) == 1) { + // it can't be odd, so, round... up? + dimensionTop++; + } + dimensionTop += 2; + + //dimensionRight++; + if ((dimensionRight & 0x01) == 1) { + // it can't be odd, so, round... up? + dimensionRight++; + } + dimensionRight += 2; + + Ref bits; + Ref transform; + Ref correctedTopRight; + + + // Rectanguar symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more + // than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as + // rectangular if the bigger side is at least 7/4 times the other: + if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) { + // The matrix is rectangular + correctedTopRight = correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight, + dimensionTop, dimensionRight); + if (correctedTopRight == NULL) { + correctedTopRight = topRight; + } + + dimensionTop = transitionsBetween(topLeft, correctedTopRight)->getTransitions(); + dimensionRight = transitionsBetween(bottomRight, correctedTopRight)->getTransitions(); + + if ((dimensionTop & 0x01) == 1) { + // it can't be odd, so, round... up? + dimensionTop++; + } + + if ((dimensionRight & 0x01) == 1) { + // it can't be odd, so, round... up? + dimensionRight++; + } + + transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight, dimensionTop, + dimensionRight); + bits = sampleGrid(image_, dimensionTop, dimensionRight, transform); + + } else { + // The matrix is square + int dimension = min(dimensionRight, dimensionTop); + + // correct top right point to match the white module + correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension); + if (correctedTopRight == NULL) { + correctedTopRight = topRight; + } + + // Redetermine the dimension using the corrected top right point + int dimensionCorrected = max(transitionsBetween(topLeft, correctedTopRight)->getTransitions(), + transitionsBetween(bottomRight, correctedTopRight)->getTransitions()); + dimensionCorrected++; + if ((dimensionCorrected & 0x01) == 1) { + dimensionCorrected++; + } + + transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight, + dimensionCorrected, dimensionCorrected); + bits = sampleGrid(image_, dimensionCorrected, dimensionCorrected, transform); + } + + std::vector > points(4); + points[0].reset(topLeft); + points[1].reset(bottomLeft); + points[2].reset(correctedTopRight); + points[3].reset(bottomRight); + Ref detectorResult(new DetectorResult(bits, points, transform)); + return detectorResult; +} + +/** + * Calculates the position of the white top right module using the output of the rectangle detector + * for a rectangular matrix + */ +Ref Detector::correctTopRightRectangular(Ref bottomLeft, + Ref bottomRight, Ref topLeft, Ref topRight, + int dimensionTop, int dimensionRight) { + + float corr = distance(bottomLeft, bottomRight) / (float) dimensionTop; + int norm = distance(topLeft, topRight); + float cos = (topRight->getX() - topLeft->getX()) / norm; + float sin = (topRight->getY() - topLeft->getY()) / norm; + + Ref c1( + new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin)); + + corr = distance(bottomLeft, topLeft) / (float) dimensionRight; + norm = distance(bottomRight, topRight); + cos = (topRight->getX() - bottomRight->getX()) / norm; + sin = (topRight->getY() - bottomRight->getY()) / norm; + + Ref c2( + new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin)); + + if (!isValid(c1)) { + if (isValid(c2)) { + return c2; + } + return Ref(NULL); + } + if (!isValid(c2)) { + return c1; + } + + int l1 = abs(dimensionTop - transitionsBetween(topLeft, c1)->getTransitions()) + + abs(dimensionRight - transitionsBetween(bottomRight, c1)->getTransitions()); + int l2 = abs(dimensionTop - transitionsBetween(topLeft, c2)->getTransitions()) + + abs(dimensionRight - transitionsBetween(bottomRight, c2)->getTransitions()); + + return l1 <= l2 ? c1 : c2; +} + +/** + * Calculates the position of the white top right module using the output of the rectangle detector + * for a square matrix + */ +Ref Detector::correctTopRight(Ref bottomLeft, + Ref bottomRight, Ref topLeft, Ref topRight, + int dimension) { + + float corr = distance(bottomLeft, bottomRight) / (float) dimension; + int norm = distance(topLeft, topRight); + float cos = (topRight->getX() - topLeft->getX()) / norm; + float sin = (topRight->getY() - topLeft->getY()) / norm; + + Ref c1( + new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin)); + + corr = distance(bottomLeft, bottomRight) / (float) dimension; + norm = distance(bottomRight, topRight); + cos = (topRight->getX() - bottomRight->getX()) / norm; + sin = (topRight->getY() - bottomRight->getY()) / norm; + + Ref c2( + new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin)); + + if (!isValid(c1)) { + if (isValid(c2)) { + return c2; + } + return Ref(NULL); + } + if (!isValid(c2)) { + return c1; + } + + int l1 = abs( + transitionsBetween(topLeft, c1)->getTransitions() + - transitionsBetween(bottomRight, c1)->getTransitions()); + int l2 = abs( + transitionsBetween(topLeft, c2)->getTransitions() + - transitionsBetween(bottomRight, c2)->getTransitions()); + + return l1 <= l2 ? c1 : c2; +} + +bool Detector::isValid(Ref p) { + return p->getX() >= 0 && p->getX() < image_->getWidth() && p->getY() > 0 + && p->getY() < image_->getHeight(); +} + +// L2 distance +int Detector::distance(Ref a, Ref b) { + return round( + (float) sqrt( + (double) (a->getX() - b->getX()) * (a->getX() - b->getX()) + + (a->getY() - b->getY()) * (a->getY() - b->getY()))); +} + +Ref Detector::transitionsBetween(Ref from, + Ref to) { + // See QR Code Detector, sizeOfBlackWhiteBlackRun() + int fromX = (int) from->getX(); + int fromY = (int) from->getY(); + int toX = (int) to->getX(); + int toY = (int) to->getY(); + bool steep = abs(toY - fromY) > abs(toX - fromX); + if (steep) { + int temp = fromX; + fromX = fromY; + fromY = temp; + temp = toX; + toX = toY; + toY = temp; + } + + int dx = abs(toX - fromX); + int dy = abs(toY - fromY); + int error = -dx >> 1; + int ystep = fromY < toY ? 1 : -1; + int xstep = fromX < toX ? 1 : -1; + int transitions = 0; + bool inBlack = image_->get(steep ? fromY : fromX, steep ? fromX : fromY); + for (int x = fromX, y = fromY; x != toX; x += xstep) { + bool isBlack = image_->get(steep ? y : x, steep ? x : y); + if (isBlack != inBlack) { + transitions++; + inBlack = isBlack; + } + error += dy; + if (error > 0) { + if (y == toY) { + break; + } + y += ystep; + error -= dx; + } + } + Ref result(new ResultPointsAndTransitions(from, to, transitions)); + return result; +} + +Ref Detector::createTransform(Ref topLeft, + Ref topRight, Ref bottomLeft, Ref bottomRight, + int dimensionX, int dimensionY) { + + Ref transform( + PerspectiveTransform::quadrilateralToQuadrilateral( + 0.5f, + 0.5f, + dimensionX - 0.5f, + 0.5f, + dimensionX - 0.5f, + dimensionY - 0.5f, + 0.5f, + dimensionY - 0.5f, + topLeft->getX(), + topLeft->getY(), + topRight->getX(), + topRight->getY(), + bottomRight->getX(), + bottomRight->getY(), + bottomLeft->getX(), + bottomLeft->getY())); + return transform; +} + +Ref Detector::sampleGrid(Ref image, int dimensionX, int dimensionY, + Ref transform) { + GridSampler &sampler = GridSampler::getInstance(); + return sampler.sampleGrid(image, dimensionX, dimensionY, transform); +} + +void Detector::insertionSort(std::vector > &vector) { + int max = vector.size(); + bool swapped = true; + Ref value; + Ref valueB; + do { + swapped = false; + for (int i = 1; i < max; i++) { + value = vector[i - 1]; + if (compare(value, (valueB = vector[i])) > 0){ + swapped = true; + vector[i - 1].reset(valueB); + vector[i].reset(value); + } + } + } while (swapped); +} + +int Detector::compare(Ref a, Ref b) { + return a->getTransitions() - b->getTransitions(); +} +} +} + +// file: zxing/datamatrix/detector/DetectorException.cpp + +/* + * DetectorException.cpp + * + * Created on: Aug 26, 2011 + * Author: luiz + */ + +// #include "DetectorException.h" + +namespace zxing { +namespace datamatrix { + +DetectorException::DetectorException(const char *msg) : + Exception(msg) { + +} + +DetectorException::~DetectorException() throw () { + // TODO Auto-generated destructor stub +} + +} +} /* namespace zxing */ + // file: zxing/datamatrix/detector/MonochromeRectangleDetector.cpp /* @@ -4427,160 +5631,737 @@ namespace zxing { // #include namespace zxing { - namespace datamatrix { - - std::vector > MonochromeRectangleDetector::detect() { - int height = image_->getHeight(); - int width = image_->getWidth(); - int halfHeight = height >> 1; - int halfWidth = width >> 1; - int deltaY = max(1, height / (MAX_MODULES << 3)); - int deltaX = max(1, width / (MAX_MODULES << 3)); - - int top = 0; - int bottom = height; - int left = 0; - int right = width; - Ref pointA(findCornerFromCenter(halfWidth, 0, left, right, - halfHeight, -deltaY, top, bottom, halfWidth >> 1)); - top = (int) pointA->getY() - 1; - Ref pointB(findCornerFromCenter(halfWidth, -deltaX, left, right, - halfHeight, 0, top, bottom, halfHeight >> 1)); - left = (int) pointB->getX() - 1; - Ref pointC(findCornerFromCenter(halfWidth, deltaX, left, right, - halfHeight, 0, top, bottom, halfHeight >> 1)); - right = (int) pointC->getX() + 1; - Ref pointD(findCornerFromCenter(halfWidth, 0, left, right, - halfHeight, deltaY, top, bottom, halfWidth >> 1)); - bottom = (int) pointD->getY() + 1; - - // Go try to find point A again with better information -- might have been off at first. - pointA.reset(findCornerFromCenter(halfWidth, 0, left, right, - halfHeight, -deltaY, top, bottom, halfWidth >> 2)); - std::vector > corners(4); - - corners[0].reset(pointA); - corners[1].reset(pointB); - corners[2].reset(pointC); - corners[3].reset(pointD); - return corners; - } - - Ref MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right, - int centerY, int deltaY, int top, int bottom, int maxWhiteRun) { - Ref lastRange(NULL); - for (int y = centerY, x = centerX; - y < bottom && y >= top && x < right && x >= left; - y += deltaY, x += deltaX) { - Ref range(NULL); - if (deltaX == 0) { - // horizontal slices, up and down - range = blackWhiteRange(y, maxWhiteRun, left, right, true); - } else { - // vertical slices, left and right - range = blackWhiteRange(x, maxWhiteRun, top, bottom, false); - } - if (range == NULL) { - if (lastRange == NULL) { - throw ReaderException("Couldn't find corners (lastRange = NULL) "); - } else { - // lastRange was found - if (deltaX == 0) { - int lastY = y - deltaY; - if (lastRange->start < centerX) { - if (lastRange->end > centerX) { - // straddle, choose one or the other based on direction - Ref result(new CornerPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY)); - return result; - } - Ref result(new CornerPoint(lastRange->start, lastY)); - return result; - } else { - Ref result(new CornerPoint(lastRange->end, lastY)); - return result; - } - } else { - int lastX = x - deltaX; - if (lastRange->start < centerY) { - if (lastRange->end > centerY) { - Ref result(new CornerPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end)); - return result; - } - Ref result(new CornerPoint(lastX, lastRange->start)); - return result; - } else { - Ref result(new CornerPoint(lastX, lastRange->end)); - return result; - } - } - } - } - lastRange = range; - } - throw ReaderException("Couldn't find corners"); - } - - Ref MonochromeRectangleDetector::blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, - bool horizontal) { - - int center = (minDim + maxDim) >> 1; - - // Scan left/up first - int start = center; - while (start >= minDim) { - if (horizontal ? image_->get(start, fixedDimension) : image_->get(fixedDimension, start)) { - start--; - } else { - int whiteRunStart = start; - do { - start--; - } while (start >= minDim && !(horizontal ? image_->get(start, fixedDimension) : - image_->get(fixedDimension, start))); - int whiteRunSize = whiteRunStart - start; - if (start < minDim || whiteRunSize > maxWhiteRun) { - start = whiteRunStart; - break; - } - } - } - start++; - - // Then try right/down - int end = center; - while (end < maxDim) { - if (horizontal ? image_->get(end, fixedDimension) : image_->get(fixedDimension, end)) { - end++; - } else { - int whiteRunStart = end; - do { - end++; - } while (end < maxDim && !(horizontal ? image_->get(end, fixedDimension) : - image_->get(fixedDimension, end))); - int whiteRunSize = end - whiteRunStart; - if (end >= maxDim || whiteRunSize > maxWhiteRun) { - end = whiteRunStart; - break; - } - } - } - end--; - Ref result(NULL); - if (end > start) { - result = new TwoInts; - result->start = start; - result->end = end; - } - return result; +namespace datamatrix { + +std::vector > MonochromeRectangleDetector::detect() { + int height = image_->getHeight(); + int width = image_->getWidth(); + int halfHeight = height >> 1; + int halfWidth = width >> 1; + int deltaY = max(1, height / (MAX_MODULES << 3)); + int deltaX = max(1, width / (MAX_MODULES << 3)); + + int top = 0; + int bottom = height; + int left = 0; + int right = width; + Ref pointA(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, -deltaY, top, bottom, halfWidth >> 1)); + top = (int) pointA->getY() - 1; + Ref pointB(findCornerFromCenter(halfWidth, -deltaX, left, right, + halfHeight, 0, top, bottom, halfHeight >> 1)); + left = (int) pointB->getX() - 1; + Ref pointC(findCornerFromCenter(halfWidth, deltaX, left, right, + halfHeight, 0, top, bottom, halfHeight >> 1)); + right = (int) pointC->getX() + 1; + Ref pointD(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, deltaY, top, bottom, halfWidth >> 1)); + bottom = (int) pointD->getY() + 1; + + // Go try to find point A again with better information -- might have been off at first. + pointA.reset(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, -deltaY, top, bottom, halfWidth >> 2)); + std::vector > corners(4); + + corners[0].reset(pointA); + corners[1].reset(pointB); + corners[2].reset(pointC); + corners[3].reset(pointD); + return corners; + } + +Ref MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right, + int centerY, int deltaY, int top, int bottom, int maxWhiteRun) { + Ref lastRange(NULL); + for (int y = centerY, x = centerX; + y < bottom && y >= top && x < right && x >= left; + y += deltaY, x += deltaX) { + Ref range(NULL); + if (deltaX == 0) { + // horizontal slices, up and down + range = blackWhiteRange(y, maxWhiteRun, left, right, true); + } else { + // vertical slices, left and right + range = blackWhiteRange(x, maxWhiteRun, top, bottom, false); + } + if (range == NULL) { + if (lastRange == NULL) { + throw ReaderException("Couldn't find corners (lastRange = NULL) "); + } else { + // lastRange was found + if (deltaX == 0) { + int lastY = y - deltaY; + if (lastRange->start < centerX) { + if (lastRange->end > centerX) { + // straddle, choose one or the other based on direction + Ref result(new CornerPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY)); + return result; + } + Ref result(new CornerPoint(lastRange->start, lastY)); + return result; + } else { + Ref result(new CornerPoint(lastRange->end, lastY)); + return result; + } + } else { + int lastX = x - deltaX; + if (lastRange->start < centerY) { + if (lastRange->end > centerY) { + Ref result(new CornerPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end)); + return result; + } + Ref result(new CornerPoint(lastX, lastRange->start)); + return result; + } else { + Ref result(new CornerPoint(lastX, lastRange->end)); + return result; + } + } } + } + lastRange = range; } + throw ReaderException("Couldn't find corners"); + } + +Ref MonochromeRectangleDetector::blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, + bool horizontal) { + + int center = (minDim + maxDim) >> 1; + + // Scan left/up first + int start = center; + while (start >= minDim) { + if (horizontal ? image_->get(start, fixedDimension) : image_->get(fixedDimension, start)) { + start--; + } else { + int whiteRunStart = start; + do { + start--; + } while (start >= minDim && !(horizontal ? image_->get(start, fixedDimension) : + image_->get(fixedDimension, start))); + int whiteRunSize = whiteRunStart - start; + if (start < minDim || whiteRunSize > maxWhiteRun) { + start = whiteRunStart; + break; + } + } + } + start++; + + // Then try right/down + int end = center; + while (end < maxDim) { + if (horizontal ? image_->get(end, fixedDimension) : image_->get(fixedDimension, end)) { + end++; + } else { + int whiteRunStart = end; + do { + end++; + } while (end < maxDim && !(horizontal ? image_->get(end, fixedDimension) : + image_->get(fixedDimension, end))); + int whiteRunSize = end - whiteRunStart; + if (end >= maxDim || whiteRunSize > maxWhiteRun) { + end = whiteRunStart; + break; + } + } + } + end--; + Ref result(NULL); + if (end > start) { + result = new TwoInts; + result->start = start; + result->end = end; + } + return result; + } } +} + +// file: zxing/multi/ByQuadrantReader.cpp + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include + +namespace zxing { +namespace multi { + +ByQuadrantReader::ByQuadrantReader(Reader& delegate) : delegate_(delegate) {} + +ByQuadrantReader::~ByQuadrantReader(){} + +Ref ByQuadrantReader::decode(Ref image){ + return decode(image, DecodeHints::DEFAULT_HINT); +} + +Ref ByQuadrantReader::decode(Ref image, DecodeHints hints){ + int width = image->getWidth(); + int height = image->getHeight(); + int halfWidth = width / 2; + int halfHeight = height / 2; + Ref topLeft = image->crop(0, 0, halfWidth, halfHeight); + try { + return delegate_.decode(topLeft, hints); + } catch (ReaderException re) { + // continue + } + + Ref topRight = image->crop(halfWidth, 0, halfWidth, halfHeight); + try { + return delegate_.decode(topRight, hints); + } catch (ReaderException re) { + // continue + } + + Ref bottomLeft = image->crop(0, halfHeight, halfWidth, halfHeight); + try { + return delegate_.decode(bottomLeft, hints); + } catch (ReaderException re) { + // continue + } + + Ref bottomRight = image->crop(halfWidth, halfHeight, halfWidth, halfHeight); + try { + return delegate_.decode(bottomRight, hints); + } catch (ReaderException re) { + // continue + } + + int quarterWidth = halfWidth / 2; + int quarterHeight = halfHeight / 2; + Ref center = image->crop(quarterWidth, quarterHeight, halfWidth, halfHeight); + return delegate_.decode(center, hints); +} + +} // End zxing::multi namespace +} // End zxing namespace + +// file: zxing/multi/GenericMultipleBarcodeReader.cpp + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include +// #include + +namespace zxing { +namespace multi { +GenericMultipleBarcodeReader::GenericMultipleBarcodeReader(Reader& delegate) : + delegate_(delegate) +{ +} + +GenericMultipleBarcodeReader::~GenericMultipleBarcodeReader(){} + +std::vector > GenericMultipleBarcodeReader::decodeMultiple( + Ref image, DecodeHints hints) +{ + std::vector > results; + doDecodeMultiple(image, hints, results, 0, 0); + if (results.empty()){ + throw ReaderException("No code detected"); + } + return results; +} + +void GenericMultipleBarcodeReader::doDecodeMultiple(Ref image, + DecodeHints hints, std::vector >& results, int xOffset, int yOffset) +{ + Ref result; + try { + result = delegate_.decode(image, hints); + } catch (ReaderException re) { + return; + } + bool alreadyFound = false; + for (unsigned int i = 0; i < results.size(); i++) { + Ref existingResult = results[i]; + if (existingResult->getText()->getText() == result->getText()->getText()) { + alreadyFound = true; + break; + } + } + if (alreadyFound) { + return; + } + + results.push_back(translateResultPoints(result, xOffset, yOffset)); + const std::vector > resultPoints = result->getResultPoints(); + if (resultPoints.empty()) { + return; + } + + int width = image->getWidth(); + int height = image->getHeight(); + float minX = width; + float minY = height; + float maxX = 0.0f; + float maxY = 0.0f; + for (unsigned int i = 0; i < resultPoints.size(); i++) { + Ref point = resultPoints[i]; + float x = point->getX(); + float y = point->getY(); + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + + // Decode left of barcode + if (minX > MIN_DIMENSION_TO_RECUR) { + doDecodeMultiple(image->crop(0, 0, (int) minX, height), + hints, results, xOffset, yOffset); + } + // Decode above barcode + if (minY > MIN_DIMENSION_TO_RECUR) { + doDecodeMultiple(image->crop(0, 0, width, (int) minY), + hints, results, xOffset, yOffset); + } + // Decode right of barcode + if (maxX < width - MIN_DIMENSION_TO_RECUR) { + doDecodeMultiple(image->crop((int) maxX, 0, width - (int) maxX, height), + hints, results, xOffset + (int) maxX, yOffset); + } + // Decode below barcode + if (maxY < height - MIN_DIMENSION_TO_RECUR) { + doDecodeMultiple(image->crop(0, (int) maxY, width, height - (int) maxY), + hints, results, xOffset, yOffset + (int) maxY); + } +} + +Ref GenericMultipleBarcodeReader::translateResultPoints(Ref result, int xOffset, int yOffset){ + const std::vector > oldResultPoints = result->getResultPoints(); + if (oldResultPoints.empty()) { + return result; + } + std::vector > newResultPoints; + for (unsigned int i = 0; i < oldResultPoints.size(); i++) { + Ref oldPoint = oldResultPoints[i]; + newResultPoints.push_back(Ref(new ResultPoint(oldPoint->getX() + xOffset, oldPoint->getY() + yOffset))); + } + return Ref(new Result(result->getText(), result->getRawBytes(), newResultPoints, result->getBarcodeFormat())); +} + +} // End zxing::multi namespace +} // End zxing namespace + +// file: zxing/multi/MultipleBarcodeReader.cpp + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include + +namespace zxing { +namespace multi { + +MultipleBarcodeReader::~MultipleBarcodeReader() { } + +std::vector > MultipleBarcodeReader::decodeMultiple(Ref image) { + return decodeMultiple(image, DecodeHints::DEFAULT_HINT); +} + +} // End zxing::multi namespace +} // End zxing namespace + +// file: zxing/multi/qrcode/QRCodeMultiReader.cpp + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include +// #include +// #include + +namespace zxing { +namespace multi { +QRCodeMultiReader::QRCodeMultiReader(){} + +QRCodeMultiReader::~QRCodeMultiReader(){} + +std::vector > QRCodeMultiReader::decodeMultiple(Ref image, + DecodeHints hints) +{ + std::vector > results; + MultiDetector detector(image->getBlackMatrix()); + + std::vector > detectorResult = detector.detectMulti(hints); + for (unsigned int i = 0; i < detectorResult.size(); i++) { + try { + Ref decoderResult = getDecoder().decode(detectorResult[i]->getBits()); + std::vector > points = detectorResult[i]->getPoints(); + Ref result = Ref(new Result(decoderResult->getText(), + decoderResult->getRawBytes(), + points, BarcodeFormat_QR_CODE)); + // result->putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult->getByteSegments()); + // result->putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult->getECLevel().toString()); + results.push_back(result); + } catch (ReaderException re) { + // ignore and continue + } + } + if (results.empty()){ + throw ReaderException("No code detected"); + } + return results; +} + +} // End zxing::multi namespace +} // End zxing namespace + +// file: zxing/multi/qrcode/detector/MultiDetector.cpp + +/* + * Copyright 2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include + +namespace zxing { +namespace multi { +using namespace zxing::qrcode; + +MultiDetector::MultiDetector(Ref image) : Detector(image) {} + +MultiDetector::~MultiDetector(){} + +std::vector > MultiDetector::detectMulti(DecodeHints hints){ + Ref image = getImage(); + MultiFinderPatternFinder finder = MultiFinderPatternFinder(image, hints.getResultPointCallback()); + std::vector > info = finder.findMulti(hints); + std::vector > result; + for(unsigned int i = 0; i < info.size(); i++){ + try{ + result.push_back(processFinderPatternInfo(info[i])); + } catch (ReaderException e){ + // ignore + } + } + + return result; +} + +} // End zxing::multi namespace +} // End zxing namespace + +// file: zxing/multi/qrcode/detector/MultiFinderPatternFinder.cpp + +/* + * Copyright 2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include +// #include +// #include +// #include + +namespace zxing{ +namespace multi { +using namespace zxing::qrcode; + +const float MultiFinderPatternFinder::MAX_MODULE_COUNT_PER_EDGE = 180; +const float MultiFinderPatternFinder::MIN_MODULE_COUNT_PER_EDGE = 9; +const float MultiFinderPatternFinder::DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f; +const float MultiFinderPatternFinder::DIFF_MODSIZE_CUTOFF = 0.5f; + +bool compareModuleSize(Ref a, Ref b){ + float value = a->getEstimatedModuleSize() - b->getEstimatedModuleSize(); + return value < 0.0; +} + + +MultiFinderPatternFinder::MultiFinderPatternFinder(Ref image, + Ref resultPointCallback) : + FinderPatternFinder(image, resultPointCallback) +{ +} + +MultiFinderPatternFinder::~MultiFinderPatternFinder(){} + +std::vector > MultiFinderPatternFinder::findMulti(DecodeHints const& hints){ + bool tryHarder = hints.getTryHarder(); + Ref image = image_; // Protected member + int maxI = image->getHeight(); + int maxJ = image->getWidth(); + // We are looking for black/white/black/white/black modules in + // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far + + // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the + // image, and then account for the center being 3 modules in size. This gives the smallest + // number of pixels the center could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3); + if (iSkip < MIN_SKIP || tryHarder) { + iSkip = MIN_SKIP; + } + + int stateCount[5]; + for (int i = iSkip - 1; i < maxI; i += iSkip) { + // Get a row of black/white values + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + int currentState = 0; + for (int j = 0; j < maxJ; j++) { + if (image->get(j, i)) { + // Black pixel + if ((currentState & 1) == 1) { // Counting white pixels + currentState++; + } + stateCount[currentState]++; + } else { // White pixel + if ((currentState & 1) == 0) { // Counting black pixels + if (currentState == 4) { // A winner? + if (foundPatternCross(stateCount)) { // Yes + bool confirmed = handlePossibleCenter(stateCount, i, j); + if (!confirmed) { + do { // Advance to next black pixel + j++; + } while (j < maxJ && !image->get(j, i)); + j--; // back up to that last white pixel + } + // Clear state to start looking again + currentState = 0; + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + } else { // No, shift counts back by two + stateCount[0] = stateCount[2]; + stateCount[1] = stateCount[3]; + stateCount[2] = stateCount[4]; + stateCount[3] = 1; + stateCount[4] = 0; + currentState = 3; + } + } else { + stateCount[++currentState]++; + } + } else { // Counting white pixels + stateCount[currentState]++; + } + } + } // for j=... + + if (foundPatternCross(stateCount)) { + handlePossibleCenter(stateCount, i, maxJ); + } // end if foundPatternCross + } // for i=iSkip-1 ... + std::vector > > patternInfo = selectBestPatterns(); + std::vector > result; + for (unsigned int i = 0; i < patternInfo.size(); i++) { + std::vector > pattern = patternInfo[i]; + FinderPatternFinder::orderBestPatterns(pattern); + result.push_back(Ref(new FinderPatternInfo(pattern))); + } + return result; +} + +std::vector > > MultiFinderPatternFinder::selectBestPatterns(){ + std::vector > possibleCenters = possibleCenters_; + + int size = possibleCenters.size(); + + if (size < 3) { + // Couldn't find enough finder patterns + throw ReaderException("No code detected"); + } + + std::vector > > results; + + /* + * Begin HE modifications to safely detect multiple codes of equal size + */ + if (size == 3) { + results.push_back(possibleCenters_); + return results; + } + + // Sort by estimated module size to speed up the upcoming checks + //TODO do a sort based on module size + std::sort(possibleCenters.begin(), possibleCenters.end(), compareModuleSize); + + /* + * Now lets start: build a list of tuples of three finder locations that + * - feature similar module sizes + * - are placed in a distance so the estimated module count is within the QR specification + * - have similar distance between upper left/right and left top/bottom finder patterns + * - form a triangle with 90° angle (checked by comparing top right/bottom left distance + * with pythagoras) + * + * Note: we allow each point to be used for more than one code region: this might seem + * counterintuitive at first, but the performance penalty is not that big. At this point, + * we cannot make a good quality decision whether the three finders actually represent + * a QR code, or are just by chance layouted so it looks like there might be a QR code there. + * So, if the layout seems right, lets have the decoder try to decode. + */ + + for (int i1 = 0; i1 < (size - 2); i1++) { + Ref p1 = possibleCenters[i1]; + for (int i2 = i1 + 1; i2 < (size - 1); i2++) { + Ref p2 = possibleCenters[i2]; + // Compare the expected module sizes; if they are really off, skip + float vModSize12 = (p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize()) / std::min(p1->getEstimatedModuleSize(), p2->getEstimatedModuleSize()); + float vModSize12A = abs(p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize()); + if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) { + // break, since elements are ordered by the module size deviation there cannot be + // any more interesting elements for the given p1. + break; + } + for (int i3 = i2 + 1; i3 < size; i3++) { + Ref p3 = possibleCenters[i3]; + // Compare the expected module sizes; if they are really off, skip + float vModSize23 = (p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize()) / std::min(p2->getEstimatedModuleSize(), p3->getEstimatedModuleSize()); + float vModSize23A = abs(p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize()); + if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) { + // break, since elements are ordered by the module size deviation there cannot be + // any more interesting elements for the given p1. + break; + } + std::vector > test; + test.push_back(p1); + test.push_back(p2); + test.push_back(p3); + FinderPatternFinder::orderBestPatterns(test); + // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal + Ref info = Ref(new FinderPatternInfo(test)); + float dA = FinderPatternFinder::distance(info->getTopLeft(), info->getBottomLeft()); + float dC = FinderPatternFinder::distance(info->getTopRight(), info->getBottomLeft()); + float dB = FinderPatternFinder::distance(info->getTopLeft(), info->getTopRight()); + // Check the sizes + float estimatedModuleCount = (dA + dB) / (p1->getEstimatedModuleSize() * 2.0f); + if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) { + continue; + } + // Calculate the difference of the edge lengths in percent + float vABBC = abs((dA - dB) / std::min(dA, dB)); + if (vABBC >= 0.1f) { + continue; + } + // Calculate the diagonal length by assuming a 90° angle at topleft + float dCpy = (float) sqrt(dA * dA + dB * dB); + // Compare to the real distance in % + float vPyC = abs((dC - dCpy) / std::min(dC, dCpy)); + if (vPyC >= 0.1f) { + continue; + } + // All tests passed! + results.push_back(test); + } // end iterate p3 + } // end iterate p2 + } // end iterate p1 + if (results.empty()){ + // Nothing found! + throw ReaderException("No code detected"); + } + return results; +} + +} // End zxing::multi namespace +} // End zxing namespace // file: zxing/oned/Code128Reader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * Code128Reader.cpp - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -4606,7 +6387,7 @@ namespace zxing { namespace zxing { namespace oned { - + const int CODE_PATTERNS_LENGTH = 107; const int countersLength = 6; static const int CODE_PATTERNS[CODE_PATTERNS_LENGTH][countersLength] = { @@ -4718,11 +6499,11 @@ namespace zxing { {2, 1, 1, 2, 3, 2}, /* 105 */ {2, 3, 3, 1, 1, 1} }; - - + + Code128Reader::Code128Reader(){ } - + int* Code128Reader::findStartPattern(Ref row){ int width = row->getSize(); int rowOffset = 0; @@ -4732,13 +6513,13 @@ namespace zxing { } rowOffset++; } - + int counterPosition = 0; int counters[countersLength] = {0,0,0,0,0,0}; int patternStart = rowOffset; bool isWhite = false; int patternLength = sizeof(counters) / sizeof(int); - + for (int i = rowOffset; i < width; i++) { bool pixel = row->get(i); if (pixel ^ isWhite) { @@ -4749,7 +6530,7 @@ namespace zxing { int bestMatch = -1; for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) { unsigned int variance = patternMatchVariance(counters, sizeof(counters) / sizeof(int), - CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE); + CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE); if (variance < bestVariance) { bestVariance = variance; bestMatch = startCode; @@ -4757,8 +6538,8 @@ namespace zxing { } if (bestMatch >= 0) { // Look for whitespace before start pattern, >= 50% of width of start pattern - if (row->isRange(fmaxl(0, patternStart - (i - patternStart) / 2), patternStart, - false)) { + if (row->isRange(std::max(0, patternStart - (i - patternStart) / 2), patternStart, + false)) { int* resultValue = new int[3]; resultValue[0] = patternStart; resultValue[1] = i; @@ -4782,23 +6563,23 @@ namespace zxing { } throw ReaderException(""); } - + int Code128Reader::decodeCode(Ref row, int counters[], int countersCount, - int rowOffset) { - if (!recordPattern(row, rowOffset, counters, countersCount)) { - throw ReaderException(""); - } + int rowOffset) { + if (!recordPattern(row, rowOffset, counters, countersCount)) { + throw ReaderException(""); + } unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept int bestMatch = -1; for (int d = 0; d < CODE_PATTERNS_LENGTH; d++) { int pattern[countersLength]; - + for(int ind = 0; ind< countersLength; ind++){ pattern[ind] = CODE_PATTERNS[d][ind]; } - // memcpy(pattern, CODE_PATTERNS[d], countersLength); +// memcpy(pattern, CODE_PATTERNS[d], countersLength); unsigned int variance = patternMatchVariance(counters, countersCount, pattern, - MAX_INDIVIDUAL_VARIANCE); + MAX_INDIVIDUAL_VARIANCE); if (variance < bestVariance) { bestVariance = variance; bestMatch = d; @@ -4811,258 +6592,260 @@ namespace zxing { throw ReaderException(""); } } - + Ref Code128Reader::decodeRow(int rowNumber, Ref row) { - int* startPatternInfo = NULL; - try { - startPatternInfo = findStartPattern(row); - int startCode = startPatternInfo[2]; - int codeSet; - switch (startCode) { - case CODE_START_A: - codeSet = CODE_CODE_A; - break; - case CODE_START_B: - codeSet = CODE_CODE_B; - break; - case CODE_START_C: - codeSet = CODE_CODE_C; - break; - default: - throw ReaderException(""); + int* startPatternInfo = NULL; + try { + startPatternInfo = findStartPattern(row); + int startCode = startPatternInfo[2]; + int codeSet; + switch (startCode) { + case CODE_START_A: + codeSet = CODE_CODE_A; + break; + case CODE_START_B: + codeSet = CODE_CODE_B; + break; + case CODE_START_C: + codeSet = CODE_CODE_C; + break; + default: + throw ReaderException(""); + } + + bool done = false; + bool isNextShifted = false; + + std::string tmpResultString; + std::stringstream tmpResultSStr; // used if its Code 128C + + int lastStart = startPatternInfo[0]; + int nextStart = startPatternInfo[1]; + int counters[countersLength] = {0,0,0,0,0,0}; + + int lastCode = 0; + int code = 0; + int checksumTotal = startCode; + int multiplier = 0; + bool lastCharacterWasPrintable = true; + + while (!done) { + bool unshift = isNextShifted; + isNextShifted = false; + + // Save off last code + lastCode = code; + + // Decode another code from image + try { + code = decodeCode(row, counters, sizeof(counters)/sizeof(int), nextStart); + } catch (ReaderException const& re) { + throw re; + } + + // Remember whether the last code was printable or not (excluding CODE_STOP) + if (code != CODE_STOP) { + lastCharacterWasPrintable = true; + } + + // Add to checksum computation (if not CODE_STOP of course) + if (code != CODE_STOP) { + multiplier++; + checksumTotal += multiplier * code; + } + + // Advance to where the next code will to start + lastStart = nextStart; + int _countersLength = sizeof(counters) / sizeof(int); + for (int i = 0; i < _countersLength; i++) { + nextStart += counters[i]; + } + + // Take care of illegal start codes + switch (code) { + case CODE_START_A: + case CODE_START_B: + case CODE_START_C: + throw ReaderException(""); + } + + switch (codeSet) { + + case CODE_CODE_A: + if (code < 64) { + tmpResultString.append(1, (char) (' ' + code)); + } else if (code < 96) { + tmpResultString.append(1, (char) (code - 64)); + } else { + // Don't let CODE_STOP, which always appears, affect whether whether we think the + // last code was printable or not. + if (code != CODE_STOP) { + lastCharacterWasPrintable = false; } - - bool done = false; - bool isNextShifted = false; - - std::string tmpResultString; - std::stringstream tmpResultSStr; // used if its Code 128C - - int lastStart = startPatternInfo[0]; - int nextStart = startPatternInfo[1]; - int counters[countersLength] = {0,0,0,0,0,0}; - - int lastCode = 0; - int code = 0; - int checksumTotal = startCode; - int multiplier = 0; - bool lastCharacterWasPrintable = true; - - while (!done) { - bool unshift = isNextShifted; - isNextShifted = false; - - // Save off last code - lastCode = code; - - // Decode another code from image - try { - code = decodeCode(row, counters, sizeof(counters)/sizeof(int), nextStart); - } catch (ReaderException re) { - throw re; - } - - // Remember whether the last code was printable or not (excluding CODE_STOP) - if (code != CODE_STOP) { - lastCharacterWasPrintable = true; - } - - // Add to checksum computation (if not CODE_STOP of course) - if (code != CODE_STOP) { - multiplier++; - checksumTotal += multiplier * code; - } - - // Advance to where the next code will to start - lastStart = nextStart; - int _countersLength = sizeof(counters) / sizeof(int); - for (int i = 0; i < _countersLength; i++) { - nextStart += counters[i]; - } - - // Take care of illegal start codes - switch (code) { - case CODE_START_A: - case CODE_START_B: - case CODE_START_C: - throw ReaderException(""); - } - - switch (codeSet) { - - case CODE_CODE_A: - if (code < 64) { - tmpResultString.append(1, (char) (' ' + code)); - } else if (code < 96) { - tmpResultString.append(1, (char) (code - 64)); - } else { - // Don't let CODE_STOP, which always appears, affect whether whether we think the - // last code was printable or not. - if (code != CODE_STOP) { - lastCharacterWasPrintable = false; - } - switch (code) { - case CODE_FNC_1: - case CODE_FNC_2: - case CODE_FNC_3: - case CODE_FNC_4_A: - // do nothing? - break; - case CODE_SHIFT: - isNextShifted = true; - codeSet = CODE_CODE_B; - break; - case CODE_CODE_B: - codeSet = CODE_CODE_B; - break; - case CODE_CODE_C: - codeSet = CODE_CODE_C; - break; - case CODE_STOP: - done = true; - break; - } - } - break; - case CODE_CODE_B: - if (code < 96) { - tmpResultString.append(1, (char) (' ' + code)); - } else { - if (code != CODE_STOP) { - lastCharacterWasPrintable = false; - } - switch (code) { - case CODE_FNC_1: - case CODE_FNC_2: - case CODE_FNC_3: - case CODE_FNC_4_B: - // do nothing? - break; - case CODE_SHIFT: - isNextShifted = true; - codeSet = CODE_CODE_C; - break; - case CODE_CODE_A: - codeSet = CODE_CODE_A; - break; - case CODE_CODE_C: - codeSet = CODE_CODE_C; - break; - case CODE_STOP: - done = true; - break; - } - } - break; - case CODE_CODE_C: - // the code read in this case is the number encoded directly - if (code < 100) { - if (code < 10) - tmpResultSStr << '0'; - tmpResultSStr << code; - } else { - if (code != CODE_STOP) { - lastCharacterWasPrintable = false; - } - switch (code) { - case CODE_FNC_1: - // do nothing? - break; - case CODE_CODE_A: - codeSet = CODE_CODE_A; - break; - case CODE_CODE_B: - codeSet = CODE_CODE_B; - break; - case CODE_STOP: - done = true; - break; - } - } - break; - } - - // Unshift back to another code set if we were shifted - if (unshift) { - switch (codeSet) { - case CODE_CODE_A: - codeSet = CODE_CODE_C; - break; - case CODE_CODE_B: - codeSet = CODE_CODE_A; - break; - case CODE_CODE_C: - codeSet = CODE_CODE_B; - break; - } - } - + switch (code) { + case CODE_FNC_1: + case CODE_FNC_2: + case CODE_FNC_3: + case CODE_FNC_4_A: + // do nothing? + break; + case CODE_SHIFT: + isNextShifted = true; + codeSet = CODE_CODE_B; + break; + case CODE_CODE_B: + codeSet = CODE_CODE_B; + break; + case CODE_CODE_C: + codeSet = CODE_CODE_C; + break; + case CODE_STOP: + done = true; + break; } - - // Check for ample whitespace following pattern, but, to do this we first need to remember that - // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left - // to read off. Would be slightly better to properly read. Here we just skip it: - int width = row->getSize(); - while (nextStart < width && row->get(nextStart)) { - nextStart++; + } + break; + case CODE_CODE_B: + if (code < 96) { + tmpResultString.append(1, (char) (' ' + code)); + } else { + if (code != CODE_STOP) { + lastCharacterWasPrintable = false; } - if (!row->isRange(nextStart, fminl(width, nextStart + (nextStart - lastStart) / 2), false)) { - throw ReaderException(""); + switch (code) { + case CODE_FNC_1: + case CODE_FNC_2: + case CODE_FNC_3: + case CODE_FNC_4_B: + // do nothing? + break; + case CODE_SHIFT: + isNextShifted = true; + codeSet = CODE_CODE_C; + break; + case CODE_CODE_A: + codeSet = CODE_CODE_A; + break; + case CODE_CODE_C: + codeSet = CODE_CODE_C; + break; + case CODE_STOP: + done = true; + break; } - - // Pull out from sum the value of the penultimate check code - checksumTotal -= multiplier * lastCode; - // lastCode is the checksum then: - if (checksumTotal % 103 != lastCode) { - throw ReaderException(""); + } + break; + case CODE_CODE_C: + tmpResultSStr.str(std::string()); + // the code read in this case is the number encoded directly + if (code < 100) { + if (code < 10) { + tmpResultSStr << '0'; + } + tmpResultSStr << code; + tmpResultString.append(tmpResultSStr.str()); + } else { + if (code != CODE_STOP) { + lastCharacterWasPrintable = false; } - - if (codeSet == CODE_CODE_C) - tmpResultString.append(tmpResultSStr.str()); - - // Need to pull out the check digits from string - int resultLength = tmpResultString.length(); - // Only bother if the result had at least one character, and if the checksum digit happened to - // be a printable character. If it was just interpreted as a control code, nothing to remove. - if (resultLength > 0 && lastCharacterWasPrintable) { - if (codeSet == CODE_CODE_C) { - tmpResultString.erase(resultLength - 2, resultLength); - } else { - tmpResultString.erase(resultLength - 1, resultLength); - } + switch (code) { + case CODE_FNC_1: + // do nothing? + break; + case CODE_CODE_A: + codeSet = CODE_CODE_A; + break; + case CODE_CODE_B: + codeSet = CODE_CODE_B; + break; + case CODE_STOP: + done = true; + break; } - - Ref resultString(new String(tmpResultString)); - if (tmpResultString.length() == 0) { - // Almost surely a false positive - throw ReaderException(""); - } - - float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; - float right = (float) (nextStart + lastStart) / 2.0f; - - std::vector< Ref > resultPoints(2); - Ref resultPoint1(new OneDResultPoint(left, (float) rowNumber)); - Ref resultPoint2(new OneDResultPoint(right, (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - delete [] startPatternInfo; - ArrayRef resultBytes(1); - return Ref(new Result(resultString, resultBytes, resultPoints, - BarcodeFormat_CODE_128)); + } + break; + } + + // Unshift back to another code set if we were shifted + if (unshift) { + switch (codeSet) { + case CODE_CODE_A: + codeSet = CODE_CODE_C; + break; + case CODE_CODE_B: + codeSet = CODE_CODE_A; + break; + case CODE_CODE_C: + codeSet = CODE_CODE_B; + break; + } + } + + } + + // Check for ample whitespace following pattern, but, to do this we first need to remember that + // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left + // to read off. Would be slightly better to properly read. Here we just skip it: + int width = row->getSize(); + while (nextStart < width && row->get(nextStart)) { + nextStart++; + } + if (!row->isRange(nextStart, + std::min(width, nextStart + (nextStart - lastStart) / 2), + false)) { + throw ReaderException(""); + } + + // Pull out from sum the value of the penultimate check code + checksumTotal -= multiplier * lastCode; + // lastCode is the checksum then: + if (checksumTotal % 103 != lastCode) { + throw ReaderException(""); + } + + // Need to pull out the check digits from string + int resultLength = tmpResultString.length(); + // Only bother if the result had at least one character, and if the checksum digit happened to + // be a printable character. If it was just interpreted as a control code, nothing to remove. + if (resultLength > 0 && lastCharacterWasPrintable) { + if (codeSet == CODE_CODE_C) { + tmpResultString.erase(resultLength - 2, resultLength); + } else { + tmpResultString.erase(resultLength - 1, resultLength); + } + } + + Ref resultString(new String(tmpResultString)); + if (tmpResultString.length() == 0) { + // Almost surely a false positive + throw ReaderException(""); + } + + float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; + float right = (float) (nextStart + lastStart) / 2.0f; + + std::vector< Ref > resultPoints(2); + Ref resultPoint1(new OneDResultPoint(left, (float) rowNumber)); + Ref resultPoint2(new OneDResultPoint(right, (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + + delete [] startPatternInfo; + ArrayRef resultBytes(1); + return Ref(new Result(resultString, resultBytes, resultPoints, + BarcodeFormat_CODE_128)); } catch (ReaderException const& re) { - delete [] startPatternInfo; - return Ref(); + delete [] startPatternInfo; + return Ref(); } } - + void Code128Reader::append(char* s, char c){ int len = strlen(s); s[len] = c; s[len + 1] = '\0'; } - + Code128Reader::~Code128Reader(){ } } @@ -5070,10 +6853,8 @@ namespace zxing { // file: zxing/oned/Code39Reader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * Code39Reader.cpp - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -5097,338 +6878,334 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - static const char* ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; - - - /** - * These represent the encodings of characters, as patterns of wide and narrow - * bars. - * The 9 least-significant bits of each int correspond to the pattern of wide - * and narrow, with 1s representing "wide" and 0s representing narrow. - */ - const int CHARACTER_ENCODINGS_LEN = 44; - static int CHARACTER_ENCODINGS[CHARACTER_ENCODINGS_LEN] = { - 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9 - 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J - 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T - 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-* - 0x0A8, 0x0A2, 0x08A, 0x02A // $-% - }; - - static int ASTERISK_ENCODING = 0x094; - static const char* ALPHABET_STRING = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; - - - /** - * Creates a reader that assumes all encoded data is data, and does not treat - * the final character as a check digit. It will not decoded "extended - * Code 39" sequences. - */ - Code39Reader::Code39Reader() : alphabet_string(ALPHABET_STRING), - usingCheckDigit(false), - extendedMode(false) { +namespace oned { + + static const char* ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; + + + /** + * These represent the encodings of characters, as patterns of wide and narrow + * bars. + * The 9 least-significant bits of each int correspond to the pattern of wide + * and narrow, with 1s representing "wide" and 0s representing narrow. + */ + const int CHARACTER_ENCODINGS_LEN = 44; + static int CHARACTER_ENCODINGS[CHARACTER_ENCODINGS_LEN] = { + 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9 + 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J + 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T + 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-* + 0x0A8, 0x0A2, 0x08A, 0x02A // $-% + }; + + static int ASTERISK_ENCODING = 0x094; + static const char* ALPHABET_STRING = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; + + + /** + * Creates a reader that assumes all encoded data is data, and does not treat + * the final character as a check digit. It will not decoded "extended + * Code 39" sequences. + */ + Code39Reader::Code39Reader() : alphabet_string(ALPHABET_STRING), + usingCheckDigit(false), + extendedMode(false) { + } + + /** + * Creates a reader that can be configured to check the last character as a + * check digit. It will not decoded "extended Code 39" sequences. + * + * @param usingCheckDigit if true, treat the last data character as a check + * digit, not data, and verify that the checksum passes. + */ + Code39Reader::Code39Reader(bool usingCheckDigit_) : + alphabet_string(ALPHABET_STRING), + usingCheckDigit(usingCheckDigit_), + extendedMode(false) { + } + + + Code39Reader::Code39Reader(bool usingCheckDigit_, bool extendedMode_) : + alphabet_string(ALPHABET_STRING), + usingCheckDigit(usingCheckDigit_), + extendedMode(extendedMode_) { + } + + Ref Code39Reader::decodeRow(int rowNumber, Ref row) { + int* start = NULL; + try { + start = findAsteriskPattern(row); + int nextStart = start[1]; + int end = row->getSize(); + + // Read off white space + while (nextStart < end && !row->get(nextStart)) { + nextStart++; + } + + std::string tmpResultString; + + const int countersLen = 9; + int counters[countersLen]; + for (int i = 0; i < countersLen; i++) { + counters[i] = 0; + } + char decodedChar; + int lastStart; + do { + if (!recordPattern(row, nextStart, counters, countersLen)) { + throw ReaderException(""); } - - /** - * Creates a reader that can be configured to check the last character as a - * check digit. It will not decoded "extended Code 39" sequences. - * - * @param usingCheckDigit if true, treat the last data character as a check - * digit, not data, and verify that the checksum passes. - */ - Code39Reader::Code39Reader(bool usingCheckDigit_) : - alphabet_string(ALPHABET_STRING), - usingCheckDigit(usingCheckDigit_), - extendedMode(false) { + int pattern = toNarrowWidePattern(counters, countersLen); + if (pattern < 0) { + throw ReaderException("pattern < 0"); } - - - Code39Reader::Code39Reader(bool usingCheckDigit_, bool extendedMode_) : - alphabet_string(ALPHABET_STRING), - usingCheckDigit(usingCheckDigit_), - extendedMode(extendedMode_) { + decodedChar = patternToChar(pattern); + tmpResultString.append(1, decodedChar); + lastStart = nextStart; + for (int i = 0; i < countersLen; i++) { + nextStart += counters[i]; } - - Ref Code39Reader::decodeRow(int rowNumber, Ref row) { - int* start = NULL; - try { - start = findAsteriskPattern(row); - int nextStart = start[1]; - int end = row->getSize(); - - // Read off white space - while (nextStart < end && !row->get(nextStart)) { - nextStart++; - } - - std::string tmpResultString; - - const int countersLen = 9; - int counters[countersLen]; - for (int i = 0; i < countersLen; i++) { - counters[i] = 0; - } - char decodedChar; - int lastStart; - do { - if (!recordPattern(row, nextStart, counters, countersLen)) { - throw ReaderException(""); - } - int pattern = toNarrowWidePattern(counters, countersLen); - if (pattern < 0) { - throw ReaderException("pattern < 0"); - } - decodedChar = patternToChar(pattern); - tmpResultString.append(1, decodedChar); - lastStart = nextStart; - for (int i = 0; i < countersLen; i++) { - nextStart += counters[i]; - } - // Read off white space - while (nextStart < end && !row->get(nextStart)) { - nextStart++; - } - } while (decodedChar != '*'); - tmpResultString.erase(tmpResultString.length()-1, 1);// remove asterisk - - // Look for whitespace after pattern: - int lastPatternSize = 0; - for (int i = 0; i < countersLen; i++) { - lastPatternSize += counters[i]; - } - int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; - // If 50% of last pattern size, following last pattern, is not whitespace, - // fail (but if it's whitespace to the very end of the image, that's OK) - if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) { - throw ReaderException("too short end white space"); - } - - if (usingCheckDigit) { - int max = tmpResultString.length() - 1; - unsigned int total = 0; - for (int i = 0; i < max; i++) { - total += alphabet_string.find_first_of(tmpResultString[i], 0); - } - if (total % 43 != alphabet_string.find_first_of(tmpResultString[max], 0)) { - throw ReaderException(""); - } - tmpResultString.erase(max, 1); - } - - Ref resultString(new String(tmpResultString)); - if (extendedMode) { - resultString = decodeExtended(tmpResultString); - } - - if (tmpResultString.length() == 0) { - // Almost surely a false positive - throw ReaderException(""); - } - - float left = (float) (start[1] + start[0]) / 2.0f; - float right = (float) (nextStart + lastStart) / 2.0f; - - std::vector< Ref > resultPoints(2); - Ref resultPoint1( - new OneDResultPoint(left, (float) rowNumber)); - Ref resultPoint2( - new OneDResultPoint(right, (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - ArrayRef resultBytes(1); - - Ref res(new Result( - resultString, resultBytes, resultPoints, BarcodeFormat_CODE_39)); - - delete [] start; - return res; - } catch (ReaderException const& re) { - delete [] start; - return Ref(); + // Read off white space + while (nextStart < end && !row->get(nextStart)) { + nextStart++; + } + } while (decodedChar != '*'); + tmpResultString.erase(tmpResultString.length()-1, 1);// remove asterisk + + // Look for whitespace after pattern: + int lastPatternSize = 0; + for (int i = 0; i < countersLen; i++) { + lastPatternSize += counters[i]; + } + int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; + // If 50% of last pattern size, following last pattern, is not whitespace, + // fail (but if it's whitespace to the very end of the image, that's OK) + if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) { + throw ReaderException("too short end white space"); + } + + if (usingCheckDigit) { + int max = tmpResultString.length() - 1; + unsigned int total = 0; + for (int i = 0; i < max; i++) { + total += alphabet_string.find_first_of(tmpResultString[i], 0); + } + if (total % 43 != alphabet_string.find_first_of(tmpResultString[max], 0)) { + throw ReaderException(""); + } + tmpResultString.erase(max, 1); + } + + Ref resultString(new String(tmpResultString)); + if (extendedMode) { + resultString = decodeExtended(tmpResultString); + } + + if (tmpResultString.length() == 0) { + // Almost surely a false positive + throw ReaderException(""); + } + + float left = (float) (start[1] + start[0]) / 2.0f; + float right = (float) (nextStart + lastStart) / 2.0f; + + std::vector< Ref > resultPoints(2); + Ref resultPoint1( + new OneDResultPoint(left, (float) rowNumber)); + Ref resultPoint2( + new OneDResultPoint(right, (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + + ArrayRef resultBytes(1); + + Ref res(new Result( + resultString, resultBytes, resultPoints, BarcodeFormat_CODE_39)); + + delete [] start; + return res; + } catch (ReaderException const& re) { + delete [] start; + return Ref(); + } + } + + int* Code39Reader::findAsteriskPattern(Ref row){ + int width = row->getSize(); + int rowOffset = 0; + while (rowOffset < width) { + if (row->get(rowOffset)) { + break; + } + rowOffset++; + } + + int counterPosition = 0; + const int countersLen = 9; + int counters[countersLen]; + for (int i = 0; i < countersLen; i++) { + counters[i] = 0; + } + int patternStart = rowOffset; + bool isWhite = false; + int patternLength = countersLen; + + for (int i = rowOffset; i < width; i++) { + bool pixel = row->get(i); + if (pixel ^ isWhite) { + counters[counterPosition]++; + } else { + if (counterPosition == patternLength - 1) { + if (toNarrowWidePattern(counters, countersLen) == ASTERISK_ENCODING) { + // Look for whitespace before start pattern, >= 50% of width of + // start pattern. + if (row->isRange(std::max(0, patternStart - ((i - patternStart) >> 1)), patternStart, false)) { + int* resultValue = new int[2]; + resultValue[0] = patternStart; + resultValue[1] = i; + return resultValue; } + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } else { + counterPosition++; } - - int* Code39Reader::findAsteriskPattern(Ref row){ - int width = row->getSize(); - int rowOffset = 0; - while (rowOffset < width) { - if (row->get(rowOffset)) { - break; - } - rowOffset++; - } - - int counterPosition = 0; - const int countersLen = 9; - int counters[countersLen]; - for (int i = 0; i < countersLen; i++) { - counters[i] = 0; - } - int patternStart = rowOffset; - bool isWhite = false; - int patternLength = countersLen; - - for (int i = rowOffset; i < width; i++) { - bool pixel = row->get(i); - if (pixel ^ isWhite) { - counters[counterPosition]++; - } else { - if (counterPosition == patternLength - 1) { - if (toNarrowWidePattern(counters, countersLen) == ASTERISK_ENCODING) { - // Look for whitespace before start pattern, >= 50% of width of - // start pattern. - long double longPatternOffset = - fmaxl(0, patternStart - (i - patternStart) / 2); - if (row->isRange(longPatternOffset, patternStart, false)) { - int* resultValue = new int[2]; - resultValue[0] = patternStart; - resultValue[1] = i; - return resultValue; - } - } - patternStart += counters[0] + counters[1]; - for (int y = 2; y < patternLength; y++) { - counters[y - 2] = counters[y]; - } - counters[patternLength - 2] = 0; - counters[patternLength - 1] = 0; - counterPosition--; - } else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw ReaderException(""); + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException(""); + } + + // For efficiency, returns -1 on failure. Not throwing here saved as many as + // 700 exceptions per image when using some of our blackbox images. + int Code39Reader::toNarrowWidePattern(int counters[], int countersLen){ + int numCounters = countersLen; + int maxNarrowCounter = 0; + int wideCounters; + do { + int minCounter = INT_MAX; + for (int i = 0; i < numCounters; i++) { + int counter = counters[i]; + if (counter < minCounter && counter > maxNarrowCounter) { + minCounter = counter; } - - // For efficiency, returns -1 on failure. Not throwing here saved as many as - // 700 exceptions per image when using some of our blackbox images. - int Code39Reader::toNarrowWidePattern(int counters[], int countersLen){ - int numCounters = countersLen; - int maxNarrowCounter = 0; - int wideCounters; - do { - int minCounter = INT_MAX; - for (int i = 0; i < numCounters; i++) { - int counter = counters[i]; - if (counter < minCounter && counter > maxNarrowCounter) { - minCounter = counter; - } - } - maxNarrowCounter = minCounter; - wideCounters = 0; - int totalWideCountersWidth = 0; - int pattern = 0; - for (int i = 0; i < numCounters; i++) { - int counter = counters[i]; - if (counters[i] > maxNarrowCounter) { - pattern |= 1 << (numCounters - 1 - i); - wideCounters++; - totalWideCountersWidth += counter; - } - } - if (wideCounters == 3) { - // Found 3 wide counters, but are they close enough in width? - // We can perform a cheap, conservative check to see if any individual - // counter is more than 1.5 times the average: - for (int i = 0; i < numCounters && wideCounters > 0; i++) { - int counter = counters[i]; - if (counters[i] > maxNarrowCounter) { - wideCounters--; - // totalWideCountersWidth = 3 * average, so this checks if - // counter >= 3/2 * average. - if ((counter << 1) >= totalWideCountersWidth) { - return -1; - } - } - } - return pattern; - } - } while (wideCounters > 3); - return -1; + } + maxNarrowCounter = minCounter; + wideCounters = 0; + int totalWideCountersWidth = 0; + int pattern = 0; + for (int i = 0; i < numCounters; i++) { + int counter = counters[i]; + if (counters[i] > maxNarrowCounter) { + pattern |= 1 << (numCounters - 1 - i); + wideCounters++; + totalWideCountersWidth += counter; } - - char Code39Reader::patternToChar(int pattern){ - for (int i = 0; i < CHARACTER_ENCODINGS_LEN; i++) { - if (CHARACTER_ENCODINGS[i] == pattern) { - return ALPHABET[i]; - } + } + if (wideCounters == 3) { + // Found 3 wide counters, but are they close enough in width? + // We can perform a cheap, conservative check to see if any individual + // counter is more than 1.5 times the average: + for (int i = 0; i < numCounters && wideCounters > 0; i++) { + int counter = counters[i]; + if (counters[i] > maxNarrowCounter) { + wideCounters--; + // totalWideCountersWidth = 3 * average, so this checks if + // counter >= 3/2 * average. + if ((counter << 1) >= totalWideCountersWidth) { + return -1; } - throw ReaderException(""); + } } - - Ref Code39Reader::decodeExtended(std::string encoded){ - int length = encoded.length(); - std::string tmpDecoded; - for (int i = 0; i < length; i++) { - char c = encoded[i]; - if (c == '+' || c == '$' || c == '%' || c == '/') { - char next = encoded[i + 1]; - char decodedChar = '\0'; - switch (c) { - case '+': - // +A to +Z map to a to z - if (next >= 'A' && next <= 'Z') { - decodedChar = (char) (next + 32); - } else { - throw ReaderException(""); - } - break; - case '$': - // $A to $Z map to control codes SH to SB - if (next >= 'A' && next <= 'Z') { - decodedChar = (char) (next - 64); - } else { - throw ReaderException(""); - } - break; - case '%': - // %A to %E map to control codes ESC to US - if (next >= 'A' && next <= 'E') { - decodedChar = (char) (next - 38); - } else if (next >= 'F' && next <= 'W') { - decodedChar = (char) (next - 11); - } else { - throw ReaderException(""); - } - break; - case '/': - // /A to /O map to ! to , and /Z maps to : - if (next >= 'A' && next <= 'O') { - decodedChar = (char) (next - 32); - } else if (next == 'Z') { - decodedChar = ':'; - } else { - throw ReaderException(""); - } - break; - } - tmpDecoded.append(1, decodedChar); - // bump up i again since we read two characters - i++; - } else { - tmpDecoded.append(1, c); - } + return pattern; + } + } while (wideCounters > 3); + return -1; + } + + char Code39Reader::patternToChar(int pattern){ + for (int i = 0; i < CHARACTER_ENCODINGS_LEN; i++) { + if (CHARACTER_ENCODINGS[i] == pattern) { + return ALPHABET[i]; + } + } + throw ReaderException(""); + } + + Ref Code39Reader::decodeExtended(std::string encoded){ + int length = encoded.length(); + std::string tmpDecoded; + for (int i = 0; i < length; i++) { + char c = encoded[i]; + if (c == '+' || c == '$' || c == '%' || c == '/') { + char next = encoded[i + 1]; + char decodedChar = '\0'; + switch (c) { + case '+': + // +A to +Z map to a to z + if (next >= 'A' && next <= 'Z') { + decodedChar = (char) (next + 32); + } else { + throw ReaderException(""); } - Ref decoded(new String(tmpDecoded)); - return decoded; + break; + case '$': + // $A to $Z map to control codes SH to SB + if (next >= 'A' && next <= 'Z') { + decodedChar = (char) (next - 64); + } else { + throw ReaderException(""); + } + break; + case '%': + // %A to %E map to control codes ESC to US + if (next >= 'A' && next <= 'E') { + decodedChar = (char) (next - 38); + } else if (next >= 'F' && next <= 'W') { + decodedChar = (char) (next - 11); + } else { + throw ReaderException(""); + } + break; + case '/': + // /A to /O map to ! to , and /Z maps to : + if (next >= 'A' && next <= 'O') { + decodedChar = (char) (next - 32); + } else if (next == 'Z') { + decodedChar = ':'; + } else { + throw ReaderException(""); + } + break; } - } // namespace oned + tmpDecoded.append(1, decodedChar); + // bump up i again since we read two characters + i++; + } else { + tmpDecoded.append(1, c); + } + } + Ref decoded(new String(tmpDecoded)); + return decoded; + } +} // namespace oned } // namespace zxing // file: zxing/oned/EAN13Reader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * EAN13Reader.cpp - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -5448,85 +7225,84 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - static const int FIRST_DIGIT_ENCODINGS[10] = { - 0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A - }; - - EAN13Reader::EAN13Reader() { } - - int EAN13Reader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, - std::string& resultString) { - const int countersLen = 4; - int counters[countersLen] = { 0, 0, 0, 0 }; - - int end = row->getSize(); - int rowOffset = startGuardEnd; - int lgPatternFound = 0; - - for (int x = 0; x < 6 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, - UPC_EAN_PATTERNS_L_AND_G_PATTERNS); - if (bestMatch < 0) { - return -1; - } - resultString.append(1, (char) ('0' + bestMatch % 10)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - if (bestMatch >= 10) { - lgPatternFound |= 1 << (5 - x); - } - } - - if (!determineFirstDigit(resultString, lgPatternFound)) { - return -1; - } - - int middleRangeStart; - int middleRangeEnd; - if (findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), - getMIDDLE_PATTERN_LEN(), &middleRangeStart, &middleRangeEnd)) { - rowOffset = middleRangeEnd; - for (int x = 0; x < 6 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, - UPC_EAN_PATTERNS_L_PATTERNS); - if (bestMatch < 0) { - return -1; - } - resultString.append(1, (char) ('0' + bestMatch)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - } - return rowOffset; - } + namespace oned { + + static const int FIRST_DIGIT_ENCODINGS[10] = { + 0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A + }; + + EAN13Reader::EAN13Reader() { } + + int EAN13Reader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, + std::string& resultString) { + (void)startGuardBegin; + const int countersLen = 4; + int counters[countersLen] = { 0, 0, 0, 0 }; + + int end = row->getSize(); + int rowOffset = startGuardEnd; + int lgPatternFound = 0; + + for (int x = 0; x < 6 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, + UPC_EAN_PATTERNS_L_AND_G_PATTERNS); + if (bestMatch < 0) { + return -1; + } + resultString.append(1, (char) ('0' + bestMatch % 10)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + if (bestMatch >= 10) { + lgPatternFound |= 1 << (5 - x); + } + } + + if (!determineFirstDigit(resultString, lgPatternFound)) { + return -1; + } + + int middleRangeStart; + int middleRangeEnd; + if (findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), + getMIDDLE_PATTERN_LEN(), &middleRangeStart, &middleRangeEnd)) { + rowOffset = middleRangeEnd; + for (int x = 0; x < 6 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, + UPC_EAN_PATTERNS_L_PATTERNS); + if (bestMatch < 0) { return -1; + } + resultString.append(1, (char) ('0' + bestMatch)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } } - - bool EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound) { - for (int d = 0; d < 10; d++) { - if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) { - resultString.insert(0, 1, (char) ('0' + d)); - return true; - } - } - return false; - } - - BarcodeFormat EAN13Reader::getBarcodeFormat(){ - return BarcodeFormat_EAN_13; - } + return rowOffset; + } + return -1; } + + bool EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound) { + for (int d = 0; d < 10; d++) { + if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) { + resultString.insert(0, 1, (char) ('0' + d)); + return true; + } + } + return false; + } + + BarcodeFormat EAN13Reader::getBarcodeFormat(){ + return BarcodeFormat_EAN_13; + } + } } // file: zxing/oned/EAN8Reader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * EAN8Reader.cpp - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -5546,63 +7322,62 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - EAN8Reader::EAN8Reader(){ } - - int EAN8Reader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, - std::string& resultString){ - const int countersLen = 4; - int counters[countersLen] = { 0, 0, 0, 0 }; - - int end = row->getSize(); - int rowOffset = startGuardEnd; - - for (int x = 0; x < 4 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, - UPC_EAN_PATTERNS_L_PATTERNS); - if (bestMatch < 0) { - return -1; - } - resultString.append(1, (char) ('0' + bestMatch)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - } - - int middleRangeStart; - int middleRangeEnd; - if (findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), - getMIDDLE_PATTERN_LEN(), &middleRangeStart, &middleRangeEnd)) { - rowOffset = middleRangeEnd; - for (int x = 0; x < 4 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, - UPC_EAN_PATTERNS_L_PATTERNS); - if (bestMatch < 0) { - return -1; - } - resultString.append(1, (char) ('0' + bestMatch)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - } - return rowOffset; - } + namespace oned { + + EAN8Reader::EAN8Reader(){ } + + int EAN8Reader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, + std::string& resultString){ + (void)startGuardBegin; + const int countersLen = 4; + int counters[countersLen] = { 0, 0, 0, 0 }; + + int end = row->getSize(); + int rowOffset = startGuardEnd; + + for (int x = 0; x < 4 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, + UPC_EAN_PATTERNS_L_PATTERNS); + if (bestMatch < 0) { + return -1; + } + resultString.append(1, (char) ('0' + bestMatch)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + } + + int middleRangeStart; + int middleRangeEnd; + if (findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), + getMIDDLE_PATTERN_LEN(), &middleRangeStart, &middleRangeEnd)) { + rowOffset = middleRangeEnd; + for (int x = 0; x < 4 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, + UPC_EAN_PATTERNS_L_PATTERNS); + if (bestMatch < 0) { return -1; + } + resultString.append(1, (char) ('0' + bestMatch)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } } - - BarcodeFormat EAN8Reader::getBarcodeFormat(){ - return BarcodeFormat_EAN_8; - } + return rowOffset; + } + return -1; } + + BarcodeFormat EAN8Reader::getBarcodeFormat(){ + return BarcodeFormat_EAN_8; + } + } } // file: zxing/oned/ITFReader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * ITFReader.cpp - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -5625,342 +7400,348 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - static const int W = 3; // Pixel width of a wide line - static const int N = 1; // Pixed width of a narrow line - - const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 }; - - /** - * Start/end guard pattern. - * - * Note: The end pattern is reversed because the row is reversed before - * searching for the END_PATTERN - */ - static const int START_PATTERN_LEN = 4; - static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N}; - - static const int END_PATTERN_REVERSED_LEN = 3; - static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W}; - - /** - * Patterns of Wide / Narrow lines to indicate each digit - */ - static const int PATTERNS_LEN = 10; - static const int PATTERNS[PATTERNS_LEN][5] = { - {N, N, W, W, N}, // 0 - {W, N, N, N, W}, // 1 - {N, W, N, N, W}, // 2 - {W, W, N, N, N}, // 3 - {N, N, W, N, W}, // 4 - {W, N, W, N, N}, // 5 - {N, W, W, N, N}, // 6 - {N, N, N, W, W}, // 7 - {W, N, N, W, N}, // 8 - {N, W, N, W, N} // 9 - }; - - - ITFReader::ITFReader() : narrowLineWidth(-1) { - } - - - Ref ITFReader::decodeRow(int rowNumber, Ref row) { - int* startRange = 0; - int* endRange = 0; - try { - // Find out where the Middle section (payload) starts & ends - startRange = decodeStart(row); - endRange = decodeEnd(row); - - std::string tmpResult; - decodeMiddle(row, startRange[1], endRange[0], tmpResult); - - // To avoid false positives with 2D barcodes (and other patterns), make - // an assumption that the decoded string must be 6, 10 or 14 digits. - int length = tmpResult.length(); - bool lengthOK = false; - if (length == 6 || length == 10 || length == 14) { - lengthOK = true; - } - if (!lengthOK) { - throw ReaderException("not enough characters count"); - } - - Ref resultString(new String(tmpResult)); - - std::vector< Ref > resultPoints(2); - Ref resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber)); - Ref resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - delete [] startRange; - delete [] endRange; - ArrayRef resultBytes(1); - return Ref(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF)); - } catch (ReaderException re) { - delete [] startRange; - delete [] endRange; - return Ref(); - } - } - - /** - * @param row row of black/white values to search - * @param payloadStart offset of start pattern - * @param resultString {@link StringBuffer} to append decoded chars to - * @throws ReaderException if decoding could not complete successfully - */ - void ITFReader::decodeMiddle(Ref row, int payloadStart, int payloadEnd, - std::string& resultString) { - // Digits are interleaved in pairs - 5 black lines for one digit, and the - // 5 - // interleaved white lines for the second digit. - // Therefore, need to scan 10 lines and then - // split these into two arrays - int counterDigitPairLen = 10; - int counterDigitPair[counterDigitPairLen]; - for (int i=0; i row) { - int endStart = skipWhiteSpace(row); - int* startPattern = 0; - try { - startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN); - - // Determine the width of a narrow line in pixels. We can do this by - // getting the width of the start pattern and dividing by 4 because its - // made up of 4 narrow lines. - narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2; - validateQuietZone(row, startPattern[0]); - return startPattern; - } catch (ReaderException re) { - delete [] startPattern; - throw re; - } - } - - /** - * Identify where the end of the middle / payload section ends. - * - * @param row row of black/white values to search - * @return Array, containing index of start of 'end block' and end of 'end - * block' - * @throws ReaderException - */ - - int* ITFReader::decodeEnd(Ref row) { - // For convenience, reverse the row and then - // search from 'the start' for the end block - row->reverse(); - int* endPattern = 0; - try { - int endStart = skipWhiteSpace(row); - endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN); - - // The start & end patterns must be pre/post fixed by a quiet zone. This - // zone must be at least 10 times the width of a narrow line. - // ref: http://www.barcode-1.net/i25code.html - validateQuietZone(row, endPattern[0]); - - // Now recalculate the indices of where the 'endblock' starts & stops to - // accommodate - // the reversed nature of the search - int temp = endPattern[0]; - endPattern[0] = row->getSize() - endPattern[1]; - endPattern[1] = row->getSize() - temp; - - row->reverse(); - return endPattern; - } catch (ReaderException re) { - delete [] endPattern; - row->reverse(); - throw re; - } - } - - /** - * The start & end patterns must be pre/post fixed by a quiet zone. This - * zone must be at least 10 times the width of a narrow line. Scan back until - * we either get to the start of the barcode or match the necessary number of - * quiet zone pixels. - * - * Note: Its assumed the row is reversed when using this method to find - * quiet zone after the end pattern. - * - * ref: http://www.barcode-1.net/i25code.html - * - * @param row bit array representing the scanned barcode. - * @param startPattern index into row of the start or end pattern. - * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown. - */ - void ITFReader::validateQuietZone(Ref row, int startPattern) { - //#pragma mark needs some corrections - // int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone - // - // for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { - // if (row->get(i)) { - // break; - // } - // quietCount--; - // } - // if (quietCount != 0) { - // // Unable to find the necessary number of quiet zone pixels. - // throw ReaderException("Unable to find the necessary number of quiet zone pixels"); - // } - } - - /** - * Skip all whitespace until we get to the first black line. - * - * @param row row of black/white values to search - * @return index of the first black line. - * @throws ReaderException Throws exception if no black lines are found in the row - */ - int ITFReader::skipWhiteSpace(Ref row) { - int width = row->getSize(); - int endStart = 0; - while (endStart < width) { - if (row->get(endStart)) { - break; - } - endStart++; - } - if (endStart == width) { - throw ReaderException(""); - } - return endStart; - } - - /** - * @param row row of black/white values to search - * @param rowOffset position to start search - * @param pattern pattern of counts of number of black and white pixels that are - * being searched for as a pattern - * @return start/end horizontal offset of guard pattern, as an array of two - * ints - * @throws ReaderException if pattern is not found - */ - int* ITFReader::findGuardPattern(Ref row, int rowOffset, const int pattern[], - int patternLen) { - // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be - // merged to a single method. - int patternLength = patternLen; - int counters[patternLength]; - for (int i=0; igetSize(); - bool isWhite = false; - - int counterPosition = 0; - int patternStart = rowOffset; - for (int x = rowOffset; x < width; x++) { - bool pixel = row->get(x); - if (pixel ^ isWhite) { - counters[counterPosition]++; - } else { - if (counterPosition == patternLength - 1) { - if (patternMatchVariance(counters, patternLength, pattern, - MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { - int* resultValue = new int[2]; - resultValue[0] = patternStart; - resultValue[1] = x; - return resultValue; - } - patternStart += counters[0] + counters[1]; - for (int y = 2; y < patternLength; y++) { - counters[y - 2] = counters[y]; - } - counters[patternLength - 2] = 0; - counters[patternLength - 1] = 0; - counterPosition--; - } else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw ReaderException(""); - } - - /** - * Attempts to decode a sequence of ITF black/white lines into single - * digit. - * - * @param counters the counts of runs of observed black/white/black/... values - * @return The decoded digit - * @throws ReaderException if digit cannot be decoded - */ - int ITFReader::decodeDigit(int counters[], int countersLen){ - unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept - int bestMatch = -1; - int max = PATTERNS_LEN; - for (int i = 0; i < max; i++) { - int pattern[countersLen]; - for(int ind = 0; ind= 0) { - return bestMatch; - } else { - throw ReaderException("digit didint found"); - } - } - - ITFReader::~ITFReader(){ - } + namespace oned { + + static const int W = 3; // Pixel width of a wide line + static const int N = 1; // Pixed width of a narrow line + + const int DEFAULT_ALLOWED_LENGTHS_LEN = 10; + const int DEFAULT_ALLOWED_LENGTHS[DEFAULT_ALLOWED_LENGTHS_LEN] = { 44, 24, 20, 18, 16, 14, 12, 10, 8, 6 }; + + /** + * Start/end guard pattern. + * + * Note: The end pattern is reversed because the row is reversed before + * searching for the END_PATTERN + */ + static const int START_PATTERN_LEN = 4; + static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N}; + + static const int END_PATTERN_REVERSED_LEN = 3; + static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W}; + + /** + * Patterns of Wide / Narrow lines to indicate each digit + */ + static const int PATTERNS_LEN = 10; + static const int PATTERNS[PATTERNS_LEN][5] = { + {N, N, W, W, N}, // 0 + {W, N, N, N, W}, // 1 + {N, W, N, N, W}, // 2 + {W, W, N, N, N}, // 3 + {N, N, W, N, W}, // 4 + {W, N, W, N, N}, // 5 + {N, W, W, N, N}, // 6 + {N, N, N, W, W}, // 7 + {W, N, N, W, N}, // 8 + {N, W, N, W, N} // 9 + }; + + + ITFReader::ITFReader() : narrowLineWidth(-1) { } + + + Ref ITFReader::decodeRow(int rowNumber, Ref row) { + int* startRange = 0; + int* endRange = 0; + try { + // Find out where the Middle section (payload) starts & ends + startRange = decodeStart(row); + endRange = decodeEnd(row); + + std::string tmpResult; + decodeMiddle(row, startRange[1], endRange[0], tmpResult); + + // To avoid false positives with 2D barcodes (and other patterns), make + // an assumption that the decoded string must be a known length + int length = tmpResult.length(); + bool lengthOK = false; + for (int i = 0; i < DEFAULT_ALLOWED_LENGTHS_LEN; i++) { + if (length == DEFAULT_ALLOWED_LENGTHS[i]) { + lengthOK = true; + break; + } + } + if (!lengthOK) { + throw ReaderException("not enough characters count"); + } + + Ref resultString(new String(tmpResult)); + + std::vector< Ref > resultPoints(2); + Ref resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber)); + Ref resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + + delete [] startRange; + delete [] endRange; + ArrayRef resultBytes(1); + return Ref(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF)); + } catch (ReaderException const& re) { + delete [] startRange; + delete [] endRange; + return Ref(); + } + } + + /** + * @param row row of black/white values to search + * @param payloadStart offset of start pattern + * @param resultString {@link StringBuffer} to append decoded chars to + * @throws ReaderException if decoding could not complete successfully + */ + void ITFReader::decodeMiddle(Ref row, int payloadStart, int payloadEnd, + std::string& resultString) { + // Digits are interleaved in pairs - 5 black lines for one digit, and the + // 5 + // interleaved white lines for the second digit. + // Therefore, need to scan 10 lines and then + // split these into two arrays + int counterDigitPairLen = 10; + int counterDigitPair[counterDigitPairLen]; + for (int i=0; i row) { + int endStart = skipWhiteSpace(row); + int* startPattern = 0; + try { + startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN); + + // Determine the width of a narrow line in pixels. We can do this by + // getting the width of the start pattern and dividing by 4 because its + // made up of 4 narrow lines. + narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2; + validateQuietZone(row, startPattern[0]); + return startPattern; + } catch (ReaderException const& re) { + delete [] startPattern; + throw re; + } + } + + /** + * Identify where the end of the middle / payload section ends. + * + * @param row row of black/white values to search + * @return Array, containing index of start of 'end block' and end of 'end + * block' + * @throws ReaderException + */ + + int* ITFReader::decodeEnd(Ref row) { + // For convenience, reverse the row and then + // search from 'the start' for the end block + row->reverse(); + int* endPattern = 0; + try { + int endStart = skipWhiteSpace(row); + endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN); + + // The start & end patterns must be pre/post fixed by a quiet zone. This + // zone must be at least 10 times the width of a narrow line. + // ref: http://www.barcode-1.net/i25code.html + validateQuietZone(row, endPattern[0]); + + // Now recalculate the indices of where the 'endblock' starts & stops to + // accommodate + // the reversed nature of the search + int temp = endPattern[0]; + endPattern[0] = row->getSize() - endPattern[1]; + endPattern[1] = row->getSize() - temp; + + row->reverse(); + return endPattern; + } catch (ReaderException const& re) { + delete [] endPattern; + row->reverse(); + throw re; + } + } + + /** + * The start & end patterns must be pre/post fixed by a quiet zone. This + * zone must be at least 10 times the width of a narrow line. Scan back until + * we either get to the start of the barcode or match the necessary number of + * quiet zone pixels. + * + * Note: Its assumed the row is reversed when using this method to find + * quiet zone after the end pattern. + * + * ref: http://www.barcode-1.net/i25code.html + * + * @param row bit array representing the scanned barcode. + * @param startPattern index into row of the start or end pattern. + * @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown. + */ + void ITFReader::validateQuietZone(Ref row, int startPattern) { + (void)row; + (void)startPattern; +//#pragma mark needs some corrections +// int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone +// +// for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) { +// if (row->get(i)) { +// break; +// } +// quietCount--; +// } +// if (quietCount != 0) { +// // Unable to find the necessary number of quiet zone pixels. +// throw ReaderException("Unable to find the necessary number of quiet zone pixels"); +// } + } + + /** + * Skip all whitespace until we get to the first black line. + * + * @param row row of black/white values to search + * @return index of the first black line. + * @throws ReaderException Throws exception if no black lines are found in the row + */ + int ITFReader::skipWhiteSpace(Ref row) { + int width = row->getSize(); + int endStart = 0; + while (endStart < width) { + if (row->get(endStart)) { + break; + } + endStart++; + } + if (endStart == width) { + throw ReaderException(""); + } + return endStart; + } + + /** + * @param row row of black/white values to search + * @param rowOffset position to start search + * @param pattern pattern of counts of number of black and white pixels that are + * being searched for as a pattern + * @return start/end horizontal offset of guard pattern, as an array of two + * ints + * @throws ReaderException if pattern is not found + */ + int* ITFReader::findGuardPattern(Ref row, int rowOffset, const int pattern[], + int patternLen) { + // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be + // merged to a single method. + int patternLength = patternLen; + int counters[patternLength]; + for (int i=0; igetSize(); + bool isWhite = false; + + int counterPosition = 0; + int patternStart = rowOffset; + for (int x = rowOffset; x < width; x++) { + bool pixel = row->get(x); + if (pixel ^ isWhite) { + counters[counterPosition]++; + } else { + if (counterPosition == patternLength - 1) { + if (patternMatchVariance(counters, patternLength, pattern, + MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { + int* resultValue = new int[2]; + resultValue[0] = patternStart; + resultValue[1] = x; + return resultValue; + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } else { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + throw ReaderException(""); + } + + /** + * Attempts to decode a sequence of ITF black/white lines into single + * digit. + * + * @param counters the counts of runs of observed black/white/black/... values + * @return The decoded digit + * @throws ReaderException if digit cannot be decoded + */ + int ITFReader::decodeDigit(int counters[], int countersLen){ + unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = -1; + int max = PATTERNS_LEN; + for (int i = 0; i < max; i++) { + int pattern[countersLen]; + for(int ind = 0; ind= 0) { + return bestMatch; + } else { + throw ReaderException("digit didint found"); + } + } + + ITFReader::~ITFReader(){ + } + } } // file: zxing/oned/MultiFormatOneDReader.cpp @@ -5993,43 +7774,43 @@ namespace zxing { // #include namespace zxing { - namespace oned { - MultiFormatOneDReader::MultiFormatOneDReader(DecodeHints hints) : readers() { - if (hints.containsFormat(BarcodeFormat_EAN_13) || - hints.containsFormat(BarcodeFormat_EAN_8) || - hints.containsFormat(BarcodeFormat_UPC_A) || - hints.containsFormat(BarcodeFormat_UPC_E)) { - readers.push_back(Ref(new MultiFormatUPCEANReader(hints))); - } - if (hints.containsFormat(BarcodeFormat_CODE_39)) { - readers.push_back(Ref(new Code39Reader())); - } - if (hints.containsFormat(BarcodeFormat_CODE_128)) { - readers.push_back(Ref(new Code128Reader())); - } - if (hints.containsFormat(BarcodeFormat_ITF)) { - readers.push_back(Ref(new ITFReader())); - } - if (readers.size() == 0) { - readers.push_back(Ref(new MultiFormatUPCEANReader(hints))); - readers.push_back(Ref(new Code39Reader())); - readers.push_back(Ref(new Code128Reader())); - readers.push_back(Ref(new ITFReader())); - } - } - - Ref MultiFormatOneDReader::decodeRow(int rowNumber, Ref row) { - int size = readers.size(); - for (int i = 0; i < size; i++) { - OneDReader* reader = readers[i]; - Ref result = reader->decodeRow(rowNumber, row); - if (!result.empty()) { - return result; - } - } - return Ref(); - } + namespace oned { + MultiFormatOneDReader::MultiFormatOneDReader(DecodeHints hints) : readers() { + if (hints.containsFormat(BarcodeFormat_EAN_13) || + hints.containsFormat(BarcodeFormat_EAN_8) || + hints.containsFormat(BarcodeFormat_UPC_A) || + hints.containsFormat(BarcodeFormat_UPC_E)) { + readers.push_back(Ref(new MultiFormatUPCEANReader(hints))); + } + if (hints.containsFormat(BarcodeFormat_CODE_39)) { + readers.push_back(Ref(new Code39Reader())); + } + if (hints.containsFormat(BarcodeFormat_CODE_128)) { + readers.push_back(Ref(new Code128Reader())); + } + if (hints.containsFormat(BarcodeFormat_ITF)) { + readers.push_back(Ref(new ITFReader())); + } + if (readers.size() == 0) { + readers.push_back(Ref(new MultiFormatUPCEANReader(hints))); + readers.push_back(Ref(new Code39Reader())); + readers.push_back(Ref(new Code128Reader())); + readers.push_back(Ref(new ITFReader())); + } } + + Ref MultiFormatOneDReader::decodeRow(int rowNumber, Ref row) { + int size = readers.size(); + for (int i = 0; i < size; i++) { + OneDReader* reader = readers[i]; + Ref result = reader->decodeRow(rowNumber, row); + if (!result.empty()) { + return result; + } + } + return Ref(); + } + } } // file: zxing/oned/MultiFormatUPCEANReader.cpp @@ -6064,66 +7845,67 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - MultiFormatUPCEANReader::MultiFormatUPCEANReader(DecodeHints hints) : readers() { - if (hints.containsFormat(BarcodeFormat_EAN_13)) { - readers.push_back(Ref(new EAN13Reader())); - } else if (hints.containsFormat(BarcodeFormat_UPC_A)) { - readers.push_back(Ref(new UPCAReader())); - } - if (hints.containsFormat(BarcodeFormat_EAN_8)) { - readers.push_back(Ref(new EAN8Reader())); - } - if (hints.containsFormat(BarcodeFormat_UPC_E)) { - readers.push_back(Ref(new UPCEReader())); - } - if (readers.size() == 0) { - readers.push_back(Ref(new EAN13Reader())); - // UPC-A is covered by EAN-13 - readers.push_back(Ref(new EAN8Reader())); - readers.push_back(Ref(new UPCEReader())); - } - } - - Ref MultiFormatUPCEANReader::decodeRow(int rowNumber, Ref row) { - // Compute this location once and reuse it on multiple implementations - int size = readers.size(); - for (int i = 0; i < size; i++) { - Ref reader = readers[i]; - Ref result = reader->decodeRow(rowNumber, row); - if (result.empty()) { - continue; - } - - // Special case: a 12-digit code encoded in UPC-A is identical to a "0" - // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, - // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". - // Individually these are correct and their readers will both read such a code - // and correctly call it EAN-13, or UPC-A, respectively. - // - // In this case, if we've been looking for both types, we'd like to call it - // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read - // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A - // result if appropriate. - if (result->getBarcodeFormat() == BarcodeFormat_EAN_13) { - const std::string& text = (result->getText())->getText(); - if (text[0] == '0') { - Ref resultString(new String(text.substr(1))); - Ref res(new Result(resultString, result->getRawBytes(), - result->getResultPoints(), BarcodeFormat_UPC_A)); - return res; - } - } - return result; - } - return Ref(); - } + namespace oned { + + MultiFormatUPCEANReader::MultiFormatUPCEANReader(DecodeHints hints) : readers() { + if (hints.containsFormat(BarcodeFormat_EAN_13)) { + readers.push_back(Ref(new EAN13Reader())); + } else if (hints.containsFormat(BarcodeFormat_UPC_A)) { + readers.push_back(Ref(new UPCAReader())); + } + if (hints.containsFormat(BarcodeFormat_EAN_8)) { + readers.push_back(Ref(new EAN8Reader())); + } + if (hints.containsFormat(BarcodeFormat_UPC_E)) { + readers.push_back(Ref(new UPCEReader())); + } + if (readers.size() == 0) { + readers.push_back(Ref(new EAN13Reader())); + // UPC-A is covered by EAN-13 + readers.push_back(Ref(new EAN8Reader())); + readers.push_back(Ref(new UPCEReader())); + } } + + Ref MultiFormatUPCEANReader::decodeRow(int rowNumber, Ref row) { + // Compute this location once and reuse it on multiple implementations + int size = readers.size(); + for (int i = 0; i < size; i++) { + Ref reader = readers[i]; + Ref result = reader->decodeRow(rowNumber, row); + if (result.empty()) { + continue; + } + + // Special case: a 12-digit code encoded in UPC-A is identical to a "0" + // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, + // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". + // Individually these are correct and their readers will both read such a code + // and correctly call it EAN-13, or UPC-A, respectively. + // + // In this case, if we've been looking for both types, we'd like to call it + // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read + // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A + // result if appropriate. + if (result->getBarcodeFormat() == BarcodeFormat_EAN_13) { + const std::string& text = (result->getText())->getText(); + if (text[0] == '0') { + Ref resultString(new String(text.substr(1))); + Ref res(new Result(resultString, result->getRawBytes(), + result->getResultPoints(), BarcodeFormat_UPC_A)); + return res; + } + } + return result; + } + return Ref(); + } + } } // file: zxing/oned/OneDReader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * OneDReader.cpp * ZXing @@ -6150,185 +7932,185 @@ namespace zxing { // #include namespace zxing { - namespace oned { - using namespace std; - - OneDReader::OneDReader() { - } - - Ref OneDReader::decode(Ref image, DecodeHints hints) { - Ref result = doDecode(image, hints); - if (result.empty() && hints.getTryHarder() && image->isRotateSupported()) { - Ref rotatedImage(image->rotateCounterClockwise()); - result = doDecode(rotatedImage, hints); - if (!result.empty()) { - /* - // Record that we found it rotated 90 degrees CCW / 270 degrees CW - Hashtable metadata = result.getResultMetadata(); - int orientation = 270; - if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) { - // But if we found it reversed in doDecode(), add in that result here: - orientation = (orientation + + namespace oned { + using namespace std; + + OneDReader::OneDReader() { + } + + Ref OneDReader::decode(Ref image, DecodeHints hints) { + Ref result = doDecode(image, hints); + if (result.empty() && hints.getTryHarder() && image->isRotateSupported()) { + Ref rotatedImage(image->rotateCounterClockwise()); + result = doDecode(rotatedImage, hints); + if (!result.empty()) { + /* + // Record that we found it rotated 90 degrees CCW / 270 degrees CW + Hashtable metadata = result.getResultMetadata(); + int orientation = 270; + if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) { + // But if we found it reversed in doDecode(), add in that result here: + orientation = (orientation + ((Integer) metadata.get(ResultMetadataType.ORIENTATION)).intValue()) % 360; - } - result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(orientation)); - */ - // Update result points - std::vector > points (result->getResultPoints()); - int height = rotatedImage->getHeight(); - for (size_t i = 0; i < points.size(); i++) { - points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX())); - } - } - } - if (result.empty()) { - throw ReaderException(""); + } + result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(orientation)); + */ + // Update result points + std::vector >& points (result->getResultPoints()); + int height = rotatedImage->getHeight(); + for (size_t i = 0; i < points.size(); i++) { + points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX())); + } + } + } + if (result.empty()) { + throw ReaderException(""); + } + return result; + } + + Ref OneDReader::doDecode(Ref image, DecodeHints hints) { + int width = image->getWidth(); + int height = image->getHeight(); + Ref row(new BitArray(width)); + int middle = height >> 1; + bool tryHarder = hints.getTryHarder(); + int rowStep = (int)fmax(1, height >> (tryHarder ? 8 : 5)); + int maxLines; + if (tryHarder) { + maxLines = height; // Look at the whole image, not just the center + } else { + maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image + } + + for (int x = 0; x < maxLines; x++) { + // Scanning from the middle out. Determine which row we're looking at next: + int rowStepsAboveOrBelow = (x + 1) >> 1; + bool isAbove = (x & 0x01) == 0; // i.e. is x even? + int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow); + if (rowNumber < 0 || rowNumber >= height) { + // Oops, if we run off the top or bottom, stop + break; + } + + // Estimate black point for this row and load it: + try { + row = image->getBlackRow(rowNumber, row); + } catch (ReaderException const& re) { + continue; + } catch (IllegalArgumentException const& re) { + continue; + } + + // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to + // handle decoding upside down barcodes. + for (int attempt = 0; attempt < 2; attempt++) { + if (attempt == 1) { + row->reverse(); // reverse the row and continue + } + + // Look for a barcode + Ref result = decodeRow(rowNumber, row); + // We found our barcode + if (!result.empty()) { + if (attempt == 1) { + // But it was upside down, so note that + // result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180)); + // And remember to flip the result points horizontally. + std::vector > points(result->getResultPoints()); + // if there's exactly two points (which there should be), flip the x coordinate + // if there's not exactly 2, I don't know what do do with it + if (points.size() == 2) { + Ref pointZero(new OneDResultPoint(width - points[0]->getX() - 1, + points[0]->getY())); + points[0] = pointZero; + + Ref pointOne(new OneDResultPoint(width - points[1]->getX() - 1, + points[1]->getY())); + points[1] = pointOne; + + result.reset(new Result(result->getText(), result->getRawBytes(), points, + result->getBarcodeFormat())); + } } return result; + } } - - Ref OneDReader::doDecode(Ref image, DecodeHints hints) { - int width = image->getWidth(); - int height = image->getHeight(); - Ref row(new BitArray(width)); - int middle = height >> 1; - bool tryHarder = hints.getTryHarder(); - int rowStep = (int)fmax(1, height >> (tryHarder ? 8 : 5)); - int maxLines; - if (tryHarder) { - maxLines = height; // Look at the whole image, not just the center - } else { - maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image - } - - for (int x = 0; x < maxLines; x++) { - // Scanning from the middle out. Determine which row we're looking at next: - int rowStepsAboveOrBelow = (x + 1) >> 1; - bool isAbove = (x & 0x01) == 0; // i.e. is x even? - int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow); - if (rowNumber < 0 || rowNumber >= height) { - // Oops, if we run off the top or bottom, stop - break; - } - - // Estimate black point for this row and load it: - try { - row = image->getBlackRow(rowNumber, row); - } catch (ReaderException re) { - continue; - } catch (IllegalArgumentException re) { - continue; - } - - // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to - // handle decoding upside down barcodes. - for (int attempt = 0; attempt < 2; attempt++) { - if (attempt == 1) { - row->reverse(); // reverse the row and continue - } - - // Look for a barcode - Ref result = decodeRow(rowNumber, row); - // We found our barcode - if (!result.empty()) { - if (attempt == 1) { - // But it was upside down, so note that - // result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180)); - // And remember to flip the result points horizontally. - std::vector > points(result->getResultPoints()); - // if there's exactly two points (which there should be), flip the x coordinate - // if there's not exactly 2, I don't know what do do with it - if (points.size() == 2) { - Ref pointZero(new OneDResultPoint(width - points[0]->getX() - 1, - points[0]->getY())); - points[0] = pointZero; - - Ref pointOne(new OneDResultPoint(width - points[1]->getX() - 1, - points[1]->getY())); - points[1] = pointOne; - - result.reset(new Result(result->getText(), result->getRawBytes(), points, - result->getBarcodeFormat())); - } - } - return result; - } - } - } - return Ref(); - } - - unsigned int OneDReader::patternMatchVariance(int counters[], int countersSize, - const int pattern[], int maxIndividualVariance) { - int numCounters = countersSize; - unsigned int total = 0; - unsigned int patternLength = 0; - for (int i = 0; i < numCounters; i++) { - total += counters[i]; - patternLength += pattern[i]; - } - if (total < patternLength) { - // If we don't even have one pixel per unit of bar width, assume this is too small - // to reliably match, so fail: - return INT_MAX; - } - // We're going to fake floating-point math in integers. We just need to use more bits. - // Scale up patternLength so that intermediate values below like scaledCounter will have - // more "significant digits" - unsigned int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength; - maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT; - - unsigned int totalVariance = 0; - for (int x = 0; x < numCounters; x++) { - int counter = counters[x] << INTEGER_MATH_SHIFT; - int scaledPattern = pattern[x] * unitBarWidth; - int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter; - if (variance > maxIndividualVariance) { - return INT_MAX; - } - totalVariance += variance; - } - return totalVariance / total; - } - - bool OneDReader::recordPattern(Ref row, int start, int counters[], int countersCount) { - int numCounters = countersCount;//sizeof(counters) / sizeof(int); - for (int i = 0; i < numCounters; i++) { - counters[i] = 0; - } - int end = row->getSize(); - if (start >= end) { - return false; - } - bool isWhite = !row->get(start); - int counterPosition = 0; - int i = start; - while (i < end) { - bool pixel = row->get(i); - if (pixel ^ isWhite) { // that is, exactly one is true - counters[counterPosition]++; - } else { - counterPosition++; - if (counterPosition == numCounters) { - break; - } else { - counters[counterPosition] = 1; - isWhite ^= true; // isWhite = !isWhite; - } - } - i++; - } - // If we read fully the last section of pixels and filled up our counters -- or filled - // the last counter but ran off the side of the image, OK. Otherwise, a problem. - if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) { - return false; - } - return true; - } - - OneDReader::~OneDReader() { - } + } + return Ref(); } + + unsigned int OneDReader::patternMatchVariance(int counters[], int countersSize, + const int pattern[], int maxIndividualVariance) { + int numCounters = countersSize; + unsigned int total = 0; + unsigned int patternLength = 0; + for (int i = 0; i < numCounters; i++) { + total += counters[i]; + patternLength += pattern[i]; + } + if (total < patternLength) { + // If we don't even have one pixel per unit of bar width, assume this is too small + // to reliably match, so fail: + return INT_MAX; + } + // We're going to fake floating-point math in integers. We just need to use more bits. + // Scale up patternLength so that intermediate values below like scaledCounter will have + // more "significant digits" + unsigned int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength; + maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT; + + unsigned int totalVariance = 0; + for (int x = 0; x < numCounters; x++) { + int counter = counters[x] << INTEGER_MATH_SHIFT; + int scaledPattern = pattern[x] * unitBarWidth; + int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter; + if (variance > maxIndividualVariance) { + return INT_MAX; + } + totalVariance += variance; + } + return totalVariance / total; + } + + bool OneDReader::recordPattern(Ref row, int start, int counters[], int countersCount) { + int numCounters = countersCount;//sizeof(counters) / sizeof(int); + for (int i = 0; i < numCounters; i++) { + counters[i] = 0; + } + int end = row->getSize(); + if (start >= end) { + return false; + } + bool isWhite = !row->get(start); + int counterPosition = 0; + int i = start; + while (i < end) { + bool pixel = row->get(i); + if (pixel ^ isWhite) { // that is, exactly one is true + counters[counterPosition]++; + } else { + counterPosition++; + if (counterPosition == numCounters) { + break; + } else { + counters[counterPosition] = 1; + isWhite ^= true; // isWhite = !isWhite; + } + } + i++; + } + // If we read fully the last section of pixels and filled up our counters -- or filled + // the last counter but ran off the side of the image, OK. Otherwise, a problem. + if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) { + return false; + } + return true; + } + + OneDReader::~OneDReader() { + } + } } // file: zxing/oned/OneDResultPoint.cpp @@ -6356,16 +8138,8 @@ namespace zxing { namespace zxing { namespace oned { - - OneDResultPoint::OneDResultPoint(float posX, float posY) : posX_(posX), posY_(posY) { - } - - float OneDResultPoint::getX() const { - return posX_; - } - - float OneDResultPoint::getY() const { - return posY_; + + OneDResultPoint::OneDResultPoint(float posX, float posY) : ResultPoint(posX,posY) { } } } @@ -6395,47 +8169,47 @@ namespace zxing { // #include namespace zxing { - namespace oned { - UPCAReader::UPCAReader() : ean13Reader() { - } - - Ref UPCAReader::decodeRow(int rowNumber, Ref row) { - return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row)); - } - - Ref UPCAReader::decodeRow(int rowNumber, Ref row, int startGuardBegin, - int startGuardEnd) { - return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardBegin, - startGuardEnd)); - } - - Ref UPCAReader::decode(Ref image, DecodeHints hints) { - return maybeReturnResult(ean13Reader.decode(image, hints)); - } - - int UPCAReader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, - std::string& resultString) { - return ean13Reader.decodeMiddle(row, startGuardBegin, startGuardEnd, resultString); - } - - Ref UPCAReader::maybeReturnResult(Ref result) { - if (result.empty()) { - return result; - } - const std::string& text = (result->getText())->getText(); - if (text[0] == '0') { - Ref resultString(new String(text.substr(1))); - Ref res(new Result(resultString, result->getRawBytes(), result->getResultPoints(), - BarcodeFormat_UPC_A)); - return res; - } - return Ref(); - } - - BarcodeFormat UPCAReader::getBarcodeFormat(){ - return BarcodeFormat_UPC_A; - } + namespace oned { + UPCAReader::UPCAReader() : ean13Reader() { } + + Ref UPCAReader::decodeRow(int rowNumber, Ref row) { + return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row)); + } + + Ref UPCAReader::decodeRow(int rowNumber, Ref row, int startGuardBegin, + int startGuardEnd) { + return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardBegin, + startGuardEnd)); + } + + Ref UPCAReader::decode(Ref image, DecodeHints hints) { + return maybeReturnResult(ean13Reader.decode(image, hints)); + } + + int UPCAReader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, + std::string& resultString) { + return ean13Reader.decodeMiddle(row, startGuardBegin, startGuardEnd, resultString); + } + + Ref UPCAReader::maybeReturnResult(Ref result) { + if (result.empty()) { + return result; + } + const std::string& text = (result->getText())->getText(); + if (text[0] == '0') { + Ref resultString(new String(text.substr(1))); + Ref res(new Result(resultString, result->getRawBytes(), result->getResultPoints(), + BarcodeFormat_UPC_A)); + return res; + } + return Ref(); + } + + BarcodeFormat UPCAReader::getBarcodeFormat(){ + return BarcodeFormat_UPC_A; + } + } } // file: zxing/oned/UPCEANReader.cpp @@ -6464,300 +8238,298 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - /** - * Start/end guard pattern. - */ - static const int START_END_PATTERN[3] = {1, 1, 1}; - - /** - * Pattern marking the middle of a UPC/EAN pattern, separating the two halves. - */ - static const int MIDDLE_PATTERN_LEN = 5; - static const int MIDDLE_PATTERN[MIDDLE_PATTERN_LEN] = {1, 1, 1, 1, 1}; - - /** - * "Odd", or "L" patterns used to encode UPC/EAN digits. - */ - const int L_PATTERNS_LEN = 10; - const int L_PATTERNS_SUB_LEN = 4; - const int L_PATTERNS[L_PATTERNS_LEN][L_PATTERNS_SUB_LEN] = { - {3, 2, 1, 1}, // 0 - {2, 2, 2, 1}, // 1 - {2, 1, 2, 2}, // 2 - {1, 4, 1, 1}, // 3 - {1, 1, 3, 2}, // 4 - {1, 2, 3, 1}, // 5 - {1, 1, 1, 4}, // 6 - {1, 3, 1, 2}, // 7 - {1, 2, 1, 3}, // 8 - {3, 1, 1, 2} // 9 - }; - - /** - * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. - */ - const int L_AND_G_PATTERNS_LEN = 20; - const int L_AND_G_PATTERNS_SUB_LEN = 4; - const int L_AND_G_PATTERNS[L_AND_G_PATTERNS_LEN][L_AND_G_PATTERNS_SUB_LEN] = { - {3, 2, 1, 1}, // 0 - {2, 2, 2, 1}, // 1 - {2, 1, 2, 2}, // 2 - {1, 4, 1, 1}, // 3 - {1, 1, 3, 2}, // 4 - {1, 2, 3, 1}, // 5 - {1, 1, 1, 4}, // 6 - {1, 3, 1, 2}, // 7 - {1, 2, 1, 3}, // 8 - {3, 1, 1, 2}, // 9 - {1, 1, 2, 3}, // 10 reversed 0 - {1, 2, 2, 2}, // 11 reversed 1 - {2, 2, 1, 2}, // 12 reversed 2 - {1, 1, 4, 1}, // 13 reversed 3 - {2, 3, 1, 1}, // 14 reversed 4 - {1, 3, 2, 1}, // 15 reversed 5 - {4, 1, 1, 1}, // 16 reversed 6 - {2, 1, 3, 1}, // 17 reversed 7 - {3, 1, 2, 1}, // 18 reversed 8 - {2, 1, 1, 3} // 19 reversed 9 - }; - - - int UPCEANReader::getMIDDLE_PATTERN_LEN() { - return MIDDLE_PATTERN_LEN; - } - - const int* UPCEANReader::getMIDDLE_PATTERN() { - return MIDDLE_PATTERN; - } - - UPCEANReader::UPCEANReader() { - } - - - Ref UPCEANReader::decodeRow(int rowNumber, Ref row) { - int rangeStart; - int rangeEnd; + namespace oned { + + /** + * Start/end guard pattern. + */ + static const int START_END_PATTERN[3] = {1, 1, 1}; + + /** + * Pattern marking the middle of a UPC/EAN pattern, separating the two halves. + */ + static const int MIDDLE_PATTERN_LEN = 5; + static const int MIDDLE_PATTERN[MIDDLE_PATTERN_LEN] = {1, 1, 1, 1, 1}; + + /** + * "Odd", or "L" patterns used to encode UPC/EAN digits. + */ + const int L_PATTERNS_LEN = 10; + const int L_PATTERNS_SUB_LEN = 4; + const int L_PATTERNS[L_PATTERNS_LEN][L_PATTERNS_SUB_LEN] = { + {3, 2, 1, 1}, // 0 + {2, 2, 2, 1}, // 1 + {2, 1, 2, 2}, // 2 + {1, 4, 1, 1}, // 3 + {1, 1, 3, 2}, // 4 + {1, 2, 3, 1}, // 5 + {1, 1, 1, 4}, // 6 + {1, 3, 1, 2}, // 7 + {1, 2, 1, 3}, // 8 + {3, 1, 1, 2} // 9 + }; + + /** + * As above but also including the "even", or "G" patterns used to encode UPC/EAN digits. + */ + const int L_AND_G_PATTERNS_LEN = 20; + const int L_AND_G_PATTERNS_SUB_LEN = 4; + const int L_AND_G_PATTERNS[L_AND_G_PATTERNS_LEN][L_AND_G_PATTERNS_SUB_LEN] = { + {3, 2, 1, 1}, // 0 + {2, 2, 2, 1}, // 1 + {2, 1, 2, 2}, // 2 + {1, 4, 1, 1}, // 3 + {1, 1, 3, 2}, // 4 + {1, 2, 3, 1}, // 5 + {1, 1, 1, 4}, // 6 + {1, 3, 1, 2}, // 7 + {1, 2, 1, 3}, // 8 + {3, 1, 1, 2}, // 9 + {1, 1, 2, 3}, // 10 reversed 0 + {1, 2, 2, 2}, // 11 reversed 1 + {2, 2, 1, 2}, // 12 reversed 2 + {1, 1, 4, 1}, // 13 reversed 3 + {2, 3, 1, 1}, // 14 reversed 4 + {1, 3, 2, 1}, // 15 reversed 5 + {4, 1, 1, 1}, // 16 reversed 6 + {2, 1, 3, 1}, // 17 reversed 7 + {3, 1, 2, 1}, // 18 reversed 8 + {2, 1, 1, 3} // 19 reversed 9 + }; + + + int UPCEANReader::getMIDDLE_PATTERN_LEN() { + return MIDDLE_PATTERN_LEN; + } + + const int* UPCEANReader::getMIDDLE_PATTERN() { + return MIDDLE_PATTERN; + } + + UPCEANReader::UPCEANReader() { + } + + + Ref UPCEANReader::decodeRow(int rowNumber, Ref row) { + int rangeStart; + int rangeEnd; if (findStartGuardPattern(row, &rangeStart, &rangeEnd)) { - try { - return decodeRow(rowNumber, row, rangeStart, rangeEnd); - } catch (ReaderException const& re) { - } + try { + return decodeRow(rowNumber, row, rangeStart, rangeEnd); + } catch (ReaderException const& re) { + } } return Ref(); - } - - Ref UPCEANReader::decodeRow(int rowNumber, Ref row, int startGuardBegin, - int startGuardEnd) { - std::string tmpResultString; - std::string& tmpResultStringRef = tmpResultString; - int endStart = decodeMiddle(row, startGuardBegin, startGuardEnd, tmpResultStringRef); - if (endStart < 0) { - return Ref(); - } - - int endGuardBegin; - int endGuardEnd; - if (!decodeEnd(row, endStart, &endGuardBegin, &endGuardEnd)) { - return Ref(); - } - - // Make sure there is a quiet zone at least as big as the end pattern after the barcode. - // The spec might want more whitespace, but in practice this is the maximum we can count on. - size_t quietEnd = endGuardEnd + (endGuardEnd - endGuardBegin); - if (quietEnd >= row->getSize() || !row->isRange(endGuardEnd, quietEnd, false)) { - return Ref(); - } - - if (!checkChecksum(tmpResultString)) { - return Ref(); - } - - Ref resultString(new String(tmpResultString)); - float left = (float) (startGuardBegin + startGuardEnd) / 2.0f; - float right = (float) (endGuardBegin + endGuardEnd) / 2.0f; - - std::vector< Ref > resultPoints(2); - Ref resultPoint1(new OneDResultPoint(left, (float) rowNumber)); - Ref resultPoint2(new OneDResultPoint(right, (float) rowNumber)); - resultPoints[0] = resultPoint1; - resultPoints[1] = resultPoint2; - - ArrayRef resultBytes(1); - return Ref(new Result(resultString, resultBytes, resultPoints, getBarcodeFormat())); - } - - bool UPCEANReader::findStartGuardPattern(Ref row, int* rangeStart, int* rangeEnd) { - int nextStart = 0; - while (findGuardPattern(row, nextStart, false, START_END_PATTERN, - sizeof(START_END_PATTERN) / sizeof(int), rangeStart, rangeEnd)) { - int start = *rangeStart; - nextStart = *rangeEnd; - // Make sure there is a quiet zone at least as big as the start pattern before the barcode. - // If this check would run off the left edge of the image, do not accept this barcode, - // as it is very likely to be a false positive. - int quietStart = start - (nextStart - start); - if (quietStart >= 0 && row->isRange(quietStart, start, false)) { - return true; - } - } - return false; - } - - bool UPCEANReader::findGuardPattern(Ref row, int rowOffset, bool whiteFirst, - const int pattern[], int patternLen, int* start, int* end) { - int patternLength = patternLen; - int counters[patternLength]; - int countersCount = sizeof(counters) / sizeof(int); - for (int i = 0; i < countersCount; i++) { - counters[i] = 0; - } - int width = row->getSize(); - bool isWhite = false; - while (rowOffset < width) { - isWhite = !row->get(rowOffset); - if (whiteFirst == isWhite) { - break; - } - rowOffset++; - } - - int counterPosition = 0; - int patternStart = rowOffset; - for (int x = rowOffset; x < width; x++) { - bool pixel = row->get(x); - if (pixel ^ isWhite) { - counters[counterPosition]++; - } else { - if (counterPosition == patternLength - 1) { - if (patternMatchVariance(counters, countersCount, pattern, - MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { - *start = patternStart; - *end = x; - return true; - } - patternStart += counters[0] + counters[1]; - for (int y = 2; y < patternLength; y++) { - counters[y - 2] = counters[y]; - } - counters[patternLength - 2] = 0; - counters[patternLength - 1] = 0; - counterPosition--; - } else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - return false; - } - - bool UPCEANReader::decodeEnd(Ref row, int endStart, int* endGuardBegin, - int* endGuardEnd) { - return findGuardPattern(row, endStart, false, START_END_PATTERN, - sizeof(START_END_PATTERN) / sizeof(int), endGuardBegin, endGuardEnd); - } - - int UPCEANReader::decodeDigit(Ref row, int counters[], int countersLen, int rowOffset, - UPC_EAN_PATTERNS patternType) { - if (!recordPattern(row, rowOffset, counters, countersLen)) { - return -1; - } - unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept - int bestMatch = -1; - - int max = 0; - switch (patternType) { - case UPC_EAN_PATTERNS_L_PATTERNS: - max = L_PATTERNS_LEN; - for (int i = 0; i < max; i++) { - int pattern[countersLen]; - for(int j = 0; j< countersLen; j++){ - pattern[j] = L_PATTERNS[i][j]; - } - - unsigned int variance = patternMatchVariance(counters, countersLen, pattern, - MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } - } - break; - case UPC_EAN_PATTERNS_L_AND_G_PATTERNS: - max = L_AND_G_PATTERNS_LEN; - for (int i = 0; i < max; i++) { - int pattern[countersLen]; - for(int j = 0; j< countersLen; j++){ - pattern[j] = L_AND_G_PATTERNS[i][j]; - } - - unsigned int variance = patternMatchVariance(counters, countersLen, pattern, - MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } - } - break; - default: - break; - } - return bestMatch; - } - - /** - * @return {@link #checkStandardUPCEANChecksum(String)} - */ - bool UPCEANReader::checkChecksum(std::string s) { - return checkStandardUPCEANChecksum(s); - } - - /** - * Computes the UPC/EAN checksum on a string of digits, and reports - * whether the checksum is correct or not. - * - * @param s string of digits to check - * @return true iff string of digits passes the UPC/EAN checksum algorithm - */ - bool UPCEANReader::checkStandardUPCEANChecksum(std::string s) { - int length = s.length(); - if (length == 0) { - return false; - } - - int sum = 0; - for (int i = length - 2; i >= 0; i -= 2) { - int digit = (int) s[i] - (int) '0'; - if (digit < 0 || digit > 9) { - return false; - } - sum += digit; - } - sum *= 3; - for (int i = length - 1; i >= 0; i -= 2) { - int digit = (int) s[i] - (int) '0'; - if (digit < 0 || digit > 9) { - return false; - } - sum += digit; - } - return sum % 10 == 0; - } - - UPCEANReader::~UPCEANReader() { - } } + + Ref UPCEANReader::decodeRow(int rowNumber, Ref row, int startGuardBegin, + int startGuardEnd) { + std::string tmpResultString; + std::string& tmpResultStringRef = tmpResultString; + int endStart = decodeMiddle(row, startGuardBegin, startGuardEnd, tmpResultStringRef); + if (endStart < 0) { + return Ref(); + } + + int endGuardBegin; + int endGuardEnd; + if (!decodeEnd(row, endStart, &endGuardBegin, &endGuardEnd)) { + return Ref(); + } + + // Make sure there is a quiet zone at least as big as the end pattern after the barcode. + // The spec might want more whitespace, but in practice this is the maximum we can count on. + size_t quietEnd = endGuardEnd + (endGuardEnd - endGuardBegin); + if (quietEnd >= row->getSize() || !row->isRange(endGuardEnd, quietEnd, false)) { + return Ref(); + } + + if (!checkChecksum(tmpResultString)) { + return Ref(); + } + + Ref resultString(new String(tmpResultString)); + float left = (float) (startGuardBegin + startGuardEnd) / 2.0f; + float right = (float) (endGuardBegin + endGuardEnd) / 2.0f; + + std::vector< Ref > resultPoints(2); + Ref resultPoint1(new OneDResultPoint(left, (float) rowNumber)); + Ref resultPoint2(new OneDResultPoint(right, (float) rowNumber)); + resultPoints[0] = resultPoint1; + resultPoints[1] = resultPoint2; + + ArrayRef resultBytes(1); + return Ref(new Result(resultString, resultBytes, resultPoints, getBarcodeFormat())); + } + + bool UPCEANReader::findStartGuardPattern(Ref row, int* rangeStart, int* rangeEnd) { + int nextStart = 0; + while (findGuardPattern(row, nextStart, false, START_END_PATTERN, + sizeof(START_END_PATTERN) / sizeof(int), rangeStart, rangeEnd)) { + int start = *rangeStart; + nextStart = *rangeEnd; + // Make sure there is a quiet zone at least as big as the start pattern before the barcode. + // If this check would run off the left edge of the image, do not accept this barcode, + // as it is very likely to be a false positive. + int quietStart = start - (nextStart - start); + if (quietStart >= 0 && row->isRange(quietStart, start, false)) { + return true; + } + } + return false; + } + + bool UPCEANReader::findGuardPattern(Ref row, int rowOffset, bool whiteFirst, + const int pattern[], int patternLen, int* start, int* end) { + int patternLength = patternLen; + int counters[patternLength]; + int countersCount = sizeof(counters) / sizeof(int); + for (int i = 0; i < countersCount; i++) { + counters[i] = 0; + } + int width = row->getSize(); + bool isWhite = false; + while (rowOffset < width) { + isWhite = !row->get(rowOffset); + if (whiteFirst == isWhite) { + break; + } + rowOffset++; + } + + int counterPosition = 0; + int patternStart = rowOffset; + for (int x = rowOffset; x < width; x++) { + bool pixel = row->get(x); + if (pixel ^ isWhite) { + counters[counterPosition]++; + } else { + if (counterPosition == patternLength - 1) { + if (patternMatchVariance(counters, countersCount, pattern, + MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { + *start = patternStart; + *end = x; + return true; + } + patternStart += counters[0] + counters[1]; + for (int y = 2; y < patternLength; y++) { + counters[y - 2] = counters[y]; + } + counters[patternLength - 2] = 0; + counters[patternLength - 1] = 0; + counterPosition--; + } else { + counterPosition++; + } + counters[counterPosition] = 1; + isWhite = !isWhite; + } + } + return false; + } + + bool UPCEANReader::decodeEnd(Ref row, int endStart, int* endGuardBegin, + int* endGuardEnd) { + return findGuardPattern(row, endStart, false, START_END_PATTERN, + sizeof(START_END_PATTERN) / sizeof(int), endGuardBegin, endGuardEnd); + } + + int UPCEANReader::decodeDigit(Ref row, int counters[], int countersLen, int rowOffset, + UPC_EAN_PATTERNS patternType) { + if (!recordPattern(row, rowOffset, counters, countersLen)) { + return -1; + } + unsigned int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept + int bestMatch = -1; + + int max = 0; + switch (patternType) { + case UPC_EAN_PATTERNS_L_PATTERNS: + max = L_PATTERNS_LEN; + for (int i = 0; i < max; i++) { + int pattern[countersLen]; + for(int j = 0; j< countersLen; j++){ + pattern[j] = L_PATTERNS[i][j]; + } + + unsigned int variance = patternMatchVariance(counters, countersLen, pattern, + MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) { + bestVariance = variance; + bestMatch = i; + } + } + break; + case UPC_EAN_PATTERNS_L_AND_G_PATTERNS: + max = L_AND_G_PATTERNS_LEN; + for (int i = 0; i < max; i++) { + int pattern[countersLen]; + for(int j = 0; j< countersLen; j++){ + pattern[j] = L_AND_G_PATTERNS[i][j]; + } + + unsigned int variance = patternMatchVariance(counters, countersLen, pattern, + MAX_INDIVIDUAL_VARIANCE); + if (variance < bestVariance) { + bestVariance = variance; + bestMatch = i; + } + } + break; + default: + break; + } + return bestMatch; + } + + /** + * @return {@link #checkStandardUPCEANChecksum(String)} + */ + bool UPCEANReader::checkChecksum(std::string s) { + return checkStandardUPCEANChecksum(s); + } + + /** + * Computes the UPC/EAN checksum on a string of digits, and reports + * whether the checksum is correct or not. + * + * @param s string of digits to check + * @return true iff string of digits passes the UPC/EAN checksum algorithm + */ + bool UPCEANReader::checkStandardUPCEANChecksum(std::string s) { + int length = s.length(); + if (length == 0) { + return false; + } + + int sum = 0; + for (int i = length - 2; i >= 0; i -= 2) { + int digit = (int) s[i] - (int) '0'; + if (digit < 0 || digit > 9) { + return false; + } + sum += digit; + } + sum *= 3; + for (int i = length - 1; i >= 0; i -= 2) { + int digit = (int) s[i] - (int) '0'; + if (digit < 0 || digit > 9) { + return false; + } + sum += digit; + } + return sum % 10 == 0; + } + + UPCEANReader::~UPCEANReader() { + } + } } // file: zxing/oned/UPCEReader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * UPCEReader.cpp - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -6777,135 +8549,137 @@ namespace zxing { // #include namespace zxing { - namespace oned { - - /** - * The pattern that marks the middle, and end, of a UPC-E pattern. - * There is no "second half" to a UPC-E barcode. - */ - static const int MIDDLE_END_PATTERN[6] = {1, 1, 1, 1, 1, 1}; - - /** - * See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of - * even-odd parity encodings of digits that imply both the number system (0 or 1) - * used, and the check digit. - */ - static const int NUMSYS_AND_CHECK_DIGIT_PATTERNS[2][10] = { - {0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25}, - {0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A} - }; - - UPCEReader::UPCEReader() { - } - - int UPCEReader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, - std::string& resultString) { - const int countersLen = 4; - int counters[countersLen] = { 0, 0, 0, 0 }; - - int end = row->getSize(); - int rowOffset = startGuardEnd; - int lgPatternFound = 0; - - for (int x = 0; x < 6 && rowOffset < end; x++) { - int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, - UPC_EAN_PATTERNS_L_AND_G_PATTERNS); - if (bestMatch < 0) { - return -1; - } - resultString.append(1, (char) ('0' + bestMatch % 10)); - for (int i = 0; i < countersLen; i++) { - rowOffset += counters[i]; - } - if (bestMatch >= 10) { - lgPatternFound |= 1 << (5 - x); - } - } - - if (!determineNumSysAndCheckDigit(resultString, lgPatternFound)) { - return -1; - } - return rowOffset; - } - - bool UPCEReader::decodeEnd(Ref row, int endStart, int* endGuardBegin, - int* endGuardEnd) { - return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN, - sizeof(MIDDLE_END_PATTERN) / sizeof(int), endGuardBegin, endGuardEnd); - } - - bool UPCEReader::checkChecksum(std::string s){ - return UPCEANReader::checkChecksum(convertUPCEtoUPCA(s)); - } - - - bool UPCEReader::determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound) { - for (int numSys = 0; numSys <= 1; numSys++) { - for (int d = 0; d < 10; d++) { - if (lgPatternFound == NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) { - resultString.insert(0, 1, (char) ('0' + numSys)); - resultString.append(1, (char) ('0' + d)); - return true; - } - } - } - return false; - } - - /** - * Expands a UPC-E value back into its full, equivalent UPC-A code value. - * - * @param upce UPC-E code as string of digits - * @return equivalent UPC-A code as string of digits - */ - std::string UPCEReader::convertUPCEtoUPCA(std::string upce) { - std::string result; - result.append(1, upce[0]); - char lastChar = upce[6]; - switch (lastChar) { - case '0': - case '1': - case '2': - result.append(upce.substr(1,2)); - result.append(1, lastChar); - result.append("0000"); - result.append(upce.substr(3,3)); - break; - case '3': - result.append(upce.substr(1,3)); - result.append("00000"); - result.append(upce.substr(4,2)); - break; - case '4': - result.append(upce.substr(1,4)); - result.append("00000"); - result.append(1, upce[5]); - break; - default: - result.append(upce.substr(1,5)); - result.append("0000"); - result.append(1, lastChar); - break; - } - result.append(1, upce[7]); - return result; - } - - - BarcodeFormat UPCEReader::getBarcodeFormat() { - return BarcodeFormat_UPC_E; - } + namespace oned { + + /** + * The pattern that marks the middle, and end, of a UPC-E pattern. + * There is no "second half" to a UPC-E barcode. + */ + static const int MIDDLE_END_PATTERN[6] = {1, 1, 1, 1, 1, 1}; + + /** + * See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of + * even-odd parity encodings of digits that imply both the number system (0 or 1) + * used, and the check digit. + */ + static const int NUMSYS_AND_CHECK_DIGIT_PATTERNS[2][10] = { + {0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25}, + {0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A} + }; + + UPCEReader::UPCEReader() { } + + int UPCEReader::decodeMiddle(Ref row, int startGuardBegin, int startGuardEnd, + std::string& resultString) { + (void)startGuardBegin; + const int countersLen = 4; + int counters[countersLen] = { 0, 0, 0, 0 }; + + int end = row->getSize(); + int rowOffset = startGuardEnd; + int lgPatternFound = 0; + + for (int x = 0; x < 6 && rowOffset < end; x++) { + int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, + UPC_EAN_PATTERNS_L_AND_G_PATTERNS); + if (bestMatch < 0) { + return -1; + } + resultString.append(1, (char) ('0' + bestMatch % 10)); + for (int i = 0; i < countersLen; i++) { + rowOffset += counters[i]; + } + if (bestMatch >= 10) { + lgPatternFound |= 1 << (5 - x); + } + } + + if (!determineNumSysAndCheckDigit(resultString, lgPatternFound)) { + return -1; + } + return rowOffset; + } + + bool UPCEReader::decodeEnd(Ref row, int endStart, int* endGuardBegin, + int* endGuardEnd) { + return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN, + sizeof(MIDDLE_END_PATTERN) / sizeof(int), endGuardBegin, endGuardEnd); + } + + bool UPCEReader::checkChecksum(std::string s){ + return UPCEANReader::checkChecksum(convertUPCEtoUPCA(s)); + } + + + bool UPCEReader::determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound) { + for (int numSys = 0; numSys <= 1; numSys++) { + for (int d = 0; d < 10; d++) { + if (lgPatternFound == NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) { + resultString.insert(0, 1, (char) ('0' + numSys)); + resultString.append(1, (char) ('0' + d)); + return true; + } + } + } + return false; + } + + /** + * Expands a UPC-E value back into its full, equivalent UPC-A code value. + * + * @param upce UPC-E code as string of digits + * @return equivalent UPC-A code as string of digits + */ + std::string UPCEReader::convertUPCEtoUPCA(std::string upce) { + std::string result; + result.append(1, upce[0]); + char lastChar = upce[6]; + switch (lastChar) { + case '0': + case '1': + case '2': + result.append(upce.substr(1,2)); + result.append(1, lastChar); + result.append("0000"); + result.append(upce.substr(3,3)); + break; + case '3': + result.append(upce.substr(1,3)); + result.append("00000"); + result.append(upce.substr(4,2)); + break; + case '4': + result.append(upce.substr(1,4)); + result.append("00000"); + result.append(1, upce[5]); + break; + default: + result.append(upce.substr(1,5)); + result.append("0000"); + result.append(1, lastChar); + break; + } + result.append(1, upce[7]); + return result; + } + + + BarcodeFormat UPCEReader::getBarcodeFormat() { + return BarcodeFormat_UPC_E; + } + } } // file: zxing/qrcode/ErrorCorrectionLevel.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * ErrorCorrectionLevel.cpp * zxing * * Created by Christian Brunschen on 15/05/2008. - * Copyright 2008 ZXing authors All rights reserved. + * Copyright 2008-2011 ZXing authors 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. @@ -6922,32 +8696,47 @@ namespace zxing { // #include +using std::string; + namespace zxing { - namespace qrcode { - - ErrorCorrectionLevel::ErrorCorrectionLevel(int inOrdinal) : - ordinal_(inOrdinal) { - } - - int ErrorCorrectionLevel::ordinal() { - return ordinal_; - } - - ErrorCorrectionLevel& ErrorCorrectionLevel::forBits(int bits) { - if (bits < 0 || bits >= N_LEVELS) { - throw ReaderException("Ellegal error correction level bits"); - } - return *FOR_BITS[bits]; - } - - ErrorCorrectionLevel ErrorCorrectionLevel::L(0); - ErrorCorrectionLevel ErrorCorrectionLevel::M(1); - ErrorCorrectionLevel ErrorCorrectionLevel::Q(2); - ErrorCorrectionLevel ErrorCorrectionLevel::H(3); - ErrorCorrectionLevel *ErrorCorrectionLevel::FOR_BITS[] = { &M, &L, &H, &Q }; - int ErrorCorrectionLevel::N_LEVELS = 4; - - } +namespace qrcode { + +ErrorCorrectionLevel::ErrorCorrectionLevel(int inOrdinal, + int bits, + char const* name) : + ordinal_(inOrdinal), bits_(bits), name_(name) {} + +int ErrorCorrectionLevel::ordinal() const { + return ordinal_; +} + +int ErrorCorrectionLevel::bits() const { + return bits_; +} + +string const& ErrorCorrectionLevel::name() const { + return name_; +} + +ErrorCorrectionLevel::operator string const& () const { + return name_; +} + +ErrorCorrectionLevel& ErrorCorrectionLevel::forBits(int bits) { + if (bits < 0 || bits >= N_LEVELS) { + throw ReaderException("Ellegal error correction level bits"); + } + return *FOR_BITS[bits]; +} + + ErrorCorrectionLevel ErrorCorrectionLevel::L(0, 0x01, "L"); + ErrorCorrectionLevel ErrorCorrectionLevel::M(1, 0x00, "M"); + ErrorCorrectionLevel ErrorCorrectionLevel::Q(2, 0x03, "Q"); + ErrorCorrectionLevel ErrorCorrectionLevel::H(3, 0x02, "H"); +ErrorCorrectionLevel *ErrorCorrectionLevel::FOR_BITS[] = { &M, &L, &H, &Q }; +int ErrorCorrectionLevel::N_LEVELS = 4; + +} } // file: zxing/qrcode/FormatInformation.cpp @@ -6976,103 +8765,104 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - int FormatInformation::FORMAT_INFO_MASK_QR = 0x5412; - int FormatInformation::FORMAT_INFO_DECODE_LOOKUP[][2] = { { 0x5412, 0x00 }, { 0x5125, 0x01 }, { 0x5E7C, 0x02 }, { - 0x5B4B, 0x03 }, { 0x45F9, 0x04 }, { 0x40CE, 0x05 }, { 0x4F97, 0x06 }, { 0x4AA0, 0x07 }, { 0x77C4, 0x08 }, { - 0x72F3, 0x09 }, { 0x7DAA, 0x0A }, { 0x789D, 0x0B }, { 0x662F, 0x0C }, { 0x6318, 0x0D }, { 0x6C41, 0x0E }, { - 0x6976, 0x0F }, { 0x1689, 0x10 }, { 0x13BE, 0x11 }, { 0x1CE7, 0x12 }, { 0x19D0, 0x13 }, { 0x0762, 0x14 }, { - 0x0255, 0x15 }, { 0x0D0C, 0x16 }, { 0x083B, 0x17 }, { 0x355F, 0x18 }, { 0x3068, 0x19 }, { 0x3F31, 0x1A }, { - 0x3A06, 0x1B }, { 0x24B4, 0x1C }, { 0x2183, 0x1D }, { 0x2EDA, 0x1E }, { 0x2BED, 0x1F }, - }; - int FormatInformation::N_FORMAT_INFO_DECODE_LOOKUPS = 32; - - int FormatInformation::BITS_SET_IN_HALF_BYTE[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; - - FormatInformation::FormatInformation(int formatInfo) : - errorCorrectionLevel_(ErrorCorrectionLevel::forBits((formatInfo >> 3) & 0x03)), dataMask_( - (unsigned char)(formatInfo & 0x07)) { - } - - ErrorCorrectionLevel& FormatInformation::getErrorCorrectionLevel() { - return errorCorrectionLevel_; - } - - unsigned char FormatInformation::getDataMask() { - return dataMask_; - } - - int FormatInformation::numBitsDiffering(unsigned int a, unsigned int b) { - a ^= b; - return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(a >> 4 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 8 - & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 12 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 16 & 0x0F)] - + BITS_SET_IN_HALF_BYTE[(a >> 20 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 24 & 0x0F)] - + BITS_SET_IN_HALF_BYTE[(a >> 28 & 0x0F)]; - } - - Ref FormatInformation::decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { - Ref result(doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2)); - if (result != 0) { - return result; - } - // Should return null, but, some QR codes apparently - // do not mask this info. Try again by actually masking the pattern - // first - return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR, - maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR); - } - Ref FormatInformation::doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { - // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing - int bestDifference = numeric_limits::max(); - int bestFormatInfo = 0; - for (int i = 0; i < N_FORMAT_INFO_DECODE_LOOKUPS; i++) { - int* decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i]; - int targetInfo = decodeInfo[0]; - if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) { - // Found an exact match - Ref result(new FormatInformation(decodeInfo[1])); - return result; - } - int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo); - if (bitsDifference < bestDifference) { - bestFormatInfo = decodeInfo[1]; - bestDifference = bitsDifference; - } - if (maskedFormatInfo1 != maskedFormatInfo2) { - // also try the other option - bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo); - if (bitsDifference < bestDifference) { - bestFormatInfo = decodeInfo[1]; - bestDifference = bitsDifference; - } - } - } - if (bestDifference <= 3) { - Ref result(new FormatInformation(bestFormatInfo)); - return result; - } - Ref result; - return result; - } - - bool operator==(const FormatInformation &a, const FormatInformation &b) { - return &(a.errorCorrectionLevel_) == &(b.errorCorrectionLevel_) && a.dataMask_ == b.dataMask_; - } - - ostream& operator<<(ostream& out, const FormatInformation& fi) { - const FormatInformation *fip = &fi; - out << "FormatInformation @ " << fip; - return out; - } - +namespace qrcode { + +using namespace std; + +int FormatInformation::FORMAT_INFO_MASK_QR = 0x5412; +int FormatInformation::FORMAT_INFO_DECODE_LOOKUP[][2] = { { 0x5412, 0x00 }, { 0x5125, 0x01 }, { 0x5E7C, 0x02 }, { + 0x5B4B, 0x03 }, { 0x45F9, 0x04 }, { 0x40CE, 0x05 }, { 0x4F97, 0x06 }, { 0x4AA0, 0x07 }, { 0x77C4, 0x08 }, { + 0x72F3, 0x09 }, { 0x7DAA, 0x0A }, { 0x789D, 0x0B }, { 0x662F, 0x0C }, { 0x6318, 0x0D }, { 0x6C41, 0x0E }, { + 0x6976, 0x0F }, { 0x1689, 0x10 }, { 0x13BE, 0x11 }, { 0x1CE7, 0x12 }, { 0x19D0, 0x13 }, { 0x0762, 0x14 }, { + 0x0255, 0x15 }, { 0x0D0C, 0x16 }, { 0x083B, 0x17 }, { 0x355F, 0x18 }, { 0x3068, 0x19 }, { 0x3F31, 0x1A }, { + 0x3A06, 0x1B }, { 0x24B4, 0x1C }, { 0x2183, 0x1D }, { 0x2EDA, 0x1E }, { 0x2BED, 0x1F }, +}; +int FormatInformation::N_FORMAT_INFO_DECODE_LOOKUPS = 32; + +int FormatInformation::BITS_SET_IN_HALF_BYTE[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; + +FormatInformation::FormatInformation(int formatInfo) : + errorCorrectionLevel_(ErrorCorrectionLevel::forBits((formatInfo >> 3) & 0x03)), dataMask_( + (unsigned char)(formatInfo & 0x07)) { +} + +ErrorCorrectionLevel& FormatInformation::getErrorCorrectionLevel() { + return errorCorrectionLevel_; +} + +unsigned char FormatInformation::getDataMask() { + return dataMask_; +} + +int FormatInformation::numBitsDiffering(unsigned int a, unsigned int b) { + a ^= b; + return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(a >> 4 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 8 + & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 12 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 16 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >> 20 & 0x0F)] + BITS_SET_IN_HALF_BYTE[(a >> 24 & 0x0F)] + + BITS_SET_IN_HALF_BYTE[(a >> 28 & 0x0F)]; +} + +Ref FormatInformation::decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { + Ref result(doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2)); + if (result != 0) { + return result; + } + // Should return null, but, some QR codes apparently + // do not mask this info. Try again by actually masking the pattern + // first + return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR, + maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR); +} +Ref FormatInformation::doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { + // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing + int bestDifference = numeric_limits::max(); + int bestFormatInfo = 0; + for (int i = 0; i < N_FORMAT_INFO_DECODE_LOOKUPS; i++) { + int* decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i]; + int targetInfo = decodeInfo[0]; + if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) { + // Found an exact match + Ref result(new FormatInformation(decodeInfo[1])); + return result; } + int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo); + if (bitsDifference < bestDifference) { + bestFormatInfo = decodeInfo[1]; + bestDifference = bitsDifference; + } + if (maskedFormatInfo1 != maskedFormatInfo2) { + // also try the other option + bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo); + if (bitsDifference < bestDifference) { + bestFormatInfo = decodeInfo[1]; + bestDifference = bitsDifference; + } + } + } + if (bestDifference <= 3) { + Ref result(new FormatInformation(bestFormatInfo)); + return result; + } + Ref result; + return result; +} + +bool operator==(const FormatInformation &a, const FormatInformation &b) { + return &(a.errorCorrectionLevel_) == &(b.errorCorrectionLevel_) && a.dataMask_ == b.dataMask_; +} + +ostream& operator<<(ostream& out, const FormatInformation& fi) { + const FormatInformation *fip = &fi; + out << "FormatInformation @ " << fip; + return out; +} + +} } // file: zxing/qrcode/QRCodeReader.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * QRCodeReader.cpp * zxing @@ -7100,9 +8890,9 @@ namespace zxing { namespace zxing { namespace qrcode { - + using namespace std; - + QRCodeReader::QRCodeReader() :decoder_() { } //TODO: see if any of the other files in the qrcode tree need tryHarder @@ -7110,50 +8900,52 @@ namespace zxing { #ifdef DEBUG cout << "decoding image " << image.object_ << ":\n" << flush; #endif - + Detector detector(image->getBlackMatrix()); - - + + #ifdef DEBUG cout << "(1) created detector " << &detector << "\n" << flush; #endif - + Ref detectorResult(detector.detect(hints)); #ifdef DEBUG cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush; #endif - + std::vector > points(detectorResult->getPoints()); - - + + #ifdef DEBUG cout << "(3) extracted points " << &points << "\n" << flush; - /*cout << "found " << points->size() << " points:\n"; - for (size_t i = 0; i < points->size(); i++) { - cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n"; - } - */ + cout << "found " << points.size() << " points:\n"; + for (size_t i = 0; i < points.size(); i++) { + cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n"; + } cout << "bits:\n"; cout << *(detectorResult->getBits()) << "\n"; #endif - + Ref decoderResult(decoder_.decode(detectorResult->getBits())); #ifdef DEBUG cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush; #endif - + Ref result( new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_QR_CODE)); #ifdef DEBUG cout << "(5) created result " << result.object_ << ", returning\n" << flush; #endif - + return result; } - + QRCodeReader::~QRCodeReader() { } - + + Decoder& QRCodeReader::getDecoder() { + return decoder_; + } } } @@ -7186,535 +8978,535 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - using namespace std; - - ECB::ECB(int count, int dataCodewords) : - count_(count), dataCodewords_(dataCodewords) { - } - - int ECB::getCount() { - return count_; - } - - int ECB::getDataCodewords() { - return dataCodewords_; - } - - ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) : - ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) { - } - - ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) : - ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) { - ecBlocks_.push_back(ecBlocks2); - } - - int ECBlocks::getECCodewords() { - return ecCodewords_; - } - - std::vector& ECBlocks::getECBlocks() { - return ecBlocks_; - } - - ECBlocks::~ECBlocks() { - for (size_t i = 0; i < ecBlocks_.size(); i++) { - delete ecBlocks_[i]; - } - } - - unsigned int Version::VERSION_DECODE_INFO[] = { 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, - 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, - 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x2542E, 0x26A64, - 0x27541, 0x28C69 - }; - int Version::N_VERSION_DECODE_INFOS = 34; - vector > Version::VERSIONS; - static int N_VERSIONS = Version::buildVersions(); - - int Version::getVersionNumber() { - return versionNumber_; - } - - vector &Version::getAlignmentPatternCenters() { - return alignmentPatternCenters_; - } - - int Version::getTotalCodewords() { - return totalCodewords_; - } - - int Version::getDimensionForVersion() { - return 17 + 4 * versionNumber_; - } - - ECBlocks& Version::getECBlocksForLevel(ErrorCorrectionLevel &ecLevel) { - return *ecBlocks_[ecLevel.ordinal()]; - } - - Version *Version::getProvisionalVersionForDimension(int dimension) { - if (dimension % 4 != 1) { - throw ReaderException("Dimension must be 1 mod 4"); - } - return Version::getVersionForNumber((dimension - 17) >> 2); - } - - Version *Version::getVersionForNumber(int versionNumber) { - if (versionNumber < 1 || versionNumber > N_VERSIONS) { - throw ReaderException("versionNumber must be between 1 and 40"); - } - - return VERSIONS[versionNumber - 1]; - } - - Version::Version(int versionNumber, vector *alignmentPatternCenters, ECBlocks *ecBlocks1, ECBlocks *ecBlocks2, - ECBlocks *ecBlocks3, ECBlocks *ecBlocks4) : - versionNumber_(versionNumber), alignmentPatternCenters_(*alignmentPatternCenters), ecBlocks_(4), totalCodewords_(0) { - ecBlocks_[0] = ecBlocks1; - ecBlocks_[1] = ecBlocks2; - ecBlocks_[2] = ecBlocks3; - ecBlocks_[3] = ecBlocks4; - - int total = 0; - int ecCodewords = ecBlocks1->getECCodewords(); - vector &ecbArray = ecBlocks1->getECBlocks(); - for (size_t i = 0; i < ecbArray.size(); i++) { - ECB *ecBlock = ecbArray[i]; - total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords); - } - totalCodewords_ = total; - } - - Version::~Version() { - delete &alignmentPatternCenters_; - for (size_t i = 0; i < ecBlocks_.size(); i++) { - delete ecBlocks_[i]; - } - } - - Version *Version::decodeVersionInformation(unsigned int versionBits) { - int bestDifference = numeric_limits::max(); - size_t bestVersion = 0; - for (int i = 0; i < N_VERSION_DECODE_INFOS; i++) { - unsigned targetVersion = VERSION_DECODE_INFO[i]; - // Do the version info bits match exactly? done. - if (targetVersion == versionBits) { - return getVersionForNumber(i + 7); - } - // Otherwise see if this is the closest to a real version info bit - // string we have seen so far - int bitsDifference = FormatInformation::numBitsDiffering(versionBits, targetVersion); - if (bitsDifference < bestDifference) { - bestVersion = i + 7; - bestDifference = bitsDifference; - } - } - // We can tolerate up to 3 bits of error since no two version info codewords will - // differ in less than 4 bits. - if (bestDifference <= 3) { - return getVersionForNumber(bestVersion); - } - // If we didn't find a close enough match, fail - return 0; - } - - Ref Version::buildFunctionPattern() { - int dimension = getDimensionForVersion(); - Ref functionPattern(new BitMatrix(dimension)); - - - // Top left finder pattern + separator + format - functionPattern->setRegion(0, 0, 9, 9); - // Top right finder pattern + separator + format - functionPattern->setRegion(dimension - 8, 0, 8, 9); - // Bottom left finder pattern + separator + format - functionPattern->setRegion(0, dimension - 8, 9, 8); - - - // Alignment patterns - size_t max = alignmentPatternCenters_.size(); - for (size_t x = 0; x < max; x++) { - int i = alignmentPatternCenters_[x] - 2; - for (size_t y = 0; y < max; y++) { - if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) { - // No alignment patterns near the three finder patterns - continue; - } - functionPattern->setRegion(alignmentPatternCenters_[y] - 2, i, 5, 5); - } - } - - // Vertical timing pattern - functionPattern->setRegion(6, 9, 1, dimension - 17); - // Horizontal timing pattern - functionPattern->setRegion(9, 6, dimension - 17, 1); - - if (versionNumber_ > 6) { - // Version info, top right - functionPattern->setRegion(dimension - 11, 0, 3, 6); - // Version info, bottom left - functionPattern->setRegion(0, dimension - 11, 6, 3); - } - - - //#ifdef DEBUG - // cout << "version " << versionNumber_ << " built function pattern:\n"; - // cout << *functionPattern; - //#endif - - return functionPattern; - } - - static vector *intArray(size_t n...) { - va_list ap; - va_start(ap, n); - vector *result = new vector(n); - for (size_t i = 0; i < n; i++) { - (*result)[i] = va_arg(ap, int); - } - va_end(ap); - return result; - } - - int Version::buildVersions() { - VERSIONS.push_back(Ref(new Version(1, intArray(0), - new ECBlocks(7, new ECB(1, 19)), - new ECBlocks(10, new ECB(1, 16)), - new ECBlocks(13, new ECB(1, 13)), - new ECBlocks(17, new ECB(1, 9))))); - VERSIONS.push_back(Ref(new Version(2, intArray(2, 6, 18), - new ECBlocks(10, new ECB(1, 34)), - new ECBlocks(16, new ECB(1, 28)), - new ECBlocks(22, new ECB(1, 22)), - new ECBlocks(28, new ECB(1, 16))))); - VERSIONS.push_back(Ref(new Version(3, intArray(2, 6, 22), - new ECBlocks(15, new ECB(1, 55)), - new ECBlocks(26, new ECB(1, 44)), - new ECBlocks(18, new ECB(2, 17)), - new ECBlocks(22, new ECB(2, 13))))); - VERSIONS.push_back(Ref(new Version(4, intArray(2, 6, 26), - new ECBlocks(20, new ECB(1, 80)), - new ECBlocks(18, new ECB(2, 32)), - new ECBlocks(26, new ECB(2, 24)), - new ECBlocks(16, new ECB(4, 9))))); - VERSIONS.push_back(Ref(new Version(5, intArray(2, 6, 30), - new ECBlocks(26, new ECB(1, 108)), - new ECBlocks(24, new ECB(2, 43)), - new ECBlocks(18, new ECB(2, 15), - new ECB(2, 16)), - new ECBlocks(22, new ECB(2, 11), - new ECB(2, 12))))); - VERSIONS.push_back(Ref(new Version(6, intArray(2, 6, 34), - new ECBlocks(18, new ECB(2, 68)), - new ECBlocks(16, new ECB(4, 27)), - new ECBlocks(24, new ECB(4, 19)), - new ECBlocks(28, new ECB(4, 15))))); - VERSIONS.push_back(Ref(new Version(7, intArray(3, 6, 22, 38), - new ECBlocks(20, new ECB(2, 78)), - new ECBlocks(18, new ECB(4, 31)), - new ECBlocks(18, new ECB(2, 14), - new ECB(4, 15)), - new ECBlocks(26, new ECB(4, 13), - new ECB(1, 14))))); - VERSIONS.push_back(Ref(new Version(8, intArray(3, 6, 24, 42), - new ECBlocks(24, new ECB(2, 97)), - new ECBlocks(22, new ECB(2, 38), - new ECB(2, 39)), - new ECBlocks(22, new ECB(4, 18), - new ECB(2, 19)), - new ECBlocks(26, new ECB(4, 14), - new ECB(2, 15))))); - VERSIONS.push_back(Ref(new Version(9, intArray(3, 6, 26, 46), - new ECBlocks(30, new ECB(2, 116)), - new ECBlocks(22, new ECB(3, 36), - new ECB(2, 37)), - new ECBlocks(20, new ECB(4, 16), - new ECB(4, 17)), - new ECBlocks(24, new ECB(4, 12), - new ECB(4, 13))))); - VERSIONS.push_back(Ref(new Version(10, intArray(3, 6, 28, 50), - new ECBlocks(18, new ECB(2, 68), - new ECB(2, 69)), - new ECBlocks(26, new ECB(4, 43), - new ECB(1, 44)), - new ECBlocks(24, new ECB(6, 19), - new ECB(2, 20)), - new ECBlocks(28, new ECB(6, 15), - new ECB(2, 16))))); - VERSIONS.push_back(Ref(new Version(11, intArray(3, 6, 30, 54), - new ECBlocks(20, new ECB(4, 81)), - new ECBlocks(30, new ECB(1, 50), - new ECB(4, 51)), - new ECBlocks(28, new ECB(4, 22), - new ECB(4, 23)), - new ECBlocks(24, new ECB(3, 12), - new ECB(8, 13))))); - VERSIONS.push_back(Ref(new Version(12, intArray(3, 6, 32, 58), - new ECBlocks(24, new ECB(2, 92), - new ECB(2, 93)), - new ECBlocks(22, new ECB(6, 36), - new ECB(2, 37)), - new ECBlocks(26, new ECB(4, 20), - new ECB(6, 21)), - new ECBlocks(28, new ECB(7, 14), - new ECB(4, 15))))); - VERSIONS.push_back(Ref(new Version(13, intArray(3, 6, 34, 62), - new ECBlocks(26, new ECB(4, 107)), - new ECBlocks(22, new ECB(8, 37), - new ECB(1, 38)), - new ECBlocks(24, new ECB(8, 20), - new ECB(4, 21)), - new ECBlocks(22, new ECB(12, 11), - new ECB(4, 12))))); - VERSIONS.push_back(Ref(new Version(14, intArray(4, 6, 26, 46, 66), - new ECBlocks(30, new ECB(3, 115), - new ECB(1, 116)), - new ECBlocks(24, new ECB(4, 40), - new ECB(5, 41)), - new ECBlocks(20, new ECB(11, 16), - new ECB(5, 17)), - new ECBlocks(24, new ECB(11, 12), - new ECB(5, 13))))); - VERSIONS.push_back(Ref(new Version(15, intArray(4, 6, 26, 48, 70), - new ECBlocks(22, new ECB(5, 87), - new ECB(1, 88)), - new ECBlocks(24, new ECB(5, 41), - new ECB(5, 42)), - new ECBlocks(30, new ECB(5, 24), - new ECB(7, 25)), - new ECBlocks(24, new ECB(11, 12), - new ECB(7, 13))))); - VERSIONS.push_back(Ref(new Version(16, intArray(4, 6, 26, 50, 74), - new ECBlocks(24, new ECB(5, 98), - new ECB(1, 99)), - new ECBlocks(28, new ECB(7, 45), - new ECB(3, 46)), - new ECBlocks(24, new ECB(15, 19), - new ECB(2, 20)), - new ECBlocks(30, new ECB(3, 15), - new ECB(13, 16))))); - VERSIONS.push_back(Ref(new Version(17, intArray(4, 6, 30, 54, 78), - new ECBlocks(28, new ECB(1, 107), - new ECB(5, 108)), - new ECBlocks(28, new ECB(10, 46), - new ECB(1, 47)), - new ECBlocks(28, new ECB(1, 22), - new ECB(15, 23)), - new ECBlocks(28, new ECB(2, 14), - new ECB(17, 15))))); - VERSIONS.push_back(Ref(new Version(18, intArray(4, 6, 30, 56, 82), - new ECBlocks(30, new ECB(5, 120), - new ECB(1, 121)), - new ECBlocks(26, new ECB(9, 43), - new ECB(4, 44)), - new ECBlocks(28, new ECB(17, 22), - new ECB(1, 23)), - new ECBlocks(28, new ECB(2, 14), - new ECB(19, 15))))); - VERSIONS.push_back(Ref(new Version(19, intArray(4, 6, 30, 58, 86), - new ECBlocks(28, new ECB(3, 113), - new ECB(4, 114)), - new ECBlocks(26, new ECB(3, 44), - new ECB(11, 45)), - new ECBlocks(26, new ECB(17, 21), - new ECB(4, 22)), - new ECBlocks(26, new ECB(9, 13), - new ECB(16, 14))))); - VERSIONS.push_back(Ref(new Version(20, intArray(4, 6, 34, 62, 90), - new ECBlocks(28, new ECB(3, 107), - new ECB(5, 108)), - new ECBlocks(26, new ECB(3, 41), - new ECB(13, 42)), - new ECBlocks(30, new ECB(15, 24), - new ECB(5, 25)), - new ECBlocks(28, new ECB(15, 15), - new ECB(10, 16))))); - VERSIONS.push_back(Ref(new Version(21, intArray(5, 6, 28, 50, 72, 94), - new ECBlocks(28, new ECB(4, 116), - new ECB(4, 117)), - new ECBlocks(26, new ECB(17, 42)), - new ECBlocks(28, new ECB(17, 22), - new ECB(6, 23)), - new ECBlocks(30, new ECB(19, 16), - new ECB(6, 17))))); - VERSIONS.push_back(Ref(new Version(22, intArray(5, 6, 26, 50, 74, 98), - new ECBlocks(28, new ECB(2, 111), - new ECB(7, 112)), - new ECBlocks(28, new ECB(17, 46)), - new ECBlocks(30, new ECB(7, 24), - new ECB(16, 25)), - new ECBlocks(24, new ECB(34, 13))))); - VERSIONS.push_back(Ref(new Version(23, intArray(5, 6, 30, 54, 78, 102), - new ECBlocks(30, new ECB(4, 121), - new ECB(5, 122)), - new ECBlocks(28, new ECB(4, 47), - new ECB(14, 48)), - new ECBlocks(30, new ECB(11, 24), - new ECB(14, 25)), - new ECBlocks(30, new ECB(16, 15), - new ECB(14, 16))))); - VERSIONS.push_back(Ref(new Version(24, intArray(5, 6, 28, 54, 80, 106), - new ECBlocks(30, new ECB(6, 117), - new ECB(4, 118)), - new ECBlocks(28, new ECB(6, 45), - new ECB(14, 46)), - new ECBlocks(30, new ECB(11, 24), - new ECB(16, 25)), - new ECBlocks(30, new ECB(30, 16), - new ECB(2, 17))))); - VERSIONS.push_back(Ref(new Version(25, intArray(5, 6, 32, 58, 84, 110), - new ECBlocks(26, new ECB(8, 106), - new ECB(4, 107)), - new ECBlocks(28, new ECB(8, 47), - new ECB(13, 48)), - new ECBlocks(30, new ECB(7, 24), - new ECB(22, 25)), - new ECBlocks(30, new ECB(22, 15), - new ECB(13, 16))))); - VERSIONS.push_back(Ref(new Version(26, intArray(5, 6, 30, 58, 86, 114), - new ECBlocks(28, new ECB(10, 114), - new ECB(2, 115)), - new ECBlocks(28, new ECB(19, 46), - new ECB(4, 47)), - new ECBlocks(28, new ECB(28, 22), - new ECB(6, 23)), - new ECBlocks(30, new ECB(33, 16), - new ECB(4, 17))))); - VERSIONS.push_back(Ref(new Version(27, intArray(5, 6, 34, 62, 90, 118), - new ECBlocks(30, new ECB(8, 122), - new ECB(4, 123)), - new ECBlocks(28, new ECB(22, 45), - new ECB(3, 46)), - new ECBlocks(30, new ECB(8, 23), - new ECB(26, 24)), - new ECBlocks(30, new ECB(12, 15), - new ECB(28, 16))))); - VERSIONS.push_back(Ref(new Version(28, intArray(6, 6, 26, 50, 74, 98, 122), - new ECBlocks(30, new ECB(3, 117), - new ECB(10, 118)), - new ECBlocks(28, new ECB(3, 45), - new ECB(23, 46)), - new ECBlocks(30, new ECB(4, 24), - new ECB(31, 25)), - new ECBlocks(30, new ECB(11, 15), - new ECB(31, 16))))); - VERSIONS.push_back(Ref(new Version(29, intArray(6, 6, 30, 54, 78, 102, 126), - new ECBlocks(30, new ECB(7, 116), - new ECB(7, 117)), - new ECBlocks(28, new ECB(21, 45), - new ECB(7, 46)), - new ECBlocks(30, new ECB(1, 23), - new ECB(37, 24)), - new ECBlocks(30, new ECB(19, 15), - new ECB(26, 16))))); - VERSIONS.push_back(Ref(new Version(30, intArray(6, 6, 26, 52, 78, 104, 130), - new ECBlocks(30, new ECB(5, 115), - new ECB(10, 116)), - new ECBlocks(28, new ECB(19, 47), - new ECB(10, 48)), - new ECBlocks(30, new ECB(15, 24), - new ECB(25, 25)), - new ECBlocks(30, new ECB(23, 15), - new ECB(25, 16))))); - VERSIONS.push_back(Ref(new Version(31, intArray(6, 6, 30, 56, 82, 108, 134), - new ECBlocks(30, new ECB(13, 115), - new ECB(3, 116)), - new ECBlocks(28, new ECB(2, 46), - new ECB(29, 47)), - new ECBlocks(30, new ECB(42, 24), - new ECB(1, 25)), - new ECBlocks(30, new ECB(23, 15), - new ECB(28, 16))))); - VERSIONS.push_back(Ref(new Version(32, intArray(6, 6, 34, 60, 86, 112, 138), - new ECBlocks(30, new ECB(17, 115)), - new ECBlocks(28, new ECB(10, 46), - new ECB(23, 47)), - new ECBlocks(30, new ECB(10, 24), - new ECB(35, 25)), - new ECBlocks(30, new ECB(19, 15), - new ECB(35, 16))))); - VERSIONS.push_back(Ref(new Version(33, intArray(6, 6, 30, 58, 86, 114, 142), - new ECBlocks(30, new ECB(17, 115), - new ECB(1, 116)), - new ECBlocks(28, new ECB(14, 46), - new ECB(21, 47)), - new ECBlocks(30, new ECB(29, 24), - new ECB(19, 25)), - new ECBlocks(30, new ECB(11, 15), - new ECB(46, 16))))); - VERSIONS.push_back(Ref(new Version(34, intArray(6, 6, 34, 62, 90, 118, 146), - new ECBlocks(30, new ECB(13, 115), - new ECB(6, 116)), - new ECBlocks(28, new ECB(14, 46), - new ECB(23, 47)), - new ECBlocks(30, new ECB(44, 24), - new ECB(7, 25)), - new ECBlocks(30, new ECB(59, 16), - new ECB(1, 17))))); - VERSIONS.push_back(Ref(new Version(35, intArray(7, 6, 30, 54, 78, - 102, 126, 150), - new ECBlocks(30, new ECB(12, 121), - new ECB(7, 122)), - new ECBlocks(28, new ECB(12, 47), - new ECB(26, 48)), - new ECBlocks(30, new ECB(39, 24), - new ECB(14, 25)), - new ECBlocks(30, new ECB(22, 15), - new ECB(41, 16))))); - VERSIONS.push_back(Ref(new Version(36, intArray(7, 6, 24, 50, 76, - 102, 128, 154), - new ECBlocks(30, new ECB(6, 121), - new ECB(14, 122)), - new ECBlocks(28, new ECB(6, 47), - new ECB(34, 48)), - new ECBlocks(30, new ECB(46, 24), - new ECB(10, 25)), - new ECBlocks(30, new ECB(2, 15), - new ECB(64, 16))))); - VERSIONS.push_back(Ref(new Version(37, intArray(7, 6, 28, 54, 80, - 106, 132, 158), - new ECBlocks(30, new ECB(17, 122), - new ECB(4, 123)), - new ECBlocks(28, new ECB(29, 46), - new ECB(14, 47)), - new ECBlocks(30, new ECB(49, 24), - new ECB(10, 25)), - new ECBlocks(30, new ECB(24, 15), - new ECB(46, 16))))); - VERSIONS.push_back(Ref(new Version(38, intArray(7, 6, 32, 58, 84, - 110, 136, 162), - new ECBlocks(30, new ECB(4, 122), - new ECB(18, 123)), - new ECBlocks(28, new ECB(13, 46), - new ECB(32, 47)), - new ECBlocks(30, new ECB(48, 24), - new ECB(14, 25)), - new ECBlocks(30, new ECB(42, 15), - new ECB(32, 16))))); - VERSIONS.push_back(Ref(new Version(39, intArray(7, 6, 26, 54, 82, - 110, 138, 166), - new ECBlocks(30, new ECB(20, 117), - new ECB(4, 118)), - new ECBlocks(28, new ECB(40, 47), - new ECB(7, 48)), - new ECBlocks(30, new ECB(43, 24), - new ECB(22, 25)), - new ECBlocks(30, new ECB(10, 15), - new ECB(67, 16))))); - VERSIONS.push_back(Ref(new Version(40, intArray(7, 6, 30, 58, 86, - 114, 142, 170), - new ECBlocks(30, new ECB(19, 118), - new ECB(6, 119)), - new ECBlocks(28, new ECB(18, 47), - new ECB(31, 48)), - new ECBlocks(30, new ECB(34, 24), - new ECB(34, 25)), - new ECBlocks(30, new ECB(20, 15), - new ECB(61, 16))))); - return VERSIONS.size(); - } +namespace qrcode { +using namespace std; + +ECB::ECB(int count, int dataCodewords) : + count_(count), dataCodewords_(dataCodewords) { +} + +int ECB::getCount() { + return count_; +} + +int ECB::getDataCodewords() { + return dataCodewords_; +} + +ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) : + ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks) { +} + +ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) : + ecCodewords_(ecCodewords), ecBlocks_(1, ecBlocks1) { + ecBlocks_.push_back(ecBlocks2); +} + +int ECBlocks::getECCodewords() { + return ecCodewords_; +} + +std::vector& ECBlocks::getECBlocks() { + return ecBlocks_; +} + +ECBlocks::~ECBlocks() { + for (size_t i = 0; i < ecBlocks_.size(); i++) { + delete ecBlocks_[i]; + } +} + +unsigned int Version::VERSION_DECODE_INFO[] = { 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D, + 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, + 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x2542E, 0x26A64, + 0x27541, 0x28C69 + }; +int Version::N_VERSION_DECODE_INFOS = 34; +vector > Version::VERSIONS; +static int N_VERSIONS = Version::buildVersions(); + +int Version::getVersionNumber() { + return versionNumber_; +} + +vector &Version::getAlignmentPatternCenters() { + return alignmentPatternCenters_; +} + +int Version::getTotalCodewords() { + return totalCodewords_; +} + +int Version::getDimensionForVersion() { + return 17 + 4 * versionNumber_; +} + +ECBlocks& Version::getECBlocksForLevel(ErrorCorrectionLevel &ecLevel) { + return *ecBlocks_[ecLevel.ordinal()]; +} + +Version *Version::getProvisionalVersionForDimension(int dimension) { + if (dimension % 4 != 1) { + throw ReaderException("Dimension must be 1 mod 4"); + } + return Version::getVersionForNumber((dimension - 17) >> 2); +} + +Version *Version::getVersionForNumber(int versionNumber) { + if (versionNumber < 1 || versionNumber > N_VERSIONS) { + throw ReaderException("versionNumber must be between 1 and 40"); + } + + return VERSIONS[versionNumber - 1]; +} + +Version::Version(int versionNumber, vector *alignmentPatternCenters, ECBlocks *ecBlocks1, ECBlocks *ecBlocks2, + ECBlocks *ecBlocks3, ECBlocks *ecBlocks4) : + versionNumber_(versionNumber), alignmentPatternCenters_(*alignmentPatternCenters), ecBlocks_(4), totalCodewords_(0) { + ecBlocks_[0] = ecBlocks1; + ecBlocks_[1] = ecBlocks2; + ecBlocks_[2] = ecBlocks3; + ecBlocks_[3] = ecBlocks4; + + int total = 0; + int ecCodewords = ecBlocks1->getECCodewords(); + vector &ecbArray = ecBlocks1->getECBlocks(); + for (size_t i = 0; i < ecbArray.size(); i++) { + ECB *ecBlock = ecbArray[i]; + total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords); + } + totalCodewords_ = total; +} + +Version::~Version() { + delete &alignmentPatternCenters_; + for (size_t i = 0; i < ecBlocks_.size(); i++) { + delete ecBlocks_[i]; + } +} + +Version *Version::decodeVersionInformation(unsigned int versionBits) { + int bestDifference = numeric_limits::max(); + size_t bestVersion = 0; + for (int i = 0; i < N_VERSION_DECODE_INFOS; i++) { + unsigned targetVersion = VERSION_DECODE_INFO[i]; + // Do the version info bits match exactly? done. + if (targetVersion == versionBits) { + return getVersionForNumber(i + 7); } + // Otherwise see if this is the closest to a real version info bit + // string we have seen so far + int bitsDifference = FormatInformation::numBitsDiffering(versionBits, targetVersion); + if (bitsDifference < bestDifference) { + bestVersion = i + 7; + bestDifference = bitsDifference; + } + } + // We can tolerate up to 3 bits of error since no two version info codewords will + // differ in less than 4 bits. + if (bestDifference <= 3) { + return getVersionForNumber(bestVersion); + } + // If we didn't find a close enough match, fail + return 0; +} + +Ref Version::buildFunctionPattern() { + int dimension = getDimensionForVersion(); + Ref functionPattern(new BitMatrix(dimension)); + + + // Top left finder pattern + separator + format + functionPattern->setRegion(0, 0, 9, 9); + // Top right finder pattern + separator + format + functionPattern->setRegion(dimension - 8, 0, 8, 9); + // Bottom left finder pattern + separator + format + functionPattern->setRegion(0, dimension - 8, 9, 8); + + + // Alignment patterns + size_t max = alignmentPatternCenters_.size(); + for (size_t x = 0; x < max; x++) { + int i = alignmentPatternCenters_[x] - 2; + for (size_t y = 0; y < max; y++) { + if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) { + // No alignment patterns near the three finder patterns + continue; + } + functionPattern->setRegion(alignmentPatternCenters_[y] - 2, i, 5, 5); + } + } + + // Vertical timing pattern + functionPattern->setRegion(6, 9, 1, dimension - 17); + // Horizontal timing pattern + functionPattern->setRegion(9, 6, dimension - 17, 1); + + if (versionNumber_ > 6) { + // Version info, top right + functionPattern->setRegion(dimension - 11, 0, 3, 6); + // Version info, bottom left + functionPattern->setRegion(0, dimension - 11, 6, 3); + } + + + //#ifdef DEBUG + // cout << "version " << versionNumber_ << " built function pattern:\n"; + // cout << *functionPattern; + //#endif + + return functionPattern; +} + +static vector *intArray(size_t n...) { + va_list ap; + va_start(ap, n); + vector *result = new vector(n); + for (size_t i = 0; i < n; i++) { + (*result)[i] = va_arg(ap, int); + } + va_end(ap); + return result; +} + +int Version::buildVersions() { + VERSIONS.push_back(Ref(new Version(1, intArray(0), + new ECBlocks(7, new ECB(1, 19)), + new ECBlocks(10, new ECB(1, 16)), + new ECBlocks(13, new ECB(1, 13)), + new ECBlocks(17, new ECB(1, 9))))); + VERSIONS.push_back(Ref(new Version(2, intArray(2, 6, 18), + new ECBlocks(10, new ECB(1, 34)), + new ECBlocks(16, new ECB(1, 28)), + new ECBlocks(22, new ECB(1, 22)), + new ECBlocks(28, new ECB(1, 16))))); + VERSIONS.push_back(Ref(new Version(3, intArray(2, 6, 22), + new ECBlocks(15, new ECB(1, 55)), + new ECBlocks(26, new ECB(1, 44)), + new ECBlocks(18, new ECB(2, 17)), + new ECBlocks(22, new ECB(2, 13))))); + VERSIONS.push_back(Ref(new Version(4, intArray(2, 6, 26), + new ECBlocks(20, new ECB(1, 80)), + new ECBlocks(18, new ECB(2, 32)), + new ECBlocks(26, new ECB(2, 24)), + new ECBlocks(16, new ECB(4, 9))))); + VERSIONS.push_back(Ref(new Version(5, intArray(2, 6, 30), + new ECBlocks(26, new ECB(1, 108)), + new ECBlocks(24, new ECB(2, 43)), + new ECBlocks(18, new ECB(2, 15), + new ECB(2, 16)), + new ECBlocks(22, new ECB(2, 11), + new ECB(2, 12))))); + VERSIONS.push_back(Ref(new Version(6, intArray(2, 6, 34), + new ECBlocks(18, new ECB(2, 68)), + new ECBlocks(16, new ECB(4, 27)), + new ECBlocks(24, new ECB(4, 19)), + new ECBlocks(28, new ECB(4, 15))))); + VERSIONS.push_back(Ref(new Version(7, intArray(3, 6, 22, 38), + new ECBlocks(20, new ECB(2, 78)), + new ECBlocks(18, new ECB(4, 31)), + new ECBlocks(18, new ECB(2, 14), + new ECB(4, 15)), + new ECBlocks(26, new ECB(4, 13), + new ECB(1, 14))))); + VERSIONS.push_back(Ref(new Version(8, intArray(3, 6, 24, 42), + new ECBlocks(24, new ECB(2, 97)), + new ECBlocks(22, new ECB(2, 38), + new ECB(2, 39)), + new ECBlocks(22, new ECB(4, 18), + new ECB(2, 19)), + new ECBlocks(26, new ECB(4, 14), + new ECB(2, 15))))); + VERSIONS.push_back(Ref(new Version(9, intArray(3, 6, 26, 46), + new ECBlocks(30, new ECB(2, 116)), + new ECBlocks(22, new ECB(3, 36), + new ECB(2, 37)), + new ECBlocks(20, new ECB(4, 16), + new ECB(4, 17)), + new ECBlocks(24, new ECB(4, 12), + new ECB(4, 13))))); + VERSIONS.push_back(Ref(new Version(10, intArray(3, 6, 28, 50), + new ECBlocks(18, new ECB(2, 68), + new ECB(2, 69)), + new ECBlocks(26, new ECB(4, 43), + new ECB(1, 44)), + new ECBlocks(24, new ECB(6, 19), + new ECB(2, 20)), + new ECBlocks(28, new ECB(6, 15), + new ECB(2, 16))))); + VERSIONS.push_back(Ref(new Version(11, intArray(3, 6, 30, 54), + new ECBlocks(20, new ECB(4, 81)), + new ECBlocks(30, new ECB(1, 50), + new ECB(4, 51)), + new ECBlocks(28, new ECB(4, 22), + new ECB(4, 23)), + new ECBlocks(24, new ECB(3, 12), + new ECB(8, 13))))); + VERSIONS.push_back(Ref(new Version(12, intArray(3, 6, 32, 58), + new ECBlocks(24, new ECB(2, 92), + new ECB(2, 93)), + new ECBlocks(22, new ECB(6, 36), + new ECB(2, 37)), + new ECBlocks(26, new ECB(4, 20), + new ECB(6, 21)), + new ECBlocks(28, new ECB(7, 14), + new ECB(4, 15))))); + VERSIONS.push_back(Ref(new Version(13, intArray(3, 6, 34, 62), + new ECBlocks(26, new ECB(4, 107)), + new ECBlocks(22, new ECB(8, 37), + new ECB(1, 38)), + new ECBlocks(24, new ECB(8, 20), + new ECB(4, 21)), + new ECBlocks(22, new ECB(12, 11), + new ECB(4, 12))))); + VERSIONS.push_back(Ref(new Version(14, intArray(4, 6, 26, 46, 66), + new ECBlocks(30, new ECB(3, 115), + new ECB(1, 116)), + new ECBlocks(24, new ECB(4, 40), + new ECB(5, 41)), + new ECBlocks(20, new ECB(11, 16), + new ECB(5, 17)), + new ECBlocks(24, new ECB(11, 12), + new ECB(5, 13))))); + VERSIONS.push_back(Ref(new Version(15, intArray(4, 6, 26, 48, 70), + new ECBlocks(22, new ECB(5, 87), + new ECB(1, 88)), + new ECBlocks(24, new ECB(5, 41), + new ECB(5, 42)), + new ECBlocks(30, new ECB(5, 24), + new ECB(7, 25)), + new ECBlocks(24, new ECB(11, 12), + new ECB(7, 13))))); + VERSIONS.push_back(Ref(new Version(16, intArray(4, 6, 26, 50, 74), + new ECBlocks(24, new ECB(5, 98), + new ECB(1, 99)), + new ECBlocks(28, new ECB(7, 45), + new ECB(3, 46)), + new ECBlocks(24, new ECB(15, 19), + new ECB(2, 20)), + new ECBlocks(30, new ECB(3, 15), + new ECB(13, 16))))); + VERSIONS.push_back(Ref(new Version(17, intArray(4, 6, 30, 54, 78), + new ECBlocks(28, new ECB(1, 107), + new ECB(5, 108)), + new ECBlocks(28, new ECB(10, 46), + new ECB(1, 47)), + new ECBlocks(28, new ECB(1, 22), + new ECB(15, 23)), + new ECBlocks(28, new ECB(2, 14), + new ECB(17, 15))))); + VERSIONS.push_back(Ref(new Version(18, intArray(4, 6, 30, 56, 82), + new ECBlocks(30, new ECB(5, 120), + new ECB(1, 121)), + new ECBlocks(26, new ECB(9, 43), + new ECB(4, 44)), + new ECBlocks(28, new ECB(17, 22), + new ECB(1, 23)), + new ECBlocks(28, new ECB(2, 14), + new ECB(19, 15))))); + VERSIONS.push_back(Ref(new Version(19, intArray(4, 6, 30, 58, 86), + new ECBlocks(28, new ECB(3, 113), + new ECB(4, 114)), + new ECBlocks(26, new ECB(3, 44), + new ECB(11, 45)), + new ECBlocks(26, new ECB(17, 21), + new ECB(4, 22)), + new ECBlocks(26, new ECB(9, 13), + new ECB(16, 14))))); + VERSIONS.push_back(Ref(new Version(20, intArray(4, 6, 34, 62, 90), + new ECBlocks(28, new ECB(3, 107), + new ECB(5, 108)), + new ECBlocks(26, new ECB(3, 41), + new ECB(13, 42)), + new ECBlocks(30, new ECB(15, 24), + new ECB(5, 25)), + new ECBlocks(28, new ECB(15, 15), + new ECB(10, 16))))); + VERSIONS.push_back(Ref(new Version(21, intArray(5, 6, 28, 50, 72, 94), + new ECBlocks(28, new ECB(4, 116), + new ECB(4, 117)), + new ECBlocks(26, new ECB(17, 42)), + new ECBlocks(28, new ECB(17, 22), + new ECB(6, 23)), + new ECBlocks(30, new ECB(19, 16), + new ECB(6, 17))))); + VERSIONS.push_back(Ref(new Version(22, intArray(5, 6, 26, 50, 74, 98), + new ECBlocks(28, new ECB(2, 111), + new ECB(7, 112)), + new ECBlocks(28, new ECB(17, 46)), + new ECBlocks(30, new ECB(7, 24), + new ECB(16, 25)), + new ECBlocks(24, new ECB(34, 13))))); + VERSIONS.push_back(Ref(new Version(23, intArray(5, 6, 30, 54, 78, 102), + new ECBlocks(30, new ECB(4, 121), + new ECB(5, 122)), + new ECBlocks(28, new ECB(4, 47), + new ECB(14, 48)), + new ECBlocks(30, new ECB(11, 24), + new ECB(14, 25)), + new ECBlocks(30, new ECB(16, 15), + new ECB(14, 16))))); + VERSIONS.push_back(Ref(new Version(24, intArray(5, 6, 28, 54, 80, 106), + new ECBlocks(30, new ECB(6, 117), + new ECB(4, 118)), + new ECBlocks(28, new ECB(6, 45), + new ECB(14, 46)), + new ECBlocks(30, new ECB(11, 24), + new ECB(16, 25)), + new ECBlocks(30, new ECB(30, 16), + new ECB(2, 17))))); + VERSIONS.push_back(Ref(new Version(25, intArray(5, 6, 32, 58, 84, 110), + new ECBlocks(26, new ECB(8, 106), + new ECB(4, 107)), + new ECBlocks(28, new ECB(8, 47), + new ECB(13, 48)), + new ECBlocks(30, new ECB(7, 24), + new ECB(22, 25)), + new ECBlocks(30, new ECB(22, 15), + new ECB(13, 16))))); + VERSIONS.push_back(Ref(new Version(26, intArray(5, 6, 30, 58, 86, 114), + new ECBlocks(28, new ECB(10, 114), + new ECB(2, 115)), + new ECBlocks(28, new ECB(19, 46), + new ECB(4, 47)), + new ECBlocks(28, new ECB(28, 22), + new ECB(6, 23)), + new ECBlocks(30, new ECB(33, 16), + new ECB(4, 17))))); + VERSIONS.push_back(Ref(new Version(27, intArray(5, 6, 34, 62, 90, 118), + new ECBlocks(30, new ECB(8, 122), + new ECB(4, 123)), + new ECBlocks(28, new ECB(22, 45), + new ECB(3, 46)), + new ECBlocks(30, new ECB(8, 23), + new ECB(26, 24)), + new ECBlocks(30, new ECB(12, 15), + new ECB(28, 16))))); + VERSIONS.push_back(Ref(new Version(28, intArray(6, 6, 26, 50, 74, 98, 122), + new ECBlocks(30, new ECB(3, 117), + new ECB(10, 118)), + new ECBlocks(28, new ECB(3, 45), + new ECB(23, 46)), + new ECBlocks(30, new ECB(4, 24), + new ECB(31, 25)), + new ECBlocks(30, new ECB(11, 15), + new ECB(31, 16))))); + VERSIONS.push_back(Ref(new Version(29, intArray(6, 6, 30, 54, 78, 102, 126), + new ECBlocks(30, new ECB(7, 116), + new ECB(7, 117)), + new ECBlocks(28, new ECB(21, 45), + new ECB(7, 46)), + new ECBlocks(30, new ECB(1, 23), + new ECB(37, 24)), + new ECBlocks(30, new ECB(19, 15), + new ECB(26, 16))))); + VERSIONS.push_back(Ref(new Version(30, intArray(6, 6, 26, 52, 78, 104, 130), + new ECBlocks(30, new ECB(5, 115), + new ECB(10, 116)), + new ECBlocks(28, new ECB(19, 47), + new ECB(10, 48)), + new ECBlocks(30, new ECB(15, 24), + new ECB(25, 25)), + new ECBlocks(30, new ECB(23, 15), + new ECB(25, 16))))); + VERSIONS.push_back(Ref(new Version(31, intArray(6, 6, 30, 56, 82, 108, 134), + new ECBlocks(30, new ECB(13, 115), + new ECB(3, 116)), + new ECBlocks(28, new ECB(2, 46), + new ECB(29, 47)), + new ECBlocks(30, new ECB(42, 24), + new ECB(1, 25)), + new ECBlocks(30, new ECB(23, 15), + new ECB(28, 16))))); + VERSIONS.push_back(Ref(new Version(32, intArray(6, 6, 34, 60, 86, 112, 138), + new ECBlocks(30, new ECB(17, 115)), + new ECBlocks(28, new ECB(10, 46), + new ECB(23, 47)), + new ECBlocks(30, new ECB(10, 24), + new ECB(35, 25)), + new ECBlocks(30, new ECB(19, 15), + new ECB(35, 16))))); + VERSIONS.push_back(Ref(new Version(33, intArray(6, 6, 30, 58, 86, 114, 142), + new ECBlocks(30, new ECB(17, 115), + new ECB(1, 116)), + new ECBlocks(28, new ECB(14, 46), + new ECB(21, 47)), + new ECBlocks(30, new ECB(29, 24), + new ECB(19, 25)), + new ECBlocks(30, new ECB(11, 15), + new ECB(46, 16))))); + VERSIONS.push_back(Ref(new Version(34, intArray(6, 6, 34, 62, 90, 118, 146), + new ECBlocks(30, new ECB(13, 115), + new ECB(6, 116)), + new ECBlocks(28, new ECB(14, 46), + new ECB(23, 47)), + new ECBlocks(30, new ECB(44, 24), + new ECB(7, 25)), + new ECBlocks(30, new ECB(59, 16), + new ECB(1, 17))))); + VERSIONS.push_back(Ref(new Version(35, intArray(7, 6, 30, 54, 78, + 102, 126, 150), + new ECBlocks(30, new ECB(12, 121), + new ECB(7, 122)), + new ECBlocks(28, new ECB(12, 47), + new ECB(26, 48)), + new ECBlocks(30, new ECB(39, 24), + new ECB(14, 25)), + new ECBlocks(30, new ECB(22, 15), + new ECB(41, 16))))); + VERSIONS.push_back(Ref(new Version(36, intArray(7, 6, 24, 50, 76, + 102, 128, 154), + new ECBlocks(30, new ECB(6, 121), + new ECB(14, 122)), + new ECBlocks(28, new ECB(6, 47), + new ECB(34, 48)), + new ECBlocks(30, new ECB(46, 24), + new ECB(10, 25)), + new ECBlocks(30, new ECB(2, 15), + new ECB(64, 16))))); + VERSIONS.push_back(Ref(new Version(37, intArray(7, 6, 28, 54, 80, + 106, 132, 158), + new ECBlocks(30, new ECB(17, 122), + new ECB(4, 123)), + new ECBlocks(28, new ECB(29, 46), + new ECB(14, 47)), + new ECBlocks(30, new ECB(49, 24), + new ECB(10, 25)), + new ECBlocks(30, new ECB(24, 15), + new ECB(46, 16))))); + VERSIONS.push_back(Ref(new Version(38, intArray(7, 6, 32, 58, 84, + 110, 136, 162), + new ECBlocks(30, new ECB(4, 122), + new ECB(18, 123)), + new ECBlocks(28, new ECB(13, 46), + new ECB(32, 47)), + new ECBlocks(30, new ECB(48, 24), + new ECB(14, 25)), + new ECBlocks(30, new ECB(42, 15), + new ECB(32, 16))))); + VERSIONS.push_back(Ref(new Version(39, intArray(7, 6, 26, 54, 82, + 110, 138, 166), + new ECBlocks(30, new ECB(20, 117), + new ECB(4, 118)), + new ECBlocks(28, new ECB(40, 47), + new ECB(7, 48)), + new ECBlocks(30, new ECB(43, 24), + new ECB(22, 25)), + new ECBlocks(30, new ECB(10, 15), + new ECB(67, 16))))); + VERSIONS.push_back(Ref(new Version(40, intArray(7, 6, 30, 58, 86, + 114, 142, 170), + new ECBlocks(30, new ECB(19, 118), + new ECB(6, 119)), + new ECBlocks(28, new ECB(18, 47), + new ECB(31, 48)), + new ECBlocks(30, new ECB(34, 24), + new ECB(34, 25)), + new ECBlocks(30, new ECB(20, 15), + new ECB(61, 16))))); + return VERSIONS.size(); +} +} } // file: zxing/qrcode/decoder/BitMatrixParser.cpp @@ -7744,166 +9536,166 @@ namespace zxing { namespace zxing { - namespace qrcode { - - int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) { - return bitMatrix_->get(x, y) ? (versionBits << 1) | 0x1 : versionBits << 1; - } - - BitMatrixParser::BitMatrixParser(Ref bitMatrix) : - bitMatrix_(bitMatrix), parsedVersion_(0), parsedFormatInfo_() { - size_t dimension = bitMatrix->getDimension(); - if ((dimension < 21) || (dimension & 0x03) != 1) { - throw ReaderException("Dimension must be 1 mod 4 and >= 21"); - } - } - - Ref BitMatrixParser::readFormatInformation() { - if (parsedFormatInfo_ != 0) { - return parsedFormatInfo_; - } - - // Read top-left format info bits - int formatInfoBits1 = 0; - for (int i = 0; i < 6; i++) { - formatInfoBits1 = copyBit(i, 8, formatInfoBits1); - } - // .. and skip a bit in the timing pattern ... - formatInfoBits1 = copyBit(7, 8, formatInfoBits1); - formatInfoBits1 = copyBit(8, 8, formatInfoBits1); - formatInfoBits1 = copyBit(8, 7, formatInfoBits1); - // .. and skip a bit in the timing pattern ... - for (int j = 5; j >= 0; j--) { - formatInfoBits1 = copyBit(8, j, formatInfoBits1); - } - - // Read the top-right/bottom-left pattern - int dimension = bitMatrix_->getDimension(); - int formatInfoBits2 = 0; - int jMin = dimension - 7; - for (int j = dimension - 1; j >= jMin; j--) { - formatInfoBits2 = copyBit(8, j, formatInfoBits2); - } - for (int i = dimension - 8; i < dimension; i++) { - formatInfoBits2 = copyBit(i, 8, formatInfoBits2); - } - - parsedFormatInfo_ = FormatInformation::decodeFormatInformation(formatInfoBits1,formatInfoBits2); - if (parsedFormatInfo_ != 0) { - return parsedFormatInfo_; - } - throw ReaderException("Could not decode format information"); - } - - Version *BitMatrixParser::readVersion() { - if (parsedVersion_ != 0) { - return parsedVersion_; - } - - int dimension = bitMatrix_->getDimension(); - - int provisionalVersion = (dimension - 17) >> 2; - if (provisionalVersion <= 6) { - return Version::getVersionForNumber(provisionalVersion); - } - - // Read top-right version info: 3 wide by 6 tall - int versionBits = 0; - for (int y = 5; y >= 0; y--) { - int xMin = dimension - 11; - for (int x = dimension - 9; x >= xMin; x--) { - versionBits = copyBit(x, y, versionBits); - } - } - - parsedVersion_ = Version::decodeVersionInformation(versionBits); - if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion() == dimension) { - return parsedVersion_; - } - - // Hmm, failed. Try bottom left: 6 wide by 3 tall - versionBits = 0; - for (int x = 5; x >= 0; x--) { - int yMin = dimension - 11; - for (int y = dimension - 9; y >= yMin; y--) { - versionBits = copyBit(x, y, versionBits); - } - } - - parsedVersion_ = Version::decodeVersionInformation(versionBits); - if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion() == dimension) { - return parsedVersion_; - } - throw ReaderException("Could not decode version"); - } - - ArrayRef BitMatrixParser::readCodewords() { - Ref formatInfo = readFormatInformation(); - Version *version = readVersion(); - - - // cerr << *bitMatrix_ << endl; - // cerr << bitMatrix_->getDimension() << endl; - - // Get the data mask for the format used in this QR Code. This will exclude - // some bits from reading as we wind through the bit matrix. - DataMask &dataMask = DataMask::forReference((int)formatInfo->getDataMask()); - // cout << (int)formatInfo->getDataMask() << endl; - int dimension = bitMatrix_->getDimension(); - dataMask.unmaskBitMatrix(*bitMatrix_, dimension); - - - // cerr << *bitMatrix_ << endl; - // cerr << version->getTotalCodewords() << endl; - - Ref functionPattern = version->buildFunctionPattern(); - - - // cout << *functionPattern << endl; - - bool readingUp = true; - ArrayRef result(version->getTotalCodewords()); - int resultOffset = 0; - int currentByte = 0; - int bitsRead = 0; - // Read columns in pairs, from right to left - for (int x = dimension - 1; x > 0; x -= 2) { - if (x == 6) { - // Skip whole column with vertical alignment pattern; - // saves time and makes the other code proceed more cleanly - x--; - } - // Read alternatingly from bottom to top then top to bottom - for (int counter = 0; counter < dimension; counter++) { - int y = readingUp ? dimension - 1 - counter : counter; - for (int col = 0; col < 2; col++) { - // Ignore bits covered by the function pattern - if (!functionPattern->get(x - col, y)) { - // Read a bit - bitsRead++; - currentByte <<= 1; - if (bitMatrix_->get(x - col, y)) { - currentByte |= 1; - } - // If we've made a whole byte, save it off - if (bitsRead == 8) { - result[resultOffset++] = (unsigned char)currentByte; - bitsRead = 0; - currentByte = 0; - } - } - } - } - readingUp = !readingUp; // switch directions - } - - if (resultOffset != version->getTotalCodewords()) { - throw ReaderException("Did not read all codewords"); - } - return result; - } - +namespace qrcode { + +int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) { + return bitMatrix_->get(x, y) ? (versionBits << 1) | 0x1 : versionBits << 1; +} + +BitMatrixParser::BitMatrixParser(Ref bitMatrix) : + bitMatrix_(bitMatrix), parsedVersion_(0), parsedFormatInfo_() { + size_t dimension = bitMatrix->getDimension(); + if ((dimension < 21) || (dimension & 0x03) != 1) { + throw ReaderException("Dimension must be 1 mod 4 and >= 21"); + } +} + +Ref BitMatrixParser::readFormatInformation() { + if (parsedFormatInfo_ != 0) { + return parsedFormatInfo_; + } + + // Read top-left format info bits + int formatInfoBits1 = 0; + for (int i = 0; i < 6; i++) { + formatInfoBits1 = copyBit(i, 8, formatInfoBits1); + } + // .. and skip a bit in the timing pattern ... + formatInfoBits1 = copyBit(7, 8, formatInfoBits1); + formatInfoBits1 = copyBit(8, 8, formatInfoBits1); + formatInfoBits1 = copyBit(8, 7, formatInfoBits1); + // .. and skip a bit in the timing pattern ... + for (int j = 5; j >= 0; j--) { + formatInfoBits1 = copyBit(8, j, formatInfoBits1); + } + + // Read the top-right/bottom-left pattern + int dimension = bitMatrix_->getDimension(); + int formatInfoBits2 = 0; + int jMin = dimension - 7; + for (int j = dimension - 1; j >= jMin; j--) { + formatInfoBits2 = copyBit(8, j, formatInfoBits2); + } + for (int i = dimension - 8; i < dimension; i++) { + formatInfoBits2 = copyBit(i, 8, formatInfoBits2); + } + + parsedFormatInfo_ = FormatInformation::decodeFormatInformation(formatInfoBits1,formatInfoBits2); + if (parsedFormatInfo_ != 0) { + return parsedFormatInfo_; + } + throw ReaderException("Could not decode format information"); +} + +Version *BitMatrixParser::readVersion() { + if (parsedVersion_ != 0) { + return parsedVersion_; + } + + int dimension = bitMatrix_->getDimension(); + + int provisionalVersion = (dimension - 17) >> 2; + if (provisionalVersion <= 6) { + return Version::getVersionForNumber(provisionalVersion); + } + + // Read top-right version info: 3 wide by 6 tall + int versionBits = 0; + for (int y = 5; y >= 0; y--) { + int xMin = dimension - 11; + for (int x = dimension - 9; x >= xMin; x--) { + versionBits = copyBit(x, y, versionBits); } + } + + parsedVersion_ = Version::decodeVersionInformation(versionBits); + if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion() == dimension) { + return parsedVersion_; + } + + // Hmm, failed. Try bottom left: 6 wide by 3 tall + versionBits = 0; + for (int x = 5; x >= 0; x--) { + int yMin = dimension - 11; + for (int y = dimension - 9; y >= yMin; y--) { + versionBits = copyBit(x, y, versionBits); + } + } + + parsedVersion_ = Version::decodeVersionInformation(versionBits); + if (parsedVersion_ != 0 && parsedVersion_->getDimensionForVersion() == dimension) { + return parsedVersion_; + } + throw ReaderException("Could not decode version"); +} + +ArrayRef BitMatrixParser::readCodewords() { + Ref formatInfo = readFormatInformation(); + Version *version = readVersion(); + + + // cerr << *bitMatrix_ << endl; + // cerr << bitMatrix_->getDimension() << endl; + + // Get the data mask for the format used in this QR Code. This will exclude + // some bits from reading as we wind through the bit matrix. + DataMask &dataMask = DataMask::forReference((int)formatInfo->getDataMask()); + // cout << (int)formatInfo->getDataMask() << endl; + int dimension = bitMatrix_->getDimension(); + dataMask.unmaskBitMatrix(*bitMatrix_, dimension); + + + // cerr << *bitMatrix_ << endl; + // cerr << version->getTotalCodewords() << endl; + + Ref functionPattern = version->buildFunctionPattern(); + + + // cout << *functionPattern << endl; + + bool readingUp = true; + ArrayRef result(version->getTotalCodewords()); + int resultOffset = 0; + int currentByte = 0; + int bitsRead = 0; + // Read columns in pairs, from right to left + for (int x = dimension - 1; x > 0; x -= 2) { + if (x == 6) { + // Skip whole column with vertical alignment pattern; + // saves time and makes the other code proceed more cleanly + x--; + } + // Read alternatingly from bottom to top then top to bottom + for (int counter = 0; counter < dimension; counter++) { + int y = readingUp ? dimension - 1 - counter : counter; + for (int col = 0; col < 2; col++) { + // Ignore bits covered by the function pattern + if (!functionPattern->get(x - col, y)) { + // Read a bit + bitsRead++; + currentByte <<= 1; + if (bitMatrix_->get(x - col, y)) { + currentByte |= 1; + } + // If we've made a whole byte, save it off + if (bitsRead == 8) { + result[resultOffset++] = (unsigned char)currentByte; + bitsRead = 0; + currentByte = 0; + } + } + } + } + readingUp = !readingUp; // switch directions + } + + if (resultOffset != version->getTotalCodewords()) { + throw ReaderException("Did not read all codewords"); + } + return result; +} + +} } // file: zxing/qrcode/decoder/DataBlock.cpp @@ -7932,99 +9724,99 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - DataBlock::DataBlock(int numDataCodewords, ArrayRef codewords) : - numDataCodewords_(numDataCodewords), codewords_(codewords) { - } - - int DataBlock::getNumDataCodewords() { - return numDataCodewords_; - } - - ArrayRef DataBlock::getCodewords() { - return codewords_; - } - - - std::vector > DataBlock::getDataBlocks(ArrayRef rawCodewords, Version *version, - ErrorCorrectionLevel &ecLevel) { - - - // Figure out the number and size of data blocks used by this version and - // error correction level - ECBlocks &ecBlocks = version->getECBlocksForLevel(ecLevel); - - - // First count the total number of data blocks - int totalBlocks = 0; - vector ecBlockArray = ecBlocks.getECBlocks(); - for (size_t i = 0; i < ecBlockArray.size(); i++) { - totalBlocks += ecBlockArray[i]->getCount(); - } - - // Now establish DataBlocks of the appropriate size and number of data codewords - std::vector > result(totalBlocks); - int numResultBlocks = 0; - for (size_t j = 0; j < ecBlockArray.size(); j++) { - ECB *ecBlock = ecBlockArray[j]; - for (int i = 0; i < ecBlock->getCount(); i++) { - int numDataCodewords = ecBlock->getDataCodewords(); - int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords; - ArrayRef buffer(numBlockCodewords); - Ref blockRef(new DataBlock(numDataCodewords, buffer)); - result[numResultBlocks++] = blockRef; - } - } - - // All blocks have the same amount of data, except that the last n - // (where n may be 0) have 1 more byte. Figure out where these start. - int shorterBlocksTotalCodewords = result[0]->codewords_.size(); - int longerBlocksStartAt = result.size() - 1; - while (longerBlocksStartAt >= 0) { - int numCodewords = result[longerBlocksStartAt]->codewords_.size(); - if (numCodewords == shorterBlocksTotalCodewords) { - break; - } - if (numCodewords != shorterBlocksTotalCodewords + 1) { - throw IllegalArgumentException("Data block sizes differ by more than 1"); - } - longerBlocksStartAt--; - } - longerBlocksStartAt++; - - int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewords(); - // The last elements of result may be 1 element longer; - // first fill out as many elements as all of them have - int rawCodewordsOffset = 0; - for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { - for (int j = 0; j < numResultBlocks; j++) { - result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++]; - } - } - // Fill out the last data block in the longer ones - for (int j = longerBlocksStartAt; j < numResultBlocks; j++) { - result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; - } - // Now add in error correction blocks - int max = result[0]->codewords_.size(); - for (int i = shorterBlocksNumDataCodewords; i < max; i++) { - for (int j = 0; j < numResultBlocks; j++) { - int iOffset = j < longerBlocksStartAt ? i : i + 1; - result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++]; - } - } - - if ((size_t)rawCodewordsOffset != rawCodewords.size()) { - throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length"); - } - - return result; - } - +namespace qrcode { + +using namespace std; + +DataBlock::DataBlock(int numDataCodewords, ArrayRef codewords) : + numDataCodewords_(numDataCodewords), codewords_(codewords) { +} + +int DataBlock::getNumDataCodewords() { + return numDataCodewords_; +} + +ArrayRef DataBlock::getCodewords() { + return codewords_; +} + + +std::vector > DataBlock::getDataBlocks(ArrayRef rawCodewords, Version *version, + ErrorCorrectionLevel &ecLevel) { + + + // Figure out the number and size of data blocks used by this version and + // error correction level + ECBlocks &ecBlocks = version->getECBlocksForLevel(ecLevel); + + + // First count the total number of data blocks + int totalBlocks = 0; + vector ecBlockArray = ecBlocks.getECBlocks(); + for (size_t i = 0; i < ecBlockArray.size(); i++) { + totalBlocks += ecBlockArray[i]->getCount(); + } + + // Now establish DataBlocks of the appropriate size and number of data codewords + std::vector > result(totalBlocks); + int numResultBlocks = 0; + for (size_t j = 0; j < ecBlockArray.size(); j++) { + ECB *ecBlock = ecBlockArray[j]; + for (int i = 0; i < ecBlock->getCount(); i++) { + int numDataCodewords = ecBlock->getDataCodewords(); + int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords; + ArrayRef buffer(numBlockCodewords); + Ref blockRef(new DataBlock(numDataCodewords, buffer)); + result[numResultBlocks++] = blockRef; } + } + + // All blocks have the same amount of data, except that the last n + // (where n may be 0) have 1 more byte. Figure out where these start. + int shorterBlocksTotalCodewords = result[0]->codewords_.size(); + int longerBlocksStartAt = result.size() - 1; + while (longerBlocksStartAt >= 0) { + int numCodewords = result[longerBlocksStartAt]->codewords_.size(); + if (numCodewords == shorterBlocksTotalCodewords) { + break; + } + if (numCodewords != shorterBlocksTotalCodewords + 1) { + throw IllegalArgumentException("Data block sizes differ by more than 1"); + } + longerBlocksStartAt--; + } + longerBlocksStartAt++; + + int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewords(); + // The last elements of result may be 1 element longer; + // first fill out as many elements as all of them have + int rawCodewordsOffset = 0; + for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { + for (int j = 0; j < numResultBlocks; j++) { + result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++]; + } + } + // Fill out the last data block in the longer ones + for (int j = longerBlocksStartAt; j < numResultBlocks; j++) { + result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; + } + // Now add in error correction blocks + int max = result[0]->codewords_.size(); + for (int i = shorterBlocksNumDataCodewords; i < max; i++) { + for (int j = 0; j < numResultBlocks; j++) { + int iOffset = j < longerBlocksStartAt ? i : i + 1; + result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++]; + } + } + + if ((size_t)rawCodewordsOffset != rawCodewords.size()) { + throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length"); + } + + return result; +} + +} } // file: zxing/qrcode/decoder/DataMask.cpp @@ -8054,143 +9846,144 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - DataMask::DataMask() { - } - - DataMask::~DataMask() { - } - - vector > DataMask::DATA_MASKS; - static int N_DATA_MASKS = DataMask::buildDataMasks(); - - DataMask &DataMask::forReference(int reference) { - if (reference < 0 || reference > 7) { - throw IllegalArgumentException("reference must be between 0 and 7"); - } - return *DATA_MASKS[reference]; - } - - void DataMask::unmaskBitMatrix(BitMatrix& bits, size_t dimension) { - for (size_t y = 0; y < dimension; y++) { - for (size_t x = 0; x < dimension; x++) { - // TODO: check why the coordinates have to be swapped - if (isMasked(y, x)) { - bits.flip(x, y); - } - } - } - } - - /** - * 000: mask bits for which (x + y) mod 2 == 0 - */ - class DataMask000 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - // return ((x + y) & 0x01) == 0; - return ((x + y) % 2) == 0; - } - }; - - /** - * 001: mask bits for which x mod 2 == 0 - */ - class DataMask001 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - // return (x & 0x01) == 0; - return (x % 2) == 0; - } - }; - - /** - * 010: mask bits for which y mod 3 == 0 - */ - class DataMask010 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - return y % 3 == 0; - } - }; - - /** - * 011: mask bits for which (x + y) mod 3 == 0 - */ - class DataMask011 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - return (x + y) % 3 == 0; - } - }; - - /** - * 100: mask bits for which (x/2 + y/3) mod 2 == 0 - */ - class DataMask100 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - // return (((x >> 1) + (y / 3)) & 0x01) == 0; - return (((x >> 1) + (y / 3)) % 2) == 0; - } - }; - - /** - * 101: mask bits for which xy mod 2 + xy mod 3 == 0 - */ - class DataMask101 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - size_t temp = x * y; - // return (temp & 0x01) + (temp % 3) == 0; - return (temp % 2) + (temp % 3) == 0; - - } - }; - - /** - * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0 - */ - class DataMask110 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - size_t temp = x * y; - // return (((temp & 0x01) + (temp % 3)) & 0x01) == 0; - return (((temp % 2) + (temp % 3)) % 2) == 0; - } - }; - - /** - * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0 - */ - class DataMask111 : public DataMask { - public: - bool isMasked(size_t x, size_t y) { - // return ((((x + y) & 0x01) + ((x * y) % 3)) & 0x01) == 0; - return ((((x + y) % 2) + ((x * y) % 3)) % 2) == 0; - } - }; - - int DataMask::buildDataMasks() { - DATA_MASKS.push_back(Ref (new DataMask000())); - DATA_MASKS.push_back(Ref (new DataMask001())); - DATA_MASKS.push_back(Ref (new DataMask010())); - DATA_MASKS.push_back(Ref (new DataMask011())); - DATA_MASKS.push_back(Ref (new DataMask100())); - DATA_MASKS.push_back(Ref (new DataMask101())); - DATA_MASKS.push_back(Ref (new DataMask110())); - DATA_MASKS.push_back(Ref (new DataMask111())); - return DATA_MASKS.size(); - } - +namespace qrcode { + +using namespace std; + +DataMask::DataMask() { +} + +DataMask::~DataMask() { +} + +vector > DataMask::DATA_MASKS; +static int N_DATA_MASKS = DataMask::buildDataMasks(); + +DataMask &DataMask::forReference(int reference) { + if (reference < 0 || reference > 7) { + throw IllegalArgumentException("reference must be between 0 and 7"); + } + return *DATA_MASKS[reference]; +} + +void DataMask::unmaskBitMatrix(BitMatrix& bits, size_t dimension) { + for (size_t y = 0; y < dimension; y++) { + for (size_t x = 0; x < dimension; x++) { + // TODO: check why the coordinates have to be swapped + if (isMasked(y, x)) { + bits.flip(x, y); + } } + } +} + +/** + * 000: mask bits for which (x + y) mod 2 == 0 + */ +class DataMask000 : public DataMask { +public: + bool isMasked(size_t x, size_t y) { + // return ((x + y) & 0x01) == 0; + return ((x + y) % 2) == 0; + } +}; + +/** + * 001: mask bits for which x mod 2 == 0 + */ +class DataMask001 : public DataMask { +public: + bool isMasked(size_t x, size_t) { + // return (x & 0x01) == 0; + return (x % 2) == 0; + } +}; + +/** + * 010: mask bits for which y mod 3 == 0 + */ +class DataMask010 : public DataMask { +public: + bool isMasked(size_t, size_t y) { + return y % 3 == 0; + } +}; + +/** + * 011: mask bits for which (x + y) mod 3 == 0 + */ +class DataMask011 : public DataMask { +public: + bool isMasked(size_t x, size_t y) { + return (x + y) % 3 == 0; + } +}; + +/** + * 100: mask bits for which (x/2 + y/3) mod 2 == 0 + */ +class DataMask100 : public DataMask { +public: + bool isMasked(size_t x, size_t y) { + // return (((x >> 1) + (y / 3)) & 0x01) == 0; + return (((x >> 1) + (y / 3)) % 2) == 0; + } +}; + +/** + * 101: mask bits for which xy mod 2 + xy mod 3 == 0 + */ +class DataMask101 : public DataMask { +public: + bool isMasked(size_t x, size_t y) { + size_t temp = x * y; + // return (temp & 0x01) + (temp % 3) == 0; + return (temp % 2) + (temp % 3) == 0; + + } +}; + +/** + * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0 + */ +class DataMask110 : public DataMask { +public: + bool isMasked(size_t x, size_t y) { + size_t temp = x * y; + // return (((temp & 0x01) + (temp % 3)) & 0x01) == 0; + return (((temp % 2) + (temp % 3)) % 2) == 0; + } +}; + +/** + * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0 + */ +class DataMask111 : public DataMask { +public: + bool isMasked(size_t x, size_t y) { + // return ((((x + y) & 0x01) + ((x * y) % 3)) & 0x01) == 0; + return ((((x + y) % 2) + ((x * y) % 3)) % 2) == 0; + } +}; + +int DataMask::buildDataMasks() { + DATA_MASKS.push_back(Ref (new DataMask000())); + DATA_MASKS.push_back(Ref (new DataMask001())); + DATA_MASKS.push_back(Ref (new DataMask010())); + DATA_MASKS.push_back(Ref (new DataMask011())); + DATA_MASKS.push_back(Ref (new DataMask100())); + DATA_MASKS.push_back(Ref (new DataMask101())); + DATA_MASKS.push_back(Ref (new DataMask110())); + DATA_MASKS.push_back(Ref (new DataMask111())); + return DATA_MASKS.size(); +} + +} } // file: zxing/qrcode/decoder/DecodedBitStreamParser.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * DecodedBitStreamParser.cpp * zxing @@ -8212,6 +10005,9 @@ namespace zxing { */ // #include +// #include +// #include +// #include // #include #ifndef NO_ICONV // #include @@ -8227,324 +10023,369 @@ namespace zxing { #define ICONV_CONST /**/ #endif +using namespace std; using namespace zxing; +using namespace zxing::qrcode; +using namespace zxing::common; -namespace zxing { - namespace qrcode { - - using namespace std; - - const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', - 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' - }; - - const char *DecodedBitStreamParser::ASCII = "ASCII"; - const char *DecodedBitStreamParser::ISO88591 = "ISO-8859-1"; - const char *DecodedBitStreamParser::UTF8 = "UTF-8"; - const char *DecodedBitStreamParser::SHIFT_JIS = "SHIFT_JIS"; - const char *DecodedBitStreamParser::EUC_JP = "EUC-JP"; - - void DecodedBitStreamParser::append(std::string &result, const unsigned char *bufIn, size_t nIn, const char *src) { -#ifndef NO_ICONV - if (nIn == 0) { - return; - } - - iconv_t cd = iconv_open(UTF8, src); - const int maxOut = 4 * nIn + 1; - unsigned char* bufOut = new unsigned char[maxOut]; - - ICONV_CONST char *fromPtr = (ICONV_CONST char *)bufIn; - size_t nFrom = nIn; - char *toPtr = (char *)bufOut; - size_t nTo = maxOut; - - while (nFrom > 0) { - size_t oneway = iconv(cd, &fromPtr, &nFrom, &toPtr, &nTo); - if (oneway == (size_t)(-1)) { - iconv_close(cd); - delete[] bufOut; - throw ReaderException("error converting characters"); - } - } - iconv_close(cd); - - int nResult = maxOut - nTo; - bufOut[nResult] = '\0'; - result.append((const char *)bufOut); - delete[] bufOut; -#else - result.append((const char *)bufIn, nIn); -#endif - } - - void DecodedBitStreamParser::decodeKanjiSegment(Ref bits, std::string &result, int count) { - // Each character will require 2 bytes. Read the characters as 2-byte pairs - // and decode as Shift_JIS afterwards - size_t nBytes = 2 * count; - unsigned char* buffer = new unsigned char[nBytes]; - int offset = 0; - while (count > 0) { - // Each 13 bits encodes a 2-byte character - - int twoBytes = bits->readBits(13); - int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); - if (assembledTwoBytes < 0x01F00) { - // In the 0x8140 to 0x9FFC range - assembledTwoBytes += 0x08140; - } else { - // In the 0xE040 to 0xEBBF range - assembledTwoBytes += 0x0C140; - } - buffer[offset] = (unsigned char)(assembledTwoBytes >> 8); - buffer[offset + 1] = (unsigned char)assembledTwoBytes; - offset += 2; - count--; - } - - append(result, buffer, nBytes, SHIFT_JIS); - delete[] buffer; - } - - void DecodedBitStreamParser::decodeByteSegment(Ref bits, std::string &result, int count) { - int nBytes = count; - unsigned char* readBytes = new unsigned char[nBytes]; - if (count << 3 > bits->available()) { - ostringstream s; - s << "Count too large: " << count; - delete[] readBytes; - throw ReaderException(s.str().c_str()); - } - for (int i = 0; i < count; i++) { - readBytes[i] = (unsigned char)bits->readBits(8); - } - // The spec isn't clear on this mode; see - // section 6.4.5: t does not say which encoding to assuming - // upon decoding. I have seen ISO-8859-1 used as well as - // Shift_JIS -- without anything like an ECI designator to - // give a hint. - const char *encoding = guessEncoding(readBytes, nBytes); - append(result, readBytes, nBytes, encoding); - delete[] readBytes; - } - - void DecodedBitStreamParser::decodeNumericSegment(Ref bits, std::string &result, int count) { - int nBytes = count; - unsigned char* bytes = new unsigned char[nBytes]; - int i = 0; - // Read three digits at a time - while (count >= 3) { - // Each 10 bits encodes three digits - int threeDigitsBits = bits->readBits(10); - if (threeDigitsBits >= 1000) { - ostringstream s; - s << "Illegal value for 3-digit unit: " << threeDigitsBits; - delete[] bytes; - throw ReaderException(s.str().c_str()); - } - bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits / 100]; - bytes[i++] = ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10]; - bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits % 10]; - count -= 3; - } - if (count == 2) { - // Two digits left over to read, encoded in 7 bits - int twoDigitsBits = bits->readBits(7); - if (twoDigitsBits >= 100) { - ostringstream s; - s << "Illegal value for 2-digit unit: " << twoDigitsBits; - delete[] bytes; - throw ReaderException(s.str().c_str()); - } - bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits / 10]; - bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits % 10]; - } else if (count == 1) { - // One digit left over to read - int digitBits = bits->readBits(4); - if (digitBits >= 10) { - ostringstream s; - s << "Illegal value for digit unit: " << digitBits; - delete[] bytes; - throw ReaderException(s.str().c_str()); - } - bytes[i++] = ALPHANUMERIC_CHARS[digitBits]; - } - append(result, bytes, nBytes, ASCII); - delete[] bytes; - } - - void DecodedBitStreamParser::decodeAlphanumericSegment(Ref bits, std::string &result, int count) { - int nBytes = count; - unsigned char* bytes = new unsigned char[nBytes]; - int i = 0; - // Read two characters at a time - while (count > 1) { - int nextTwoCharsBits = bits->readBits(11); - bytes[i++] = ALPHANUMERIC_CHARS[nextTwoCharsBits / 45]; - bytes[i++] = ALPHANUMERIC_CHARS[nextTwoCharsBits % 45]; - count -= 2; - } - if (count == 1) { - bytes[i++] = ALPHANUMERIC_CHARS[bits->readBits(6)]; - } - append(result, bytes, nBytes, ASCII); - delete[] bytes; - } - - const char * - DecodedBitStreamParser::guessEncoding(unsigned char *bytes, int length) { - const bool ASSUME_SHIFT_JIS = false; - char const* const PLATFORM_DEFAULT_ENCODING="UTF-8"; - - // Does it start with the UTF-8 byte order mark? then guess it's UTF-8 - if (length > 3 && bytes[0] == (unsigned char)0xEF && bytes[1] == (unsigned char)0xBB && bytes[2] - == (unsigned char)0xBF) { - return UTF8; - } - // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, - // which should be by far the most common encodings. ISO-8859-1 - // should not have bytes in the 0x80 - 0x9F range, while Shift_JIS - // uses this as a first byte of a two-byte character. If we see this - // followed by a valid second byte in Shift_JIS, assume it is Shift_JIS. - // If we see something else in that second byte, we'll make the risky guess - // that it's UTF-8. - bool canBeISO88591 = true; - bool canBeShiftJIS = true; - bool canBeUTF8 = true; - int utf8BytesLeft = 0; - int maybeDoubleByteCount = 0; - int maybeSingleByteKatakanaCount = 0; - bool sawLatin1Supplement = false; - bool sawUTF8Start = false; - bool lastWasPossibleDoubleByteStart = false; - for (int i = 0; - i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); - i++) { - int value = bytes[i] & 0xFF; - - // UTF-8 stuff - if (value >= 0x80 && value <= 0xBF) { - if (utf8BytesLeft > 0) { - utf8BytesLeft--; - } - } else { - if (utf8BytesLeft > 0) { - canBeUTF8 = false; - } - if (value >= 0xC0 && value <= 0xFD) { - sawUTF8Start = true; - int valueCopy = value; - while ((valueCopy & 0x40) != 0) { - utf8BytesLeft++; - valueCopy <<= 1; - } - } - } - - // Shift_JIS stuff - - if (value >= 0xA1 && value <= 0xDF) { - // count the number of characters that might be a Shift_JIS single-byte Katakana character - if (!lastWasPossibleDoubleByteStart) { - maybeSingleByteKatakanaCount++; - } - } - if (!lastWasPossibleDoubleByteStart && - ((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) { - canBeShiftJIS = false; - } - if (((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF))) { - // These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid - // second byte. - if (lastWasPossibleDoubleByteStart) { - // If we just checked this and the last byte for being a valid double-byte - // char, don't check starting on this byte. If this and the last byte - // formed a valid pair, then this shouldn't be checked to see if it starts - // a double byte pair of course. - lastWasPossibleDoubleByteStart = false; - } else { - // ... otherwise do check to see if this plus the next byte form a valid - // double byte pair encoding a character. - lastWasPossibleDoubleByteStart = true; - if (i >= length - 1) { - canBeShiftJIS = false; - } else { - int nextValue = bytes[i + 1] & 0xFF; - if (nextValue < 0x40 || nextValue > 0xFC) { - canBeShiftJIS = false; - } else { - maybeDoubleByteCount++; - } - // There is some conflicting information out there about which bytes can follow which in - // double-byte Shift_JIS characters. The rule above seems to be the one that matches practice. - } - } - } else { - lastWasPossibleDoubleByteStart = false; - } - } - if (utf8BytesLeft > 0) { - canBeUTF8 = false; - } - - // Easy -- if assuming Shift_JIS and no evidence it can't be, done - if (canBeShiftJIS && ASSUME_SHIFT_JIS) { - return SHIFT_JIS; - } - if (canBeUTF8 && sawUTF8Start) { - return UTF8; - } - // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is: - // - If we saw - // - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or - // - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1), - // - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS - if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) { - return SHIFT_JIS; - } - // Otherwise, we default to ISO-8859-1 unless we know it can't be - if (!sawLatin1Supplement && canBeISO88591) { - return ISO88591; - } - // Otherwise, we take a wild guess with platform encoding - return PLATFORM_DEFAULT_ENCODING; - } - - string DecodedBitStreamParser::decode(ArrayRef bytes, Version *version) { - string result; - Ref bits(new BitSource(bytes)); - Mode *mode = &Mode::TERMINATOR; - do { - // While still another segment to read... - if (bits->available() < 4) { - // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here - mode = &Mode::TERMINATOR; - } else { - mode = &Mode::forBits(bits->readBits(4)); // mode is encoded by 4 bits - } - if (mode != &Mode::TERMINATOR) { - // How many characters will follow, encoded in this mode? - int count = bits->readBits(mode->getCharacterCountBits(version)); - if (mode == &Mode::NUMERIC) { - decodeNumericSegment(bits, result, count); - } else if (mode == &Mode::ALPHANUMERIC) { - decodeAlphanumericSegment(bits, result, count); - } else if (mode == &Mode::BYTE) { - decodeByteSegment(bits, result, count); - } else if (mode == &Mode::KANJI) { - decodeKanjiSegment(bits, result, count); - } else { - throw ReaderException("Unsupported mode indicator"); - } - } - } while (mode != &Mode::TERMINATOR); - return result; - } - - } +const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] = +{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' +}; + +namespace {int GB2312_SUBSET = 1;} + +void DecodedBitStreamParser::append(std::string &result, + string const& in, + const char *src) { + append(result, (unsigned char const*)in.c_str(), in.length(), src); } +void DecodedBitStreamParser::append(std::string &result, + const unsigned char *bufIn, + size_t nIn, + const char *src) { +#ifndef NO_ICONV + if (nIn == 0) { + return; + } + + iconv_t cd = iconv_open(StringUtils::UTF8, src); + if (cd == (iconv_t)-1) { + result.append((const char *)bufIn, nIn); + return; + } + + const int maxOut = 4 * nIn + 1; + unsigned char* bufOut = new unsigned char[maxOut]; + + ICONV_CONST char *fromPtr = (ICONV_CONST char *)bufIn; + size_t nFrom = nIn; + char *toPtr = (char *)bufOut; + size_t nTo = maxOut; + + while (nFrom > 0) { + size_t oneway = iconv(cd, &fromPtr, &nFrom, &toPtr, &nTo); + if (oneway == (size_t)(-1)) { + iconv_close(cd); + delete[] bufOut; + throw ReaderException("error converting characters"); + } + } + iconv_close(cd); + + int nResult = maxOut - nTo; + bufOut[nResult] = '\0'; + result.append((const char *)bufOut); + delete[] bufOut; +#else + result.append((const char *)bufIn, nIn); +#endif +} + +void DecodedBitStreamParser::decodeHanziSegment(Ref bits_, + string& result, + int count) { + BitSource& bits (*bits_); + // Don't crash trying to read more bits than we have available. + if (count * 13 > bits.available()) { + throw FormatException(); + } + + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as GB2312 afterwards + size_t nBytes = 2 * count; + unsigned char* buffer = new unsigned char[nBytes]; + int offset = 0; + while (count > 0) { + // Each 13 bits encodes a 2-byte character + int twoBytes = bits.readBits(13); + int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060); + if (assembledTwoBytes < 0x003BF) { + // In the 0xA1A1 to 0xAAFE range + assembledTwoBytes += 0x0A1A1; + } else { + // In the 0xB0A1 to 0xFAFE range + assembledTwoBytes += 0x0A6A1; + } + buffer[offset] = (unsigned char) ((assembledTwoBytes >> 8) & 0xFF); + buffer[offset + 1] = (unsigned char) (assembledTwoBytes & 0xFF); + offset += 2; + count--; + } + + try { + append(result, buffer, nBytes, StringUtils::GB2312); + } catch (ReaderException const& re) { + delete [] buffer; + throw FormatException(); + } + + delete [] buffer; + } + +void DecodedBitStreamParser::decodeKanjiSegment(Ref bits, std::string &result, int count) { + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as Shift_JIS afterwards + size_t nBytes = 2 * count; + unsigned char* buffer = new unsigned char[nBytes]; + int offset = 0; + while (count > 0) { + // Each 13 bits encodes a 2-byte character + + int twoBytes = bits->readBits(13); + int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); + if (assembledTwoBytes < 0x01F00) { + // In the 0x8140 to 0x9FFC range + assembledTwoBytes += 0x08140; + } else { + // In the 0xE040 to 0xEBBF range + assembledTwoBytes += 0x0C140; + } + buffer[offset] = (unsigned char)(assembledTwoBytes >> 8); + buffer[offset + 1] = (unsigned char)assembledTwoBytes; + offset += 2; + count--; + } + + append(result, buffer, nBytes, StringUtils::SHIFT_JIS); + delete[] buffer; +} + +void DecodedBitStreamParser::decodeByteSegment(Ref bits_, + string& result, + int count, + CharacterSetECI* currentCharacterSetECI, + ArrayRef< ArrayRef >& byteSegments, + Hashtable const& hints) { + int nBytes = count; + BitSource& bits (*bits_); + // Don't crash trying to read more bits than we have available. + if (count << 3 > bits.available()) { + throw FormatException(); + } + + ArrayRef bytes_ (count); + unsigned char* readBytes = &(*bytes_)[0]; + for (int i = 0; i < count; i++) { + readBytes[i] = (unsigned char) bits.readBits(8); + } + string encoding; + if (currentCharacterSetECI == 0) { + // The spec isn't clear on this mode; see + // section 6.4.5: t does not say which encoding to assuming + // upon decoding. I have seen ISO-8859-1 used as well as + // Shift_JIS -- without anything like an ECI designator to + // give a hint. + encoding = StringUtils::guessEncoding(readBytes, count, hints); + } else { + encoding = currentCharacterSetECI->getEncodingName(); + } + try { + append(result, readBytes, nBytes, encoding.c_str()); + } catch (ReaderException const& re) { + throw FormatException(); + } + byteSegments->values().push_back(bytes_); +} + +void DecodedBitStreamParser::decodeNumericSegment(Ref bits, std::string &result, int count) { + int nBytes = count; + unsigned char* bytes = new unsigned char[nBytes]; + int i = 0; + // Read three digits at a time + while (count >= 3) { + // Each 10 bits encodes three digits + if (bits->available() < 10) { + throw ReaderException("format exception"); + } + int threeDigitsBits = bits->readBits(10); + if (threeDigitsBits >= 1000) { + ostringstream s; + s << "Illegal value for 3-digit unit: " << threeDigitsBits; + delete[] bytes; + throw ReaderException(s.str().c_str()); + } + bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits / 100]; + bytes[i++] = ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10]; + bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits % 10]; + count -= 3; + } + if (count == 2) { + if (bits->available() < 7) { + throw ReaderException("format exception"); + } + // Two digits left over to read, encoded in 7 bits + int twoDigitsBits = bits->readBits(7); + if (twoDigitsBits >= 100) { + ostringstream s; + s << "Illegal value for 2-digit unit: " << twoDigitsBits; + delete[] bytes; + throw ReaderException(s.str().c_str()); + } + bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits / 10]; + bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits % 10]; + } else if (count == 1) { + if (bits->available() < 4) { + throw ReaderException("format exception"); + } + // One digit left over to read + int digitBits = bits->readBits(4); + if (digitBits >= 10) { + ostringstream s; + s << "Illegal value for digit unit: " << digitBits; + delete[] bytes; + throw ReaderException(s.str().c_str()); + } + bytes[i++] = ALPHANUMERIC_CHARS[digitBits]; + } + append(result, bytes, nBytes, StringUtils::ASCII); + delete[] bytes; +} + +char DecodedBitStreamParser::toAlphaNumericChar(size_t value) { + if (value >= sizeof(DecodedBitStreamParser::ALPHANUMERIC_CHARS)) { + throw FormatException(); + } + return ALPHANUMERIC_CHARS[value]; +} + +void DecodedBitStreamParser::decodeAlphanumericSegment(Ref bits_, + string& result, + int count, + bool fc1InEffect) { + BitSource& bits (*bits_); + ostringstream bytes; + // Read two characters at a time + while (count > 1) { + int nextTwoCharsBits = bits.readBits(11); + bytes << toAlphaNumericChar(nextTwoCharsBits / 45); + bytes << toAlphaNumericChar(nextTwoCharsBits % 45); + count -= 2; + } + if (count == 1) { + // special case: one character left + bytes << toAlphaNumericChar(bits.readBits(6)); + } + // See section 6.4.8.1, 6.4.8.2 + string s = bytes.str(); + if (fc1InEffect) { + // We need to massage the result a bit if in an FNC1 mode: + ostringstream r; + for (size_t i = 0; i < s.length(); i++) { + if (s[i] != '%') { + r << s[i]; + } else { + if (i < s.length() - 1 && s[i + 1] == '%') { + // %% is rendered as % + r << s[i++]; + } else { + // In alpha mode, % should be converted to FNC1 separator 0x1D + r << (char)0x1D; + } + } + } + s = r.str(); + } + append(result, s, StringUtils::ASCII); +} + +namespace { + int parseECIValue(BitSource bits) { + int firstByte = bits.readBits(8); + if ((firstByte & 0x80) == 0) { + // just one byte + return firstByte & 0x7F; + } + if ((firstByte & 0xC0) == 0x80) { + // two bytes + int secondByte = bits.readBits(8); + return ((firstByte & 0x3F) << 8) | secondByte; + } + if ((firstByte & 0xE0) == 0xC0) { + // three bytes + int secondThirdBytes = bits.readBits(16); + return ((firstByte & 0x1F) << 16) | secondThirdBytes; + } + throw IllegalArgumentException("Bad ECI bits starting with byte " + firstByte); + } +} + +Ref +DecodedBitStreamParser::decode(ArrayRef bytes, + Version* version, + ErrorCorrectionLevel const& ecLevel, + Hashtable const& hints) { + Ref bits_ (new BitSource(bytes)); + BitSource& bits (*bits_); + string result; + CharacterSetECI* currentCharacterSetECI = 0; + bool fc1InEffect = false; + ArrayRef< ArrayRef > byteSegments (size_t(0)); + Mode* mode = 0; + do { + // While still another segment to read... + if (bits.available() < 4) { + // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here + mode = &Mode::TERMINATOR; + } else { + try { + mode = &Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits + } catch (IllegalArgumentException const& iae) { + throw iae; + // throw FormatException.getFormatInstance(); + } + } + if (mode != &Mode::TERMINATOR) { + if ((mode == &Mode::FNC1_FIRST_POSITION) || (mode == &Mode::FNC1_SECOND_POSITION)) { + // We do little with FNC1 except alter the parsed result a bit according to the spec + fc1InEffect = true; + } else if (mode == &Mode::STRUCTURED_APPEND) { + // not really supported; all we do is ignore it + // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue + bits.readBits(16); + } else if (mode == &Mode::ECI) { + // Count doesn't apply to ECI + int value = parseECIValue(bits); + currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value); + if (currentCharacterSetECI == 0) { + throw FormatException(); + } + } else { + // First handle Hanzi mode which does not start with character count + if (mode == &Mode::HANZI) { + //chinese mode contains a sub set indicator right after mode indicator + int subset = bits.readBits(4); + int countHanzi = bits.readBits(mode->getCharacterCountBits(version)); + if (subset == GB2312_SUBSET) { + decodeHanziSegment(bits_, result, countHanzi); + } + } else { + // "Normal" QR code modes: + // How many characters will follow, encoded in this mode? + int count = bits.readBits(mode->getCharacterCountBits(version)); + if (mode == &Mode::NUMERIC) { + decodeNumericSegment(bits_, result, count); + } else if (mode == &Mode::ALPHANUMERIC) { + decodeAlphanumericSegment(bits_, result, count, fc1InEffect); + } else if (mode == &Mode::BYTE) { + decodeByteSegment(bits_, result, count, currentCharacterSetECI, byteSegments, hints); + } else if (mode == &Mode::KANJI) { + decodeKanjiSegment(bits_, result, count); + } else { + throw FormatException(); + } + } + } + } + } while (mode != &Mode::TERMINATOR); + + return Ref(new DecoderResult(bytes, Ref(new String(result)), byteSegments, (string)ecLevel)); +} + + // file: zxing/qrcode/decoder/Decoder.cpp /* @@ -8577,82 +10418,82 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - Decoder::Decoder() : - rsDecoder_(GF256::QR_CODE_FIELD) { - } - - void Decoder::correctErrors(ArrayRef codewordBytes, int numDataCodewords) { - int numCodewords = codewordBytes->size(); - ArrayRef codewordInts(numCodewords); - for (int i = 0; i < numCodewords; i++) { - codewordInts[i] = codewordBytes[i] & 0xff; - } - int numECCodewords = numCodewords - numDataCodewords; - - try { - rsDecoder_.decode(codewordInts, numECCodewords); - } catch (ReedSolomonException ex) { - ReaderException rex(ex.what()); - throw rex; - } - - for (int i = 0; i < numDataCodewords; i++) { - codewordBytes[i] = (unsigned char)codewordInts[i]; - } - } - - Ref Decoder::decode(Ref bits) { - // Construct a parser and read version, error-correction level - BitMatrixParser parser(bits); - - Version *version = parser.readVersion(); - ErrorCorrectionLevel &ecLevel = parser.readFormatInformation()->getErrorCorrectionLevel(); - - - // Read codewords - ArrayRef codewords(parser.readCodewords()); - - - // Separate into data blocks - std::vector > dataBlocks(DataBlock::getDataBlocks(codewords, version, ecLevel)); - - - // Count total number of data bytes - int totalBytes = 0; - for (size_t i = 0; i < dataBlocks.size(); i++) { - totalBytes += dataBlocks[i]->getNumDataCodewords(); - } - ArrayRef resultBytes(totalBytes); - int resultOffset = 0; - - - // Error-correct and copy data blocks together into a stream of bytes - for (size_t j = 0; j < dataBlocks.size(); j++) { - Ref dataBlock(dataBlocks[j]); - ArrayRef codewordBytes = dataBlock->getCodewords(); - int numDataCodewords = dataBlock->getNumDataCodewords(); - correctErrors(codewordBytes, numDataCodewords); - for (int i = 0; i < numDataCodewords; i++) { - resultBytes[resultOffset++] = codewordBytes[i]; - } - } - - // Decode the contents of that stream of bytes - Ref text(new String(DecodedBitStreamParser::decode(resultBytes, version))); - - Ref result(new DecoderResult(resultBytes, text)); - return result; - } - +namespace qrcode { + +using namespace std; + +Decoder::Decoder() : + rsDecoder_(GF256::QR_CODE_FIELD) { +} + +void Decoder::correctErrors(ArrayRef codewordBytes, int numDataCodewords) { + int numCodewords = codewordBytes->size(); + ArrayRef codewordInts(numCodewords); + for (int i = 0; i < numCodewords; i++) { + codewordInts[i] = codewordBytes[i] & 0xff; + } + int numECCodewords = numCodewords - numDataCodewords; + + try { + rsDecoder_.decode(codewordInts, numECCodewords); + } catch (ReedSolomonException const& ex) { + ReaderException rex(ex.what()); + throw rex; + } + + for (int i = 0; i < numDataCodewords; i++) { + codewordBytes[i] = (unsigned char)codewordInts[i]; + } +} + +Ref Decoder::decode(Ref bits) { + // Construct a parser and read version, error-correction level + BitMatrixParser parser(bits); + + Version *version = parser.readVersion(); + ErrorCorrectionLevel &ecLevel = parser.readFormatInformation()->getErrorCorrectionLevel(); + + + // Read codewords + ArrayRef codewords(parser.readCodewords()); + + + // Separate into data blocks + std::vector > dataBlocks(DataBlock::getDataBlocks(codewords, version, ecLevel)); + + + // Count total number of data bytes + int totalBytes = 0; + for (size_t i = 0; i < dataBlocks.size(); i++) { + totalBytes += dataBlocks[i]->getNumDataCodewords(); + } + ArrayRef resultBytes(totalBytes); + int resultOffset = 0; + + + // Error-correct and copy data blocks together into a stream of bytes + for (size_t j = 0; j < dataBlocks.size(); j++) { + Ref dataBlock(dataBlocks[j]); + ArrayRef codewordBytes = dataBlock->getCodewords(); + int numDataCodewords = dataBlock->getNumDataCodewords(); + correctErrors(codewordBytes, numDataCodewords); + for (int i = 0; i < numDataCodewords; i++) { + resultBytes[resultOffset++] = codewordBytes[i]; } + } + + return DecodedBitStreamParser::decode(resultBytes, + version, + ecLevel, + DecodedBitStreamParser::Hashtable()); +} + +} } // file: zxing/qrcode/decoder/Mode.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * Mode.cpp * zxing @@ -8679,56 +10520,69 @@ namespace zxing { // #include // #include -namespace zxing { - namespace qrcode { - using namespace std; - - Mode Mode::TERMINATOR(0, 0, 0); - Mode Mode::NUMERIC(10, 12, 14); - Mode Mode::ALPHANUMERIC(9, 11, 13); - Mode Mode::BYTE(8, 16, 16); - Mode Mode::KANJI(8, 10, 12); - - Mode::Mode(int cbv0_9, int cbv10_26, int cbv27) : - characterCountBitsForVersions0To9_(cbv0_9), characterCountBitsForVersions10To26_(cbv10_26), - characterCountBitsForVersions27AndHigher_(cbv27) { - } - - Mode& Mode::forBits(int bits) { - switch (bits) { - case 0x0: - return TERMINATOR; - case 0x1: - return NUMERIC; - case 0x2: - return ALPHANUMERIC; - case 0x4: - return BYTE; - case 0x8: - return KANJI; - default: - ostringstream s; - s << "Illegal mode bits: " << bits; - throw ReaderException(s.str().c_str()); - } - } - - int Mode::getCharacterCountBits(Version *version) { - int number = version->getVersionNumber(); - if (number <= 9) { - return characterCountBitsForVersions0To9_; - } else if (number <= 26) { - return characterCountBitsForVersions10To26_; - } else { - return characterCountBitsForVersions27AndHigher_; - } - } - +using zxing::qrcode::Mode; +using std::ostringstream; + +Mode Mode::TERMINATOR(0, 0, 0, 0x00, "TERMINATOR"); +Mode Mode::NUMERIC(10, 12, 14, 0x01, "NUMERIC"); +Mode Mode::ALPHANUMERIC(9, 11, 13, 0x02, "ALPHANUMERIC"); +Mode Mode::STRUCTURED_APPEND(0, 0, 0, 0x03, "STRUCTURED_APPEND"); +Mode Mode::BYTE(8, 16, 16, 0x04, "BYTE"); +Mode Mode::ECI(0, 0, 0, 0x07, "ECI"); +Mode Mode::KANJI(8, 10, 12, 0x08, "KANJI"); +Mode Mode::FNC1_FIRST_POSITION(0, 0, 0, 0x05, "FNC1_FIRST_POSITION"); +Mode Mode::FNC1_SECOND_POSITION(0, 0, 0, 0x09, "FNC1_SECOND_POSITION"); +Mode Mode::HANZI(8, 10, 12, 0x0D, "HANZI"); + +Mode::Mode(int cbv0_9, int cbv10_26, int cbv27, int bits, char const* name) : + characterCountBitsForVersions0To9_(cbv0_9), characterCountBitsForVersions10To26_(cbv10_26), + characterCountBitsForVersions27AndHigher_(cbv27), bits_(bits), name_(name) { +} + +Mode& Mode::forBits(int bits) { + switch (bits) { + case 0x0: + return TERMINATOR; + case 0x1: + return NUMERIC; + case 0x2: + return ALPHANUMERIC; + case 0x3: + return STRUCTURED_APPEND; + case 0x4: + return BYTE; + case 0x5: + return FNC1_FIRST_POSITION; + case 0x7: + return ECI; + case 0x8: + return KANJI; + case 0x9: + return FNC1_SECOND_POSITION; + case 0xD: + // 0xD is defined in GBT 18284-2000, may not be supported in foreign country + return HANZI; + default: + ostringstream s; + s << "Illegal mode bits: " << bits; + throw ReaderException(s.str().c_str()); } } +int Mode::getCharacterCountBits(Version *version) { + int number = version->getVersionNumber(); + if (number <= 9) { + return characterCountBitsForVersions0To9_; + } else if (number <= 26) { + return characterCountBitsForVersions10To26_; + } else { + return characterCountBitsForVersions27AndHigher_; + } +} + // file: zxing/qrcode/detector/AlignmentPattern.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * AlignmentPattern.cpp * zxing @@ -8752,32 +10606,37 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - AlignmentPattern::AlignmentPattern(float posX, float posY, float estimatedModuleSize) : - posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize) { - } - - float AlignmentPattern::getX() const { - return posX_; - } - - float AlignmentPattern::getY() const { - return posY_; - } - - bool AlignmentPattern::aboutEquals(float moduleSize, float i, float j) const { - return abs(i - posY_) <= moduleSize && abs(j - posX_) <= moduleSize && (abs(moduleSize - estimatedModuleSize_) - <= 1.0f || abs(moduleSize - estimatedModuleSize_) / estimatedModuleSize_ <= 0.1f); - } - - } +namespace qrcode { + +using namespace std; + +AlignmentPattern::AlignmentPattern(float posX, float posY, float estimatedModuleSize) : + ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize) { +} + +bool AlignmentPattern::aboutEquals(float moduleSize, float i, float j) const { + if (abs(i - getY()) <= moduleSize && abs(j - getX()) <= moduleSize) { + float moduleSizeDiff = abs(moduleSize - estimatedModuleSize_); + return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize_; + } + return false; +} + +Ref AlignmentPattern::combineEstimate(float i, float j, float newModuleSize) const { + float combinedX = (getX() + j) / 2.0f; + float combinedY = (getY() + i) / 2.0f; + float combinedModuleSize = (estimatedModuleSize_ + newModuleSize) / 2.0f; + Ref result + (new AlignmentPattern(combinedX, combinedY, combinedModuleSize)); + return result; +} + +} } // file: zxing/qrcode/detector/AlignmentPatternFinder.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * AlignmentPatternFinder.cpp * zxing @@ -8806,190 +10665,190 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - float AlignmentPatternFinder::centerFromEnd(vector &stateCount, int end) { - return (float)(end - stateCount[2]) - stateCount[1] / 2.0f; - } - - bool AlignmentPatternFinder::foundPatternCross(vector &stateCount) { - float maxVariance = moduleSize_ / 2.0f; - for (size_t i = 0; i < 3; i++) { - if (abs(moduleSize_ - stateCount[i]) >= maxVariance) { - return false; - } - } - return true; - } - - float AlignmentPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount, - int originalStateCountTotal) { - int maxI = image_->getHeight(); - vector stateCount(3, 0); - - - // Start counting up from center - int i = startI; - while (i >= 0 && image_->get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return NAN; - } - while (i >= 0 && !image_->get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return NAN; - } - - // Now also count down from center - i = startI + 1; - while (i < maxI && image_->get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i++; - } - if (i == maxI || stateCount[1] > maxCount) { - return NAN; - } - while (i < maxI && !image_->get(centerJ, i) && stateCount[2] <= maxCount) { - stateCount[2]++; - i++; - } - if (stateCount[2] > maxCount) { - return NAN; - } - - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; - if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return NAN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN; - } - - Ref AlignmentPatternFinder::handlePossibleCenter(vector &stateCount, size_t i, size_t j) { - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; - float centerJ = centerFromEnd(stateCount, j); - float centerI = crossCheckVertical(i, (int)centerJ, 2 * stateCount[1], stateCountTotal); - if (!isnan(centerI)) { - float estimatedModuleSize = (float)(stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f; - int max = possibleCenters_->size(); - for (int index = 0; index < max; index++) { - Ref center((*possibleCenters_)[index]); - // Look for about the same center and module size: - if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) { - Ref result(new AlignmentPattern(centerJ, centerI, estimatedModuleSize)); - return result; - } - } - AlignmentPattern *tmp = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); - // Hadn't found this before; save it - tmp->retain(); - possibleCenters_->push_back(tmp); - if (callback_ != 0) { - callback_->foundPossibleResultPoint(*tmp); - } - } - Ref result; - return result; - } - - AlignmentPatternFinder::AlignmentPatternFinder(Ref image, size_t startX, size_t startY, size_t width, - size_t height, float moduleSize, - Refconst& callback) : - image_(image), possibleCenters_(new vector ()), startX_(startX), startY_(startY), - width_(width), height_(height), moduleSize_(moduleSize), callback_(callback) { - } - - AlignmentPatternFinder::~AlignmentPatternFinder() { - for (size_t i = 0; i < possibleCenters_->size(); i++) { - (*possibleCenters_)[i]->release(); - (*possibleCenters_)[i] = 0; - } - delete possibleCenters_; - } - - Ref AlignmentPatternFinder::find() { - size_t maxJ = startX_ + width_; - size_t middleI = startY_ + (height_ >> 1); - // Ref luminanceRow(new BitArray(width_)); - // We are looking for black/white/black modules in 1:1:1 ratio; - // this tracks the number of black/white/black modules seen so far - vector stateCount(3, 0); - for (size_t iGen = 0; iGen < height_; iGen++) { - // Search from middle outwards - size_t i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1)); - // image_->getBlackRow(i, luminanceRow, startX_, width_); - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - size_t j = startX_; - // Burn off leading white pixels before anything else; if we start in the middle of - // a white run, it doesn't make sense to count its length, since we don't know if the - // white run continued to the left of the start point - while (j < maxJ && !image_->get(j, i)) { - j++; - } - int currentState = 0; - while (j < maxJ) { - if (image_->get(j, i)) { - // Black pixel - if (currentState == 1) { // Counting black pixels - stateCount[currentState]++; - } else { // Counting white pixels - if (currentState == 2) { // A winner? - if (foundPatternCross(stateCount)) { // Yes - Ref confirmed(handlePossibleCenter(stateCount, i, j)); - if (confirmed != 0) { - return confirmed; - } - } - stateCount[0] = stateCount[2]; - stateCount[1] = 1; - stateCount[2] = 0; - currentState = 1; - } else { - stateCount[++currentState]++; - } - } - } else { // White pixel - if (currentState == 1) { // Counting black pixels - currentState++; - } - stateCount[currentState]++; - } - j++; - } - if (foundPatternCross(stateCount)) { - Ref confirmed(handlePossibleCenter(stateCount, i, maxJ)); - if (confirmed != 0) { - return confirmed; - } - } - - } - - // Hmm, nothing we saw was observed and confirmed twice. If we had - // any guess at all, return it. - if (possibleCenters_->size() > 0) { - Ref center((*possibleCenters_)[0]); - return center; - } - - throw zxing::ReaderException("Could not find alignment pattern"); - } - +namespace qrcode { + +using namespace std; + +float AlignmentPatternFinder::centerFromEnd(vector &stateCount, int end) { + return (float)(end - stateCount[2]) - stateCount[1] / 2.0f; +} + +bool AlignmentPatternFinder::foundPatternCross(vector &stateCount) { + float maxVariance = moduleSize_ / 2.0f; + for (size_t i = 0; i < 3; i++) { + if (abs(moduleSize_ - stateCount[i]) >= maxVariance) { + return false; } + } + return true; +} + +float AlignmentPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount, + int originalStateCountTotal) { + int maxI = image_->getHeight(); + vector stateCount(3, 0); + + + // Start counting up from center + int i = startI; + while (i >= 0 && image_->get(centerJ, i) && stateCount[1] <= maxCount) { + stateCount[1]++; + i--; + } + // If already too many modules in this state or ran off the edge: + if (i < 0 || stateCount[1] > maxCount) { + return NAN; + } + while (i >= 0 && !image_->get(centerJ, i) && stateCount[0] <= maxCount) { + stateCount[0]++; + i--; + } + if (stateCount[0] > maxCount) { + return NAN; + } + + // Now also count down from center + i = startI + 1; + while (i < maxI && image_->get(centerJ, i) && stateCount[1] <= maxCount) { + stateCount[1]++; + i++; + } + if (i == maxI || stateCount[1] > maxCount) { + return NAN; + } + while (i < maxI && !image_->get(centerJ, i) && stateCount[2] <= maxCount) { + stateCount[2]++; + i++; + } + if (stateCount[2] > maxCount) { + return NAN; + } + + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; + if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { + return NAN; + } + + return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN; +} + +Ref AlignmentPatternFinder::handlePossibleCenter(vector &stateCount, size_t i, size_t j) { + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; + float centerJ = centerFromEnd(stateCount, j); + float centerI = crossCheckVertical(i, (int)centerJ, 2 * stateCount[1], stateCountTotal); + if (!isnan(centerI)) { + float estimatedModuleSize = (float)(stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f; + int max = possibleCenters_->size(); + for (int index = 0; index < max; index++) { + Ref center((*possibleCenters_)[index]); + // Look for about the same center and module size: + if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) { + return center->combineEstimate(centerI, centerJ, estimatedModuleSize); + } + } + AlignmentPattern *tmp = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); + // Hadn't found this before; save it + tmp->retain(); + possibleCenters_->push_back(tmp); + if (callback_ != 0) { + callback_->foundPossibleResultPoint(*tmp); + } + } + Ref result; + return result; +} + +AlignmentPatternFinder::AlignmentPatternFinder(Ref image, size_t startX, size_t startY, size_t width, + size_t height, float moduleSize, + Refconst& callback) : + image_(image), possibleCenters_(new vector ()), startX_(startX), startY_(startY), + width_(width), height_(height), moduleSize_(moduleSize), callback_(callback) { +} + +AlignmentPatternFinder::~AlignmentPatternFinder() { + for (size_t i = 0; i < possibleCenters_->size(); i++) { + (*possibleCenters_)[i]->release(); + (*possibleCenters_)[i] = 0; + } + delete possibleCenters_; +} + +Ref AlignmentPatternFinder::find() { + size_t maxJ = startX_ + width_; + size_t middleI = startY_ + (height_ >> 1); + // Ref luminanceRow(new BitArray(width_)); + // We are looking for black/white/black modules in 1:1:1 ratio; + // this tracks the number of black/white/black modules seen so far + vector stateCount(3, 0); + for (size_t iGen = 0; iGen < height_; iGen++) { + // Search from middle outwards + size_t i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1)); + // image_->getBlackRow(i, luminanceRow, startX_, width_); + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + size_t j = startX_; + // Burn off leading white pixels before anything else; if we start in the middle of + // a white run, it doesn't make sense to count its length, since we don't know if the + // white run continued to the left of the start point + while (j < maxJ && !image_->get(j, i)) { + j++; + } + int currentState = 0; + while (j < maxJ) { + if (image_->get(j, i)) { + // Black pixel + if (currentState == 1) { // Counting black pixels + stateCount[currentState]++; + } else { // Counting white pixels + if (currentState == 2) { // A winner? + if (foundPatternCross(stateCount)) { // Yes + Ref confirmed(handlePossibleCenter(stateCount, i, j)); + if (confirmed != 0) { + return confirmed; + } + } + stateCount[0] = stateCount[2]; + stateCount[1] = 1; + stateCount[2] = 0; + currentState = 1; + } else { + stateCount[++currentState]++; + } + } + } else { // White pixel + if (currentState == 1) { // Counting black pixels + currentState++; + } + stateCount[currentState]++; + } + j++; + } + if (foundPatternCross(stateCount)) { + Ref confirmed(handlePossibleCenter(stateCount, i, maxJ)); + if (confirmed != 0) { + return confirmed; + } + } + + } + + // Hmm, nothing we saw was observed and confirmed twice. If we had + // any guess at all, return it. + if (possibleCenters_->size() > 0) { + Ref center((*possibleCenters_)[0]); + return center; + } + + throw zxing::ReaderException("Could not find alignment pattern"); +} + +} } // file: zxing/qrcode/detector/Detector.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * Detector.cpp * zxing @@ -9023,262 +10882,274 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - Detector::Detector(Ref image) : - image_(image) { - } - - Ref Detector::getImage() { - return image_; - } - - Ref Detector::detect(DecodeHints const& hints) { - callback_ = hints.getResultPointCallback(); - FinderPatternFinder finder(image_, hints.getResultPointCallback()); - Ref info(finder.find(hints)); - - Ref topLeft(info->getTopLeft()); - Ref topRight(info->getTopRight()); - Ref bottomLeft(info->getBottomLeft()); - - float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); - if (moduleSize < 1.0f) { - throw zxing::ReaderException("bad module size"); - } - int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); - Version *provisionalVersion = Version::getProvisionalVersionForDimension(dimension); - int modulesBetweenFPCenters = provisionalVersion->getDimensionForVersion() - 7; - - Ref alignmentPattern; - // Anything above version 1 has an alignment pattern - if (provisionalVersion->getAlignmentPatternCenters().size() > 0) { - - - // Guess where a "bottom right" finder pattern would have been - float bottomRightX = topRight->getX() - topLeft->getX() + bottomLeft->getX(); - float bottomRightY = topRight->getY() - topLeft->getY() + bottomLeft->getY(); - - - // Estimate that alignment pattern is closer by 3 modules - // from "bottom right" to known top left location - float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters; - int estAlignmentX = (int)(topLeft->getX() + correctionToTopLeft * (bottomRightX - topLeft->getX())); - int estAlignmentY = (int)(topLeft->getY() + correctionToTopLeft * (bottomRightY - topLeft->getY())); - - - // Kind of arbitrary -- expand search radius before giving up - for (int i = 4; i <= 16; i <<= 1) { - try { - alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i); - break; - } catch (zxing::ReaderException re) { - // try next round - } - } - if (alignmentPattern == 0) { - // Try anyway - } - - } - - Ref transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); - Ref bits(sampleGrid(image_, dimension, transform)); - std::vector > points(alignmentPattern == 0 ? 3 : 4); - points[0].reset(bottomLeft); - points[1].reset(topLeft); - points[2].reset(topRight); - if (alignmentPattern != 0) { - points[3].reset(alignmentPattern); - } - - Ref result(new DetectorResult(bits, points, transform)); - return result; - } - - Ref Detector::createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref alignmentPattern, int dimension) { - - float dimMinusThree = (float)dimension - 3.5f; - float bottomRightX; - float bottomRightY; - float sourceBottomRightX; - float sourceBottomRightY; - if (alignmentPattern != 0) { - bottomRightX = alignmentPattern->getX(); - bottomRightY = alignmentPattern->getY(); - sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f; - } else { - // Don't have an alignment pattern, just make up the bottom-right point - bottomRightX = (topRight->getX() - topLeft->getX()) + bottomLeft->getX(); - bottomRightY = (topRight->getY() - topLeft->getY()) + bottomLeft->getY(); - sourceBottomRightX = sourceBottomRightY = dimMinusThree; - } - - Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, - sourceBottomRightY, 3.5f, dimMinusThree, topLeft->getX(), topLeft->getY(), topRight->getX(), - topRight->getY(), bottomRightX, bottomRightY, bottomLeft->getX(), bottomLeft->getY())); - - return transform; - } - - Ref Detector::sampleGrid(Ref image, int dimension, Ref transform) { - GridSampler &sampler = GridSampler::getInstance(); - return sampler.sampleGrid(image, dimension, transform); - } - - int Detector::computeDimension(Ref topLeft, Ref topRight, Ref bottomLeft, - float moduleSize) { - int tltrCentersDimension = int(FinderPatternFinder::distance(topLeft, topRight) / moduleSize + 0.5f); - int tlblCentersDimension = int(FinderPatternFinder::distance(topLeft, bottomLeft) / moduleSize + 0.5f); - int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; - switch (dimension & 0x03) { // mod 4 - case 0: - dimension++; - break; - // 1? do nothing - case 2: - dimension--; - break; - case 3: - ostringstream s; - s << "Bad dimension: " << dimension; - throw zxing::ReaderException(s.str().c_str()); - } - return dimension; - } - - float Detector::calculateModuleSize(Ref topLeft, Ref topRight, Ref bottomLeft) { - // Take the average - return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; - } - - float Detector::calculateModuleSizeOneWay(Ref pattern, Ref otherPattern) { - float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern->getX(), (int)pattern->getY(), - (int)otherPattern->getX(), (int)otherPattern->getY()); - float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern->getX(), (int)otherPattern->getY(), - (int)pattern->getX(), (int)pattern->getY()); - if (isnan(moduleSizeEst1)) { - return moduleSizeEst2; - } - if (isnan(moduleSizeEst2)) { - return moduleSizeEst1; - } - // Average them, and divide by 7 since we've counted the width of 3 black modules, - // and 1 white and 1 black module on either side. Ergo, divide sum by 14. - return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; - } - - float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) { - - float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); - - // Now count other way -- don't run off image though of course - float scale = 1.0f; - int otherToX = fromX - (toX - fromX); - if (otherToX < 0) { - scale = (float) fromX / (float) (fromX - otherToX); - otherToX = 0; - } else if (otherToX > (int)image_->getWidth()) { - scale = (float) (image_->getWidth() - fromX) / (float) (otherToX - fromX); - otherToX = image_->getWidth(); - } - int otherToY = (int) (fromY - (toY - fromY) * scale); - - scale = 1.0f; - if (otherToY < 0) { - scale = (float) fromY / (float) (fromY - otherToY); - otherToY = 0; - } else if (otherToY > (int)image_->getHeight()) { - scale = (float) (image_->getHeight() - fromY) / (float) (otherToY - fromY); - otherToY = image_->getHeight(); - } - otherToX = (int) (fromX + (otherToX - fromX) * scale); - - result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); - return result; - } - - float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) { - // Mild variant of Bresenham's algorithm; - // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm - bool steep = abs(toY - fromY) > abs(toX - fromX); - if (steep) { - int temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - - int dx = abs(toX - fromX); - int dy = abs(toY - fromY); - int error = -dx >> 1; - int ystep = fromY < toY ? 1 : -1; - int xstep = fromX < toX ? 1 : -1; - int state = 0; // In black pixels, looking for white, first or second time - for (int x = fromX, y = fromY; x != toX; x += xstep) { - - int realX = steep ? y : x; - int realY = steep ? x : y; - if (state == 1) { // In white pixels, looking for black - if (image_->get(realX, realY)) { - state++; - } - } else { - if (!image_->get(realX, realY)) { - state++; - } - } - - if (state == 3) { // Found black, white, black, and stumbled back onto white; done - int diffX = x - fromX; - int diffY = y - fromY; - if (xstep < 0) { - diffX++; - } - return (float)sqrt((double)(diffX * diffX + diffY * diffY)); - } - error += dy; - if (error > 0) { - y += ystep; - error -= dx; - } - } - int diffX = toX - fromX; - int diffY = toY - fromY; - return (float)sqrt((double)(diffX * diffX + diffY * diffY)); - } - - Ref Detector::findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, - float allowanceFactor) { - // Look for an alignment pattern (3 modules in size) around where it - // should be - int allowance = (int)(allowanceFactor * overallEstModuleSize); - int alignmentAreaLeftX = max(0, estAlignmentX - allowance); - int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance); - if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) { - throw zxing::ReaderException("region too small to hold alignment pattern"); - } - int alignmentAreaTopY = max(0, estAlignmentY - allowance); - int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance); - if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) { - throw zxing::ReaderException("region too small to hold alignment pattern"); - } - - AlignmentPatternFinder alignmentFinder(image_, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, callback_); - return alignmentFinder.find(); - } - +namespace qrcode { + +using namespace std; + +Detector::Detector(Ref image) : + image_(image) { +} + +Ref Detector::getImage() { + return image_; +} + +Ref Detector::detect(DecodeHints const& hints) { + callback_ = hints.getResultPointCallback(); + FinderPatternFinder finder(image_, hints.getResultPointCallback()); + Ref info(finder.find(hints)); + return processFinderPatternInfo(info); +} + +Ref Detector::processFinderPatternInfo(Ref info){ + Ref topLeft(info->getTopLeft()); + Ref topRight(info->getTopRight()); + Ref bottomLeft(info->getBottomLeft()); + + float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); + if (moduleSize < 1.0f) { + throw zxing::ReaderException("bad module size"); + } + int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); + Version *provisionalVersion = Version::getProvisionalVersionForDimension(dimension); + int modulesBetweenFPCenters = provisionalVersion->getDimensionForVersion() - 7; + + Ref alignmentPattern; + // Anything above version 1 has an alignment pattern + if (provisionalVersion->getAlignmentPatternCenters().size() > 0) { + + + // Guess where a "bottom right" finder pattern would have been + float bottomRightX = topRight->getX() - topLeft->getX() + bottomLeft->getX(); + float bottomRightY = topRight->getY() - topLeft->getY() + bottomLeft->getY(); + + + // Estimate that alignment pattern is closer by 3 modules + // from "bottom right" to known top left location + float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters; + int estAlignmentX = (int)(topLeft->getX() + correctionToTopLeft * (bottomRightX - topLeft->getX())); + int estAlignmentY = (int)(topLeft->getY() + correctionToTopLeft * (bottomRightY - topLeft->getY())); + + + // Kind of arbitrary -- expand search radius before giving up + for (int i = 4; i <= 16; i <<= 1) { + try { + alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i); + break; + } catch (zxing::ReaderException const& re) { + // try next round + } } + if (alignmentPattern == 0) { + // Try anyway + } + + } + + Ref transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); + Ref bits(sampleGrid(image_, dimension, transform)); + std::vector > points(alignmentPattern == 0 ? 3 : 4); + points[0].reset(bottomLeft); + points[1].reset(topLeft); + points[2].reset(topRight); + if (alignmentPattern != 0) { + points[3].reset(alignmentPattern); + } + + Ref result(new DetectorResult(bits, points, transform)); + return result; +} + +Ref Detector::createTransform(Ref topLeft, Ref topRight, Ref < + ResultPoint > bottomLeft, Ref alignmentPattern, int dimension) { + + float dimMinusThree = (float)dimension - 3.5f; + float bottomRightX; + float bottomRightY; + float sourceBottomRightX; + float sourceBottomRightY; + if (alignmentPattern != 0) { + bottomRightX = alignmentPattern->getX(); + bottomRightY = alignmentPattern->getY(); + sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f; + } else { + // Don't have an alignment pattern, just make up the bottom-right point + bottomRightX = (topRight->getX() - topLeft->getX()) + bottomLeft->getX(); + bottomRightY = (topRight->getY() - topLeft->getY()) + bottomLeft->getY(); + sourceBottomRightX = sourceBottomRightY = dimMinusThree; + } + + Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX, + sourceBottomRightY, 3.5f, dimMinusThree, topLeft->getX(), topLeft->getY(), topRight->getX(), + topRight->getY(), bottomRightX, bottomRightY, bottomLeft->getX(), bottomLeft->getY())); + + return transform; +} + +Ref Detector::sampleGrid(Ref image, int dimension, Ref transform) { + GridSampler &sampler = GridSampler::getInstance(); + return sampler.sampleGrid(image, dimension, transform); +} + +int Detector::computeDimension(Ref topLeft, Ref topRight, Ref bottomLeft, + float moduleSize) { + int tltrCentersDimension = int(FinderPatternFinder::distance(topLeft, topRight) / moduleSize + 0.5f); + int tlblCentersDimension = int(FinderPatternFinder::distance(topLeft, bottomLeft) / moduleSize + 0.5f); + int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; + switch (dimension & 0x03) { // mod 4 + case 0: + dimension++; + break; + // 1? do nothing + case 2: + dimension--; + break; + case 3: + ostringstream s; + s << "Bad dimension: " << dimension; + throw zxing::ReaderException(s.str().c_str()); + } + return dimension; +} + +float Detector::calculateModuleSize(Ref topLeft, Ref topRight, Ref bottomLeft) { + // Take the average + return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; +} + +float Detector::calculateModuleSizeOneWay(Ref pattern, Ref otherPattern) { + float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern->getX(), (int)pattern->getY(), + (int)otherPattern->getX(), (int)otherPattern->getY()); + float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern->getX(), (int)otherPattern->getY(), + (int)pattern->getX(), (int)pattern->getY()); + if (isnan(moduleSizeEst1)) { + return moduleSizeEst2; + } + if (isnan(moduleSizeEst2)) { + return moduleSizeEst1; + } + // Average them, and divide by 7 since we've counted the width of 3 black modules, + // and 1 white and 1 black module on either side. Ergo, divide sum by 14. + return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; +} + +float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) { + + float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); + + // Now count other way -- don't run off image though of course + float scale = 1.0f; + int otherToX = fromX - (toX - fromX); + if (otherToX < 0) { + scale = (float) fromX / (float) (fromX - otherToX); + otherToX = 0; + } else if (otherToX >= (int)image_->getWidth()) { + scale = (float) (image_->getWidth() - 1 - fromX) / (float) (otherToX - fromX); + otherToX = image_->getWidth() - 1; + } + int otherToY = (int) (fromY - (toY - fromY) * scale); + + scale = 1.0f; + if (otherToY < 0) { + scale = (float) fromY / (float) (fromY - otherToY); + otherToY = 0; + } else if (otherToY >= (int)image_->getHeight()) { + scale = (float) (image_->getHeight() - 1 - fromY) / (float) (otherToY - fromY); + otherToY = image_->getHeight() - 1; + } + otherToX = (int) (fromX + (otherToX - fromX) * scale); + + result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); + + // Middle pixel is double-counted this way; subtract 1 + return result - 1.0f; +} + +float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) { + // Mild variant of Bresenham's algorithm; + // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm + bool steep = abs(toY - fromY) > abs(toX - fromX); + if (steep) { + int temp = fromX; + fromX = fromY; + fromY = temp; + temp = toX; + toX = toY; + toY = temp; + } + + int dx = abs(toX - fromX); + int dy = abs(toY - fromY); + int error = -dx >> 1; + int xstep = fromX < toX ? 1 : -1; + int ystep = fromY < toY ? 1 : -1; + + // In black pixels, looking for white, first or second time. + int state = 0; + // Loop up until x == toX, but not beyond + int xLimit = toX + xstep; + for (int x = fromX, y = fromY; x != xLimit; x += xstep) { + int realX = steep ? y : x; + int realY = steep ? x : y; + + // Does current pixel mean we have moved white to black or vice versa? + if (!((state == 1) ^ image_->get(realX, realY))) { + if (state == 2) { + int diffX = x - fromX; + int diffY = y - fromY; + return (float) sqrt((double) (diffX * diffX + diffY * diffY)); + } + state++; + } + + error += dy; + if (error > 0) { + if (y == toY) { + break; + } + y += ystep; + error -= dx; + } + } + // Found black-white-black; give the benefit of the doubt that the next pixel outside the image + // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a + // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. + if (state == 2) { + int diffX = toX + xstep - fromX; + int diffY = toY - fromY; + return (float) sqrt((double) (diffX * diffX + diffY * diffY)); + } + // else we didn't find even black-white-black; no estimate is really possible + return NAN; +} + +Ref Detector::findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, + float allowanceFactor) { + // Look for an alignment pattern (3 modules in size) around where it + // should be + int allowance = (int)(allowanceFactor * overallEstModuleSize); + int alignmentAreaLeftX = max(0, estAlignmentX - allowance); + int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance); + if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) { + throw zxing::ReaderException("region too small to hold alignment pattern"); + } + int alignmentAreaTopY = max(0, estAlignmentY - allowance); + int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance); + if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) { + throw zxing::ReaderException("region too small to hold alignment pattern"); + } + + AlignmentPatternFinder alignmentFinder(image_, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX + - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, callback_); + return alignmentFinder.find(); +} + +} } // file: zxing/qrcode/detector/FinderPattern.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * FinderPattern.cpp * zxing @@ -9303,43 +11174,56 @@ namespace zxing { namespace zxing { namespace qrcode { - + using namespace std; - + FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize) : - posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize), counter_(1) { + ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize), count_(1) { } - - float FinderPattern::getX() const { - return posX_; + + FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize, int count) : + ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize), count_(count) { } - - float FinderPattern::getY() const { - return posY_; - } - + int FinderPattern::getCount() const { - return counter_; + return count_; } - + float FinderPattern::getEstimatedModuleSize() const { return estimatedModuleSize_; } - + void FinderPattern::incrementCount() { - counter_++; + count_++; } - + +/* bool FinderPattern::aboutEquals(float moduleSize, float i, float j) const { return abs(i - posY_) <= moduleSize && abs(j - posX_) <= moduleSize && (abs(moduleSize - estimatedModuleSize_) <= 1.0f || abs(moduleSize - estimatedModuleSize_) / estimatedModuleSize_ <= 0.1f); } - +*/ + bool FinderPattern::aboutEquals(float moduleSize, float i, float j) const { + if (abs(i - getY()) <= moduleSize && abs(j - getX()) <= moduleSize) { + float moduleSizeDiff = abs(moduleSize - estimatedModuleSize_); + return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize_; + } + return false; + } + + Ref FinderPattern::combineEstimate(float i, float j, float newModuleSize) const { + int combinedCount = count_ + 1; + float combinedX = (count_ * getX() + j) / combinedCount; + float combinedY = (count_ * getY() + i) / combinedCount; + float combinedModuleSize = (count_ * getEstimatedModuleSize() + newModuleSize) / combinedCount; + return Ref(new FinderPattern(combinedX, combinedY, combinedModuleSize, combinedCount)); + } } } // file: zxing/qrcode/detector/FinderPatternFinder.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* * FinderPatternFinder.cpp * zxing @@ -9369,516 +11253,516 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - using namespace std; - - class FurthestFromAverageComparator { - private: - const float averageModuleSize_; - public: - FurthestFromAverageComparator(float averageModuleSize) : - averageModuleSize_(averageModuleSize) { - } - bool operator()(Ref a, Ref b) { - float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_); - float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_); - return dA > dB; - } - }; - - class CenterComparator { - const float averageModuleSize_; - public: - CenterComparator(float averageModuleSize) : - averageModuleSize_(averageModuleSize) { - } - bool operator()(Ref a, Ref b) { - // N.B.: we want the result in descending order ... - if (a->getCount() != b->getCount()) { - return a->getCount() > b->getCount(); - } else { - float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_); - float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_); - return dA < dB; - } - } - }; - - int FinderPatternFinder::CENTER_QUORUM = 2; - int FinderPatternFinder::MIN_SKIP = 3; - int FinderPatternFinder::MAX_MODULES = 57; - - float FinderPatternFinder::centerFromEnd(int* stateCount, int end) { - return (float)(end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f; - } - - bool FinderPatternFinder::foundPatternCross(int* stateCount) { - int totalModuleSize = 0; - for (int i = 0; i < 5; i++) { - if (stateCount[i] == 0) { - return false; - } - totalModuleSize += stateCount[i]; - } - if (totalModuleSize < 7) { - return false; - } - float moduleSize = (float)totalModuleSize / 7.0f; - float maxVariance = moduleSize / 2.0f; - // Allow less than 50% variance from 1-1-3-1-1 proportions - return abs(moduleSize - stateCount[0]) < maxVariance && abs(moduleSize - stateCount[1]) < maxVariance && abs(3.0f - * moduleSize - stateCount[2]) < 3.0f * maxVariance && abs(moduleSize - stateCount[3]) < maxVariance && abs( - moduleSize - stateCount[4]) < maxVariance; - } - - float FinderPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount, int originalStateCountTotal) { - - int maxI = image_->getHeight(); - int stateCount[5]; - for (int i = 0; i < 5; i++) - stateCount[i] = 0; - - - // Start counting up from center - int i = startI; - while (i >= 0 && image_->get(centerJ, i)) { - stateCount[2]++; - i--; - } - if (i < 0) { - return NAN; - } - while (i >= 0 && !image_->get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return NAN; - } - while (i >= 0 && image_->get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return NAN; - } - - // Now also count down from center - i = startI + 1; - while (i < maxI && image_->get(centerJ, i)) { - stateCount[2]++; - i++; - } - if (i == maxI) { - return NAN; - } - while (i < maxI && !image_->get(centerJ, i) && stateCount[3] < maxCount) { - stateCount[3]++; - i++; - } - if (i == maxI || stateCount[3] >= maxCount) { - return NAN; - } - while (i < maxI && image_->get(centerJ, i) && stateCount[4] < maxCount) { - stateCount[4]++; - i++; - } - if (stateCount[4] >= maxCount) { - return NAN; - } - - // If we found a finder-pattern-like section, but its size is more than 40% different than - // the original, assume it's a false positive - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; - if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return NAN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN; - } - - float FinderPatternFinder::crossCheckHorizontal(size_t startJ, size_t centerI, int maxCount, - int originalStateCountTotal) { - - int maxJ = image_->getWidth(); - int stateCount[5]; - for (int i = 0; i < 5; i++) - stateCount[i] = 0; - - int j = startJ; - while (j >= 0 && image_->get(j, centerI)) { - stateCount[2]++; - j--; - } - if (j < 0) { - return NAN; - } - while (j >= 0 && !image_->get(j, centerI) && stateCount[1] <= maxCount) { - stateCount[1]++; - j--; - } - if (j < 0 || stateCount[1] > maxCount) { - return NAN; - } - while (j >= 0 && image_->get(j, centerI) && stateCount[0] <= maxCount) { - stateCount[0]++; - j--; - } - if (stateCount[0] > maxCount) { - return NAN; - } - - j = startJ + 1; - while (j < maxJ && image_->get(j, centerI)) { - stateCount[2]++; - j++; - } - if (j == maxJ) { - return NAN; - } - while (j < maxJ && !image_->get(j, centerI) && stateCount[3] < maxCount) { - stateCount[3]++; - j++; - } - if (j == maxJ || stateCount[3] >= maxCount) { - return NAN; - } - while (j < maxJ && image_->get(j, centerI) && stateCount[4] < maxCount) { - stateCount[4]++; - j++; - } - if (stateCount[4] >= maxCount) { - return NAN; - } - - // If we found a finder-pattern-like section, but its size is significantly different than - // the original, assume it's a false positive - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; - if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { - return NAN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : NAN; - } - - bool FinderPatternFinder::handlePossibleCenter(int* stateCount, size_t i, size_t j) { - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; - float centerJ = centerFromEnd(stateCount, j); - float centerI = crossCheckVertical(i, (size_t)centerJ, stateCount[2], stateCountTotal); - if (!isnan(centerI)) { - // Re-cross check - centerJ = crossCheckHorizontal((size_t)centerJ, (size_t)centerI, stateCount[2], stateCountTotal); - if (!isnan(centerJ)) { - float estimatedModuleSize = (float)stateCountTotal / 7.0f; - bool found = false; - size_t max = possibleCenters_.size(); - for (size_t index = 0; index < max; index++) { - Ref center = possibleCenters_[index]; - // Look for about the same center and module size: - if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) { - center->incrementCount(); - found = true; - break; - } - } - if (!found) { - Ref newPattern(new FinderPattern(centerJ, centerI, estimatedModuleSize)); - possibleCenters_.push_back(newPattern); - if (callback_ != 0) { - callback_->foundPossibleResultPoint(*newPattern); - } - } - return true; - } - } - return false; - } - - int FinderPatternFinder::findRowSkip() { - size_t max = possibleCenters_.size(); - if (max <= 1) { - return 0; - } - Ref firstConfirmedCenter; - for (size_t i = 0; i < max; i++) { - Ref center = possibleCenters_[i]; - if (center->getCount() >= CENTER_QUORUM) { - if (firstConfirmedCenter == 0) { - firstConfirmedCenter = center; - } else { - // We have two confirmed centers - // How far down can we skip before resuming looking for the next - // pattern? In the worst case, only the difference between the - // difference in the x / y coordinates of the two centers. - // This is the case where you find top left first. Draw it out. - hasSkipped_ = true; - return (int)(abs(firstConfirmedCenter->getX() - center->getX()) - abs(firstConfirmedCenter->getY() - - center->getY()))/2; - } - } - } - return 0; - } - - bool FinderPatternFinder::haveMultiplyConfirmedCenters() { - int confirmedCount = 0; - float totalModuleSize = 0.0f; - size_t max = possibleCenters_.size(); - for (size_t i = 0; i < max; i++) { - Ref pattern = possibleCenters_[i]; - if (pattern->getCount() >= CENTER_QUORUM) { - confirmedCount++; - totalModuleSize += pattern->getEstimatedModuleSize(); - } - } - if (confirmedCount < 3) { - return false; - } - // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" - // and that we need to keep looking. We detect this by asking if the estimated module sizes - // vary too much. We arbitrarily say that when the total deviation from average exceeds - // 5% of the total module size estimates, it's too much. - float average = totalModuleSize / max; - float totalDeviation = 0.0f; - for (size_t i = 0; i < max; i++) { - Ref pattern = possibleCenters_[i]; - totalDeviation += abs(pattern->getEstimatedModuleSize() - average); - } - return totalDeviation <= 0.05f * totalModuleSize; - } - - vector > FinderPatternFinder::selectBestPatterns() { - size_t startSize = possibleCenters_.size(); - - if (startSize < 3) { - // Couldn't find enough finder patterns - throw zxing::ReaderException("Could not find three finder patterns"); - } - - // Filter outlier possibilities whose module size is too different - if (startSize > 3) { - // But we can only afford to do so if we have at least 4 possibilities to choose from - float totalModuleSize = 0.0f; - float square = 0.0f; - for (size_t i = 0; i < startSize; i++) { - float size = possibleCenters_[i]->getEstimatedModuleSize(); - totalModuleSize += size; - square += size * size; - } - float average = totalModuleSize / (float) startSize; - float stdDev = (float)sqrt(square / startSize - average * average); - - sort(possibleCenters_.begin(), possibleCenters_.end(), FurthestFromAverageComparator(average)); - - float limit = max(0.2f * average, stdDev); - - for (size_t i = 0; i < possibleCenters_.size() && possibleCenters_.size() > 3; i++) { - if (abs(possibleCenters_[i]->getEstimatedModuleSize() - average) > limit) { - possibleCenters_.erase(possibleCenters_.begin()+i); - i--; - } - } - } - - if (possibleCenters_.size() > 3) { - // Throw away all but those first size candidate points we found. - float totalModuleSize = 0.0f; - for (size_t i = 0; i < startSize; i++) { - float size = possibleCenters_[i]->getEstimatedModuleSize(); - totalModuleSize += size; - } - float average = totalModuleSize / (float) startSize; - sort(possibleCenters_.begin(), possibleCenters_.end(), CenterComparator(average)); - } - - if (possibleCenters_.size() > 3) { - possibleCenters_.erase(possibleCenters_.begin()+3,possibleCenters_.end()); - } - - vector > result(3); - result[0] = possibleCenters_[0]; - result[1] = possibleCenters_[1]; - result[2] = possibleCenters_[2]; - return result; - } - - vector > FinderPatternFinder::orderBestPatterns(vector > patterns) { - // Find distances between pattern centers - float abDistance = distance(patterns[0], patterns[1]); - float bcDistance = distance(patterns[1], patterns[2]); - float acDistance = distance(patterns[0], patterns[2]); - - Ref topLeft; - Ref topRight; - Ref bottomLeft; - // Assume one closest to other two is top left; - // topRight and bottomLeft will just be guesses below at first - if (bcDistance >= abDistance && bcDistance >= acDistance) { - topLeft = patterns[0]; - topRight = patterns[1]; - bottomLeft = patterns[2]; - } else if (acDistance >= bcDistance && acDistance >= abDistance) { - topLeft = patterns[1]; - topRight = patterns[0]; - bottomLeft = patterns[2]; - } else { - topLeft = patterns[2]; - topRight = patterns[0]; - bottomLeft = patterns[1]; - } - - // Use cross product to figure out which of other1/2 is the bottom left - // pattern. The vector "top-left -> bottom-left" x "top-left -> top-right" - // should yield a vector with positive z component - if ((bottomLeft->getY() - topLeft->getY()) * (topRight->getX() - topLeft->getX()) < (bottomLeft->getX() - - topLeft->getX()) * (topRight->getY() - topLeft->getY())) { - Ref temp = topRight; - topRight = bottomLeft; - bottomLeft = temp; - } - - vector > results(3); - results[0] = bottomLeft; - results[1] = topLeft; - results[2] = topRight; - return results; - } - - float FinderPatternFinder::distance(Ref p1, Ref p2) { - float dx = p1->getX() - p2->getX(); - float dy = p1->getY() - p2->getY(); - return (float)sqrt(dx * dx + dy * dy); - } - - FinderPatternFinder::FinderPatternFinder(Ref image, - Refconst& callback) : - image_(image), possibleCenters_(), hasSkipped_(false), callback_(callback) { - } - - Ref FinderPatternFinder::find(DecodeHints const& hints) { - bool tryHarder = hints.getTryHarder(); - - size_t maxI = image_->getHeight(); - size_t maxJ = image_->getWidth(); - - - // We are looking for black/white/black/white/black modules in - // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far - - // As this is used often, we use an integer array instead of vector - int stateCount[5]; - bool done = false; - - - // Let's assume that the maximum version QR Code we support takes up 1/4 - // the height of the image, and then account for the center being 3 - // modules in size. This gives the smallest number of pixels the center - // could be, so skip this often. When trying harder, look for all - // QR versions regardless of how dense they are. - int iSkip = (3 * maxI) / (4 * MAX_MODULES); - if (iSkip < MIN_SKIP || tryHarder) { - iSkip = MIN_SKIP; - } - - // This is slightly faster than using the Ref. Efficiency is important here - BitMatrix& matrix = *image_; - - for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) { - // Get a row of black/white values - - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - stateCount[3] = 0; - stateCount[4] = 0; - int currentState = 0; - for (size_t j = 0; j < maxJ; j++) { - if (matrix.get(j, i)) { - // Black pixel - if ((currentState & 1) == 1) { // Counting white pixels - currentState++; - } - stateCount[currentState]++; - } else { // White pixel - if ((currentState & 1) == 0) { // Counting black pixels - if (currentState == 4) { // A winner? - if (foundPatternCross(stateCount)) { // Yes - bool confirmed = handlePossibleCenter(stateCount, i, j); - if (confirmed) { - // Start examining every other line. Checking each line turned out to be too - // expensive and didn't improve performance. - iSkip = 2; - if (hasSkipped_) { - done = haveMultiplyConfirmedCenters(); - } else { - int rowSkip = findRowSkip(); - if (rowSkip > stateCount[2]) { - // Skip rows between row of lower confirmed center - // and top of presumed third confirmed center - // but back up a bit to get a full chance of detecting - // it, entire width of center of finder pattern - - // Skip by rowSkip, but back off by stateCount[2] (size - // of last center of pattern we saw) to be conservative, - // and also back off by iSkip which is about to be - // re-added - i += rowSkip - stateCount[2] - iSkip; - j = maxJ - 1; - } - } - } else { - stateCount[0] = stateCount[2]; - stateCount[1] = stateCount[3]; - stateCount[2] = stateCount[4]; - stateCount[3] = 1; - stateCount[4] = 0; - currentState = 3; - continue; - } - // Clear state to start looking again - currentState = 0; - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - stateCount[3] = 0; - stateCount[4] = 0; - } else { // No, shift counts back by two - stateCount[0] = stateCount[2]; - stateCount[1] = stateCount[3]; - stateCount[2] = stateCount[4]; - stateCount[3] = 1; - stateCount[4] = 0; - currentState = 3; - } - } else { - stateCount[++currentState]++; - } - } else { // Counting white pixels - stateCount[currentState]++; - } - } - } - if (foundPatternCross(stateCount)) { - bool confirmed = handlePossibleCenter(stateCount, i, maxJ); - if (confirmed) { - iSkip = stateCount[0]; - if (hasSkipped_) { - // Found a third one - done = haveMultiplyConfirmedCenters(); - } - } - } - } - - vector > patternInfo = selectBestPatterns(); - patternInfo = orderBestPatterns(patternInfo); - - Ref result(new FinderPatternInfo(patternInfo)); - return result; - } +namespace qrcode { + +using namespace std; + +class FurthestFromAverageComparator { +private: + const float averageModuleSize_; +public: + FurthestFromAverageComparator(float averageModuleSize) : + averageModuleSize_(averageModuleSize) { + } + bool operator()(Ref a, Ref b) { + float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_); + float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_); + return dA > dB; + } +}; + +class CenterComparator { + const float averageModuleSize_; +public: + CenterComparator(float averageModuleSize) : + averageModuleSize_(averageModuleSize) { + } + bool operator()(Ref a, Ref b) { + // N.B.: we want the result in descending order ... + if (a->getCount() != b->getCount()) { + return a->getCount() > b->getCount(); + } else { + float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_); + float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_); + return dA < dB; } + } +}; + +int FinderPatternFinder::CENTER_QUORUM = 2; +int FinderPatternFinder::MIN_SKIP = 3; +int FinderPatternFinder::MAX_MODULES = 57; + +float FinderPatternFinder::centerFromEnd(int* stateCount, int end) { + return (float)(end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f; +} + +bool FinderPatternFinder::foundPatternCross(int* stateCount) { + int totalModuleSize = 0; + for (int i = 0; i < 5; i++) { + if (stateCount[i] == 0) { + return false; + } + totalModuleSize += stateCount[i]; + } + if (totalModuleSize < 7) { + return false; + } + float moduleSize = (float)totalModuleSize / 7.0f; + float maxVariance = moduleSize / 2.0f; + // Allow less than 50% variance from 1-1-3-1-1 proportions + return abs(moduleSize - stateCount[0]) < maxVariance && abs(moduleSize - stateCount[1]) < maxVariance && abs(3.0f + * moduleSize - stateCount[2]) < 3.0f * maxVariance && abs(moduleSize - stateCount[3]) < maxVariance && abs( + moduleSize - stateCount[4]) < maxVariance; +} + +float FinderPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount, int originalStateCountTotal) { + + int maxI = image_->getHeight(); + int stateCount[5]; + for (int i = 0; i < 5; i++) + stateCount[i] = 0; + + + // Start counting up from center + int i = startI; + while (i >= 0 && image_->get(centerJ, i)) { + stateCount[2]++; + i--; + } + if (i < 0) { + return NAN; + } + while (i >= 0 && !image_->get(centerJ, i) && stateCount[1] <= maxCount) { + stateCount[1]++; + i--; + } + // If already too many modules in this state or ran off the edge: + if (i < 0 || stateCount[1] > maxCount) { + return NAN; + } + while (i >= 0 && image_->get(centerJ, i) && stateCount[0] <= maxCount) { + stateCount[0]++; + i--; + } + if (stateCount[0] > maxCount) { + return NAN; + } + + // Now also count down from center + i = startI + 1; + while (i < maxI && image_->get(centerJ, i)) { + stateCount[2]++; + i++; + } + if (i == maxI) { + return NAN; + } + while (i < maxI && !image_->get(centerJ, i) && stateCount[3] < maxCount) { + stateCount[3]++; + i++; + } + if (i == maxI || stateCount[3] >= maxCount) { + return NAN; + } + while (i < maxI && image_->get(centerJ, i) && stateCount[4] < maxCount) { + stateCount[4]++; + i++; + } + if (stateCount[4] >= maxCount) { + return NAN; + } + + // If we found a finder-pattern-like section, but its size is more than 40% different than + // the original, assume it's a false positive + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { + return NAN; + } + + return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN; +} + +float FinderPatternFinder::crossCheckHorizontal(size_t startJ, size_t centerI, int maxCount, + int originalStateCountTotal) { + + int maxJ = image_->getWidth(); + int stateCount[5]; + for (int i = 0; i < 5; i++) + stateCount[i] = 0; + + int j = startJ; + while (j >= 0 && image_->get(j, centerI)) { + stateCount[2]++; + j--; + } + if (j < 0) { + return NAN; + } + while (j >= 0 && !image_->get(j, centerI) && stateCount[1] <= maxCount) { + stateCount[1]++; + j--; + } + if (j < 0 || stateCount[1] > maxCount) { + return NAN; + } + while (j >= 0 && image_->get(j, centerI) && stateCount[0] <= maxCount) { + stateCount[0]++; + j--; + } + if (stateCount[0] > maxCount) { + return NAN; + } + + j = startJ + 1; + while (j < maxJ && image_->get(j, centerI)) { + stateCount[2]++; + j++; + } + if (j == maxJ) { + return NAN; + } + while (j < maxJ && !image_->get(j, centerI) && stateCount[3] < maxCount) { + stateCount[3]++; + j++; + } + if (j == maxJ || stateCount[3] >= maxCount) { + return NAN; + } + while (j < maxJ && image_->get(j, centerI) && stateCount[4] < maxCount) { + stateCount[4]++; + j++; + } + if (stateCount[4] >= maxCount) { + return NAN; + } + + // If we found a finder-pattern-like section, but its size is significantly different than + // the original, assume it's a false positive + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { + return NAN; + } + + return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : NAN; +} + +bool FinderPatternFinder::handlePossibleCenter(int* stateCount, size_t i, size_t j) { + int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; + float centerJ = centerFromEnd(stateCount, j); + float centerI = crossCheckVertical(i, (size_t)centerJ, stateCount[2], stateCountTotal); + if (!isnan(centerI)) { + // Re-cross check + centerJ = crossCheckHorizontal((size_t)centerJ, (size_t)centerI, stateCount[2], stateCountTotal); + if (!isnan(centerJ)) { + float estimatedModuleSize = (float)stateCountTotal / 7.0f; + bool found = false; + size_t max = possibleCenters_.size(); + for (size_t index = 0; index < max; index++) { + Ref center = possibleCenters_[index]; + // Look for about the same center and module size: + if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) { + possibleCenters_[index] = center->combineEstimate(centerI, centerJ, estimatedModuleSize); + found = true; + break; + } + } + if (!found) { + Ref newPattern(new FinderPattern(centerJ, centerI, estimatedModuleSize)); + possibleCenters_.push_back(newPattern); + if (callback_ != 0) { + callback_->foundPossibleResultPoint(*newPattern); + } + } + return true; + } + } + return false; +} + +int FinderPatternFinder::findRowSkip() { + size_t max = possibleCenters_.size(); + if (max <= 1) { + return 0; + } + Ref firstConfirmedCenter; + for (size_t i = 0; i < max; i++) { + Ref center = possibleCenters_[i]; + if (center->getCount() >= CENTER_QUORUM) { + if (firstConfirmedCenter == 0) { + firstConfirmedCenter = center; + } else { + // We have two confirmed centers + // How far down can we skip before resuming looking for the next + // pattern? In the worst case, only the difference between the + // difference in the x / y coordinates of the two centers. + // This is the case where you find top left first. Draw it out. + hasSkipped_ = true; + return (int)(abs(firstConfirmedCenter->getX() - center->getX()) - abs(firstConfirmedCenter->getY() + - center->getY()))/2; + } + } + } + return 0; +} + +bool FinderPatternFinder::haveMultiplyConfirmedCenters() { + int confirmedCount = 0; + float totalModuleSize = 0.0f; + size_t max = possibleCenters_.size(); + for (size_t i = 0; i < max; i++) { + Ref pattern = possibleCenters_[i]; + if (pattern->getCount() >= CENTER_QUORUM) { + confirmedCount++; + totalModuleSize += pattern->getEstimatedModuleSize(); + } + } + if (confirmedCount < 3) { + return false; + } + // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" + // and that we need to keep looking. We detect this by asking if the estimated module sizes + // vary too much. We arbitrarily say that when the total deviation from average exceeds + // 5% of the total module size estimates, it's too much. + float average = totalModuleSize / max; + float totalDeviation = 0.0f; + for (size_t i = 0; i < max; i++) { + Ref pattern = possibleCenters_[i]; + totalDeviation += abs(pattern->getEstimatedModuleSize() - average); + } + return totalDeviation <= 0.05f * totalModuleSize; +} + +vector > FinderPatternFinder::selectBestPatterns() { + size_t startSize = possibleCenters_.size(); + + if (startSize < 3) { + // Couldn't find enough finder patterns + throw zxing::ReaderException("Could not find three finder patterns"); + } + + // Filter outlier possibilities whose module size is too different + if (startSize > 3) { + // But we can only afford to do so if we have at least 4 possibilities to choose from + float totalModuleSize = 0.0f; + float square = 0.0f; + for (size_t i = 0; i < startSize; i++) { + float size = possibleCenters_[i]->getEstimatedModuleSize(); + totalModuleSize += size; + square += size * size; + } + float average = totalModuleSize / (float) startSize; + float stdDev = (float)sqrt(square / startSize - average * average); + + sort(possibleCenters_.begin(), possibleCenters_.end(), FurthestFromAverageComparator(average)); + + float limit = max(0.2f * average, stdDev); + + for (size_t i = 0; i < possibleCenters_.size() && possibleCenters_.size() > 3; i++) { + if (abs(possibleCenters_[i]->getEstimatedModuleSize() - average) > limit) { + possibleCenters_.erase(possibleCenters_.begin()+i); + i--; + } + } + } + + if (possibleCenters_.size() > 3) { + // Throw away all but those first size candidate points we found. + float totalModuleSize = 0.0f; + for (size_t i = 0; i < possibleCenters_.size(); i++) { + float size = possibleCenters_[i]->getEstimatedModuleSize(); + totalModuleSize += size; + } + float average = totalModuleSize / (float) possibleCenters_.size(); + sort(possibleCenters_.begin(), possibleCenters_.end(), CenterComparator(average)); + } + + if (possibleCenters_.size() > 3) { + possibleCenters_.erase(possibleCenters_.begin()+3,possibleCenters_.end()); + } + + vector > result(3); + result[0] = possibleCenters_[0]; + result[1] = possibleCenters_[1]; + result[2] = possibleCenters_[2]; + return result; +} + +vector > FinderPatternFinder::orderBestPatterns(vector > patterns) { + // Find distances between pattern centers + float abDistance = distance(patterns[0], patterns[1]); + float bcDistance = distance(patterns[1], patterns[2]); + float acDistance = distance(patterns[0], patterns[2]); + + Ref topLeft; + Ref topRight; + Ref bottomLeft; + // Assume one closest to other two is top left; + // topRight and bottomLeft will just be guesses below at first + if (bcDistance >= abDistance && bcDistance >= acDistance) { + topLeft = patterns[0]; + topRight = patterns[1]; + bottomLeft = patterns[2]; + } else if (acDistance >= bcDistance && acDistance >= abDistance) { + topLeft = patterns[1]; + topRight = patterns[0]; + bottomLeft = patterns[2]; + } else { + topLeft = patterns[2]; + topRight = patterns[0]; + bottomLeft = patterns[1]; + } + + // Use cross product to figure out which of other1/2 is the bottom left + // pattern. The vector "top-left -> bottom-left" x "top-left -> top-right" + // should yield a vector with positive z component + if ((bottomLeft->getY() - topLeft->getY()) * (topRight->getX() - topLeft->getX()) < (bottomLeft->getX() + - topLeft->getX()) * (topRight->getY() - topLeft->getY())) { + Ref temp = topRight; + topRight = bottomLeft; + bottomLeft = temp; + } + + vector > results(3); + results[0] = bottomLeft; + results[1] = topLeft; + results[2] = topRight; + return results; +} + +float FinderPatternFinder::distance(Ref p1, Ref p2) { + float dx = p1->getX() - p2->getX(); + float dy = p1->getY() - p2->getY(); + return (float)sqrt(dx * dx + dy * dy); +} + +FinderPatternFinder::FinderPatternFinder(Ref image, + Refconst& callback) : + image_(image), possibleCenters_(), hasSkipped_(false), callback_(callback) { +} + +Ref FinderPatternFinder::find(DecodeHints const& hints) { + bool tryHarder = hints.getTryHarder(); + + size_t maxI = image_->getHeight(); + size_t maxJ = image_->getWidth(); + + + // We are looking for black/white/black/white/black modules in + // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far + + // As this is used often, we use an integer array instead of vector + int stateCount[5]; + bool done = false; + + + // Let's assume that the maximum version QR Code we support takes up 1/4 + // the height of the image, and then account for the center being 3 + // modules in size. This gives the smallest number of pixels the center + // could be, so skip this often. When trying harder, look for all + // QR versions regardless of how dense they are. + int iSkip = (3 * maxI) / (4 * MAX_MODULES); + if (iSkip < MIN_SKIP || tryHarder) { + iSkip = MIN_SKIP; + } + + // This is slightly faster than using the Ref. Efficiency is important here + BitMatrix& matrix = *image_; + + for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) { + // Get a row of black/white values + + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + int currentState = 0; + for (size_t j = 0; j < maxJ; j++) { + if (matrix.get(j, i)) { + // Black pixel + if ((currentState & 1) == 1) { // Counting white pixels + currentState++; + } + stateCount[currentState]++; + } else { // White pixel + if ((currentState & 1) == 0) { // Counting black pixels + if (currentState == 4) { // A winner? + if (foundPatternCross(stateCount)) { // Yes + bool confirmed = handlePossibleCenter(stateCount, i, j); + if (confirmed) { + // Start examining every other line. Checking each line turned out to be too + // expensive and didn't improve performance. + iSkip = 2; + if (hasSkipped_) { + done = haveMultiplyConfirmedCenters(); + } else { + int rowSkip = findRowSkip(); + if (rowSkip > stateCount[2]) { + // Skip rows between row of lower confirmed center + // and top of presumed third confirmed center + // but back up a bit to get a full chance of detecting + // it, entire width of center of finder pattern + + // Skip by rowSkip, but back off by stateCount[2] (size + // of last center of pattern we saw) to be conservative, + // and also back off by iSkip which is about to be + // re-added + i += rowSkip - stateCount[2] - iSkip; + j = maxJ - 1; + } + } + } else { + stateCount[0] = stateCount[2]; + stateCount[1] = stateCount[3]; + stateCount[2] = stateCount[4]; + stateCount[3] = 1; + stateCount[4] = 0; + currentState = 3; + continue; + } + // Clear state to start looking again + currentState = 0; + stateCount[0] = 0; + stateCount[1] = 0; + stateCount[2] = 0; + stateCount[3] = 0; + stateCount[4] = 0; + } else { // No, shift counts back by two + stateCount[0] = stateCount[2]; + stateCount[1] = stateCount[3]; + stateCount[2] = stateCount[4]; + stateCount[3] = 1; + stateCount[4] = 0; + currentState = 3; + } + } else { + stateCount[++currentState]++; + } + } else { // Counting white pixels + stateCount[currentState]++; + } + } + } + if (foundPatternCross(stateCount)) { + bool confirmed = handlePossibleCenter(stateCount, i, maxJ); + if (confirmed) { + iSkip = stateCount[0]; + if (hasSkipped_) { + // Found a third one + done = haveMultiplyConfirmedCenters(); + } + } + } + } + + vector > patternInfo = selectBestPatterns(); + patternInfo = orderBestPatterns(patternInfo); + + Ref result(new FinderPatternInfo(patternInfo)); + return result; +} +} } // file: zxing/qrcode/detector/FinderPatternInfo.cpp @@ -9906,31 +11790,29 @@ namespace zxing { // #include namespace zxing { - namespace qrcode { - - FinderPatternInfo::FinderPatternInfo(std::vector > patternCenters) : - bottomLeft_(patternCenters[0]), topLeft_(patternCenters[1]), topRight_(patternCenters[2]) { - } - - Ref FinderPatternInfo::getBottomLeft() { - return bottomLeft_; - } - Ref FinderPatternInfo::getTopLeft() { - return topLeft_; - } - Ref FinderPatternInfo::getTopRight() { - return topRight_; - } - - } +namespace qrcode { + +FinderPatternInfo::FinderPatternInfo(std::vector > patternCenters) : + bottomLeft_(patternCenters[0]), topLeft_(patternCenters[1]), topRight_(patternCenters[2]) { +} + +Ref FinderPatternInfo::getBottomLeft() { + return bottomLeft_; +} +Ref FinderPatternInfo::getTopLeft() { + return topLeft_; +} +Ref FinderPatternInfo::getTopRight() { + return topRight_; +} + +} } // file: zxing/qrcode/detector/QREdgeDetector.cpp +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- /* - * QREdgeDetector.cpp - * zxing - * * Created by Ralf Kistner on 7/12/2009. * Copyright 2008 ZXing authors All rights reserved. * @@ -9954,146 +11836,147 @@ namespace zxing { using namespace std; namespace zxing { - namespace qrcode { - - static const float patternEdgeThreshold = 2; - static const int patternEdgeWidth = 3; - static const float patternEdgeSearchRatio = 1.1; - static const int patternEdgeSkip = 2; - - static const float accurateEdgeThreshold = 3.3; - static const int accurateEdgeWidth = 7; - static const int accurateEdgeSkip = 2; - - static Point guessLastPattern(Point topLeft, Point topRight, Point bottomLeft) { - return Point(topRight.x - topLeft.x + bottomLeft.x, topRight.y - topLeft.y + bottomLeft.y); - } - - static Point rp(Ref rp) { - return Point(rp->getX(), rp->getY()); - } - - QREdgeDetector::QREdgeDetector(Ref image) : Detector(image) { } - - Ref QREdgeDetector::createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref alignmentPattern, int dimension) { - - if(alignmentPattern == NULL) { - Point corner = findCorner(*Detector::getImage(), rp(topLeft), rp(topRight), rp(bottomLeft), dimension); - return get1CornerTransform(rp(topLeft), rp(topRight), rp(bottomLeft), corner, dimension); - } else { - return Detector::createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); - } - } - - - - - Point QREdgeDetector::findCorner(const BitMatrix& image, Point topLeft, Point topRight, Point bottomLeft, int dimension) { - Point bottomRight = guessLastPattern(topLeft, topRight, bottomLeft); - - Line bottomEst = findPatternEdge(image, bottomLeft, topLeft, bottomRight, false); - Line rightEst = findPatternEdge(image, topRight, topLeft, bottomRight, true); - - //return EdgeDetector::intersection(bottomEst, rightEst); - - Line bottom = EdgeDetector::findLine(image, bottomEst, false, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip); - Line right = EdgeDetector::findLine(image, rightEst, true, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip); - - - return EdgeDetector::intersection(bottom, right); - } - - Line QREdgeDetector::findPatternEdge(const BitMatrix& image, Point pattern, Point opposite, Point direction, bool invert) { - Point start = endOfReverseBlackWhiteBlackRun(image, pattern, opposite); - - float dx = pattern.x - start.x; - float dy = pattern.y - start.y; - float dist = sqrt(dx*dx + dy*dy); - - float dirX = direction.x - pattern.x; - float dirY = direction.y - pattern.y; - float dirSize = sqrt(dirX*dirX + dirY*dirY); - - float nx = dirX/dirSize; - float ny = dirY/dirSize; - - float search = dist * patternEdgeSearchRatio; - Point a(start.x + nx*search, start.y + ny*search); - Point b(start.x - nx*search, start.y - ny*search); - - return EdgeDetector::findLine(image, Line(a, b), invert, patternEdgeWidth, patternEdgeThreshold, patternEdgeSkip); - } - - - Ref QREdgeDetector::get1CornerTransform(Point topLeft, Point topRight, Point bottomLeft, Point corner, int dimension) { - float dimMinusThree = (float) dimension - 3.5f; - - Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, dimension, - dimension, 3.5f, dimMinusThree, topLeft.x, topLeft.y, topRight.x, - topRight.y, corner.x, corner.y, bottomLeft.x, bottomLeft.y)); - - return transform; - } - - // Adapted from "sizeOfBlackWhiteBlackRun" in zxing::qrcode::Detector - Point QREdgeDetector::endOfReverseBlackWhiteBlackRun(const BitMatrix& image, Point from, Point to) { - int fromX = (int)from.x; - int fromY = (int)from.y; - int toX = (int)to.x; - int toY = (int)to.y; - - bool steep = abs(toY - fromY) > abs(toX - fromX); - if (steep) { - int temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - - int dx = abs(toX - fromX); - int dy = abs(toY - fromY); - int error = -dx >> 1; - int ystep = fromY < toY ? -1 : 1; - int xstep = fromX < toX ? -1 : 1; - int state = 0; // In black pixels, looking for white, first or second time - - // In case there are no points, prepopulate to from - int realX = fromX; - int realY = fromY; - for (int x = fromX, y = fromY; x != toX; x += xstep) { - realX = steep ? y : x; - realY = steep ? x : y; - - if(realX < 0 || realY < 0 || realX >= (int)image.getWidth() || realY >= (int)image.getHeight()) - break; - - if (state == 1) { // In white pixels, looking for black - if (image.get(realX, realY)) { - state++; - } - } else { - if (!image.get(realX, realY)) { - state++; - } - } - - if (state == 3) { // Found black, white, black, and stumbled back onto white; done - return Point(realX, realY); - } - error += dy; - if (error > 0) { - y += ystep; - error -= dx; - } - } - - // B-W-B run not found, return the last point visited. - return Point(realX, realY); - } - - } // namespace qrcode +namespace qrcode { + +static const float patternEdgeThreshold = 2; +static const int patternEdgeWidth = 3; +static const float patternEdgeSearchRatio = 1.1; +static const int patternEdgeSkip = 2; + +static const float accurateEdgeThreshold = 3.3; +static const int accurateEdgeWidth = 7; +static const int accurateEdgeSkip = 2; + +static Point guessLastPattern(Point topLeft, Point topRight, Point bottomLeft) { + return Point(topRight.x - topLeft.x + bottomLeft.x, topRight.y - topLeft.y + bottomLeft.y); +} + +static Point rp(Ref rp) { + return Point(rp->getX(), rp->getY()); +} + +QREdgeDetector::QREdgeDetector(Ref image) : Detector(image) { } + +Ref QREdgeDetector::createTransform(Ref topLeft, Ref topRight, Ref < + ResultPoint > bottomLeft, Ref alignmentPattern, int dimension) { + + if(alignmentPattern == NULL) { + Point corner = findCorner(*Detector::getImage(), rp(topLeft), rp(topRight), rp(bottomLeft), dimension); + return get1CornerTransform(rp(topLeft), rp(topRight), rp(bottomLeft), corner, dimension); + } else { + return Detector::createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); + } +} + + + + +Point QREdgeDetector::findCorner(const BitMatrix& image, Point topLeft, Point topRight, Point bottomLeft, int dimension) { + (void)dimension; + Point bottomRight = guessLastPattern(topLeft, topRight, bottomLeft); + + Line bottomEst = findPatternEdge(image, bottomLeft, topLeft, bottomRight, false); + Line rightEst = findPatternEdge(image, topRight, topLeft, bottomRight, true); + + //return EdgeDetector::intersection(bottomEst, rightEst); + + Line bottom = EdgeDetector::findLine(image, bottomEst, false, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip); + Line right = EdgeDetector::findLine(image, rightEst, true, accurateEdgeWidth, accurateEdgeThreshold, accurateEdgeSkip); + + + return EdgeDetector::intersection(bottom, right); +} + +Line QREdgeDetector::findPatternEdge(const BitMatrix& image, Point pattern, Point opposite, Point direction, bool invert) { + Point start = endOfReverseBlackWhiteBlackRun(image, pattern, opposite); + + float dx = pattern.x - start.x; + float dy = pattern.y - start.y; + float dist = sqrt(dx*dx + dy*dy); + + float dirX = direction.x - pattern.x; + float dirY = direction.y - pattern.y; + float dirSize = sqrt(dirX*dirX + dirY*dirY); + + float nx = dirX/dirSize; + float ny = dirY/dirSize; + + float search = dist * patternEdgeSearchRatio; + Point a(start.x + nx*search, start.y + ny*search); + Point b(start.x - nx*search, start.y - ny*search); + + return EdgeDetector::findLine(image, Line(a, b), invert, patternEdgeWidth, patternEdgeThreshold, patternEdgeSkip); +} + + +Ref QREdgeDetector::get1CornerTransform(Point topLeft, Point topRight, Point bottomLeft, Point corner, int dimension) { + float dimMinusThree = (float) dimension - 3.5f; + + Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, dimension, + dimension, 3.5f, dimMinusThree, topLeft.x, topLeft.y, topRight.x, + topRight.y, corner.x, corner.y, bottomLeft.x, bottomLeft.y)); + + return transform; +} + +// Adapted from "sizeOfBlackWhiteBlackRun" in zxing::qrcode::Detector +Point QREdgeDetector::endOfReverseBlackWhiteBlackRun(const BitMatrix& image, Point from, Point to) { + int fromX = (int)from.x; + int fromY = (int)from.y; + int toX = (int)to.x; + int toY = (int)to.y; + + bool steep = abs(toY - fromY) > abs(toX - fromX); + if (steep) { + int temp = fromX; + fromX = fromY; + fromY = temp; + temp = toX; + toX = toY; + toY = temp; + } + + int dx = abs(toX - fromX); + int dy = abs(toY - fromY); + int error = -dx >> 1; + int ystep = fromY < toY ? -1 : 1; + int xstep = fromX < toX ? -1 : 1; + int state = 0; // In black pixels, looking for white, first or second time + + // In case there are no points, prepopulate to from + int realX = fromX; + int realY = fromY; + for (int x = fromX, y = fromY; x != toX; x += xstep) { + realX = steep ? y : x; + realY = steep ? x : y; + + if(realX < 0 || realY < 0 || realX >= (int)image.getWidth() || realY >= (int)image.getHeight()) + break; + + if (state == 1) { // In white pixels, looking for black + if (image.get(realX, realY)) { + state++; + } + } else { + if (!image.get(realX, realY)) { + state++; + } + } + + if (state == 3) { // Found black, white, black, and stumbled back onto white; done + return Point(realX, realY); + } + error += dy; + if (error > 0) { + y += ystep; + error -= dx; + } + } + + // B-W-B run not found, return the last point visited. + return Point(realX, realY); +} + +} // namespace qrcode } // namespace zxing diff --git a/iOS/BarcodeScanner/zxing-all-in-one.h b/iOS/BarcodeScanner/zxing-all-in-one.h index 57abb10..2718ca9 100644 --- a/iOS/BarcodeScanner/zxing-all-in-one.h +++ b/iOS/BarcodeScanner/zxing-all-in-one.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include // file: zxing/Exception.h @@ -49,6 +51,7 @@ private: std::string message; public: + Exception(); Exception(const char *msg); virtual const char* what() const throw(); virtual ~Exception() throw(); @@ -95,13 +98,11 @@ public: // file: zxing/common/Counted.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __COUNTED_H__ // #define __COUNTED_H__ /* - * Counted.h - * zxing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -118,7 +119,6 @@ public: */ //#define DEBUG_COUNTING -//using namespace std; // #include @@ -142,7 +142,7 @@ public: } virtual ~Counted() { } - virtual Counted *retain() { + Counted *retain() { #ifdef DEBUG_COUNTING cout << "retaining " << typeid(*this).name() << " " << this << " @ " << count_; @@ -153,7 +153,7 @@ public: #endif return this; } - virtual void release() { + void release() { #ifdef DEBUG_COUNTING cout << "releasing " << typeid(*this).name() << " " << this << " @ " << count_; @@ -275,8 +275,8 @@ public: return object_; } - bool operator==(const int x) { - return x == 0 ? object_ == 0 : false; + bool operator==(const T* that) { + return object_ == that; } bool operator==(const Ref &other) const { return object_ == other.object_ || *object_ == *(other.object_); @@ -286,8 +286,8 @@ public: return object_ == other.object_ || *object_ == *(other.object_); } - bool operator!=(const int x) { - return x == 0 ? object_ != 0 : true; + bool operator!=(const T* that) { + return !(*this == that); } bool empty() const { @@ -303,13 +303,11 @@ public: // file: zxing/common/BitArray.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __BIT_ARRAY_H__ // #define __BIT_ARRAY_H__ /* - * BitArray.h - * zxing - * * Copyright 2010 ZXing authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -328,17 +326,26 @@ public: // #include // #include // #include -// #include +// #include namespace zxing { +#define ZX_LOG_DIGITS(digits) \ + ((digits == 8) ? 3 : \ + ((digits == 16) ? 4 : \ + ((digits == 32) ? 5 : \ + ((digits == 64) ? 6 : \ + ((digits == 128) ? 7 : \ + (-1)))))) + class BitArray : public Counted { private: size_t size_; std::vector bits_; - static const unsigned int bitsPerWord_; - static const unsigned int logBits_; - static const unsigned int bitsMask_; + static const unsigned int bitsPerWord_ = + std::numeric_limits::digits; + static const unsigned int logBits_ = ZX_LOG_DIGITS(bitsPerWord_); + static const unsigned int bitsMask_ = (1 << logBits_) - 1; static size_t wordsForBits(size_t bits); explicit BitArray(); @@ -346,9 +353,17 @@ public: BitArray(size_t size); ~BitArray(); size_t getSize(); - bool get(size_t i); - void set(size_t i); + + bool get(size_t i) { + return (bits_[i >> logBits_] & (1 << (i & bitsMask_))) != 0; + } + + void set(size_t i) { + bits_[i >> logBits_] |= 1 << (i & bitsMask_); + } + void setBulk(size_t i, unsigned int newBits); + void setRange(int start, int end); void clear(); bool isRange(size_t start, size_t end, bool value); std::vector& getBitArray(); @@ -361,6 +376,7 @@ public: // file: zxing/common/BitMatrix.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __BIT_MATRIX_H__ // #define __BIT_MATRIX_H__ @@ -396,14 +412,35 @@ private: size_t words_; unsigned int* bits_; +#define ZX_LOG_DIGITS(digits) \ + ((digits == 8) ? 3 : \ + ((digits == 16) ? 4 : \ + ((digits == 32) ? 5 : \ + ((digits == 64) ? 6 : \ + ((digits == 128) ? 7 : \ + (-1)))))) + + static const unsigned int bitsPerWord = + std::numeric_limits::digits; + static const unsigned int logBits = ZX_LOG_DIGITS(bitsPerWord); + static const unsigned int bitsMask = (1 << logBits) - 1; + public: BitMatrix(size_t dimension); BitMatrix(size_t width, size_t height); ~BitMatrix(); - // Inlining this does not really improve performance. - bool get(size_t x, size_t y) const; - void set(size_t x, size_t y); + + bool get(size_t x, size_t y) const { + size_t offset = x + width_ * y; + return ((bits_[offset >> logBits] >> (offset & bitsMask)) & 0x01) != 0; + } + + void set(size_t x, size_t y) { + size_t offset = x + width_ * y; + bits_[offset >> logBits] |= 1 << (offset & bitsMask); + } + void flip(size_t x, size_t y); void clear(); void setRegion(size_t left, size_t top, size_t width, size_t height); @@ -429,6 +466,7 @@ private: // file: zxing/common/Array.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __ARRAY_H__ // #define __ARRAY_H__ @@ -524,7 +562,7 @@ public: } }; -template class ArrayRef { +template class ArrayRef : public Counted { private: public: Array *array_; @@ -563,7 +601,7 @@ public: reset(const_cast *>(&a)); } ArrayRef(const ArrayRef &other) : - array_(0) { + Counted(), array_(0) { #ifdef DEBUG_COUNTING cout << "instantiating ArrayRef " << this << " from ArrayRef " << &other << ":\n"; #endif @@ -731,6 +769,9 @@ public: bytes_(bytes), byteOffset_(0), bitOffset_(0) { } + int getByteOffset() { + return byteOffset_; + } /** * @param numBits number of bits to read @@ -785,9 +826,17 @@ class DecoderResult : public Counted { private: ArrayRef rawBytes_; Ref text_; + ArrayRef< ArrayRef > byteSegments_; + std::string ecLevel_; public: + DecoderResult(ArrayRef rawBytes, + Ref text, + ArrayRef< ArrayRef >& byteSegments, + std::string const& ecLevel); + DecoderResult(ArrayRef rawBytes, Ref text); + ArrayRef getRawBytes(); Ref getText(); }; @@ -850,6 +899,7 @@ public: // file: zxing/ResultPoint.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __RESULT_POINT_H__ // #define __RESULT_POINT_H__ @@ -873,17 +923,31 @@ public: */ // #include +// #include namespace zxing { class ResultPoint : public Counted { protected: - ResultPoint() {} + float posX_; + float posY_; + public: + ResultPoint(); + ResultPoint(float x, float y); virtual ~ResultPoint(); - virtual float getX() const = 0; - virtual float getY() const = 0; + virtual float getX() const; + virtual float getY() const; + + bool equals(Ref other); + + static void orderBestPatterns(std::vector > &patterns); + static float distance(Ref point1, Ref point2); + static float distance(float x1, float x2, float y1, float y2); + +private: + static float crossProductZ(Ref pointA, Ref pointB, Ref pointC); }; } @@ -1032,6 +1096,7 @@ Point intersection(Line a, Line b); // file: zxing/LuminanceSource.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __LUMINANCESOURCE_H__ // #define __LUMINANCESOURCE_H__ /* @@ -1076,6 +1141,8 @@ public: virtual bool isRotateSupported() const; virtual Ref rotateCounterClockwise(); + operator std::string (); // should be const but don't want to make sure a + // large breaking change right now }; } @@ -1345,6 +1412,8 @@ private: public: Ref sampleGrid(Ref image, int dimension, Ref transform); + Ref sampleGrid(Ref image, int dimensionX, int dimensionY, Ref transform); + Ref sampleGrid(Ref image, int dimension, float p1ToX, float p1ToY, float p2ToX, float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX, float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY); @@ -1357,6 +1426,7 @@ public: // file: zxing/common/HybridBinarizer.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __HYBRIDBINARIZER_H__ // #define __HYBRIDBINARIZER_H__ /* @@ -1388,7 +1458,7 @@ namespace zxing { class HybridBinarizer : public GlobalHistogramBinarizer { private: - Ref cached_matrix_; + Ref matrix_; Ref cached_row_; int cached_row_num_; @@ -1399,19 +1469,31 @@ namespace zxing { virtual Ref getBlackMatrix(); Ref createBinarizer(Ref source); private: - void binarizeEntireImage(); - // We'll be using one-D arrays because C++ can't dynamically allocate 2D arrays - int* calculateBlackPoints(unsigned char* luminances, int subWidth, int subHeight, - int width, int height); - void calculateThresholdForBlock(unsigned char* luminances, int subWidth, int subHeight, - int width, int height, int blackPoints[], Ref matrix); - void threshold8x8Block(unsigned char* luminances, int xoffset, int yoffset, int threshold, - int stride, Ref matrix); + // We'll be using one-D arrays because C++ can't dynamically allocate 2D + // arrays + int* calculateBlackPoints(unsigned char* luminances, + int subWidth, + int subHeight, + int width, + int height); + void calculateThresholdForBlock(unsigned char* luminances, + int subWidth, + int subHeight, + int width, + int height, + int blackPoints[], + Ref const& matrix); + void threshold8x8Block(unsigned char* luminances, + int xoffset, + int yoffset, + int threshold, + int stride, + Ref const& matrix); }; } -#endif /* GLOBALHISTOGRAMBINARIZER_H_ */ +#endif // file: zxing/common/reedsolomon/GF256.h @@ -1583,7 +1665,7 @@ public: private: std::vector > runEuclideanAlgorithm(Ref a, Ref b, int R); ArrayRef findErrorLocations(Ref errorLocator); - ArrayRef findErrorMagnitudes(Ref errorEvaluator, ArrayRef errorLocations); + ArrayRef findErrorMagnitudes(Ref errorEvaluator, ArrayRef errorLocations, bool dataMatrix); }; } @@ -1806,6 +1888,12 @@ class DecodeHints { private: + DecodeHintType hints; + + Ref callback; + + public: + static const DecodeHintType BARCODEFORMAT_QR_CODE_HINT = 1 << BarcodeFormat_QR_CODE; static const DecodeHintType BARCODEFORMAT_DATA_MATRIX_HINT = 1 << BarcodeFormat_DATA_MATRIX; static const DecodeHintType BARCODEFORMAT_UPC_E_HINT = 1 << BarcodeFormat_UPC_E; @@ -1815,14 +1903,9 @@ class DecodeHints { static const DecodeHintType BARCODEFORMAT_CODE_128_HINT = 1 << BarcodeFormat_CODE_128; static const DecodeHintType BARCODEFORMAT_CODE_39_HINT = 1 << BarcodeFormat_CODE_39; static const DecodeHintType BARCODEFORMAT_ITF_HINT = 1 << BarcodeFormat_ITF; + static const DecodeHintType CHARACTER_SET = 1 << 30; static const DecodeHintType TRYHARDER_HINT = 1 << 31; - DecodeHintType hints; - - Ref callback; - - public: - static const DecodeHints PRODUCT_HINT; static const DecodeHints ONED_HINT; static const DecodeHints DEFAULT_HINT; @@ -1892,6 +1975,7 @@ public: Ref getText(); ArrayRef getRawBytes(); const std::vector >& getResultPoints() const; + std::vector >& getResultPoints(); BarcodeFormat getBarcodeFormat() const; friend std::ostream& operator<<(std::ostream &out, Result& result); @@ -2025,6 +2109,7 @@ namespace zxing { class ReaderException : public Exception { public: + ReaderException(); ReaderException(const char *msg); ~ReaderException() throw(); }; @@ -2364,9 +2449,10 @@ public: // #include // #include +// #include // #include // #include -// #include +// #include namespace zxing { @@ -2419,7 +2505,7 @@ private: /** * See ISO 16022:2006, 5.2.9 and Annex B, B.2 */ - void decodeBase256Segment(Ref bits, std::ostringstream &result);//,std::vector byteSegments); + void decodeBase256Segment(Ref bits, std::ostringstream &result, std::vector byteSegments); void parseTwoBytes(int firstByte, int secondByte, int*& result); /** @@ -2435,7 +2521,7 @@ private: public: DecodedBitStreamParser() { }; - std::string decode(ArrayRef bytes); + Ref decode(ArrayRef bytes); }; } @@ -2476,14 +2562,10 @@ namespace zxing { class CornerPoint : public ResultPoint { private: - float posX_; - float posY_; int counter_; public: CornerPoint(float posX, float posY); - float getX() const; - float getY() const; int getCount() const; void incrementCount(); bool equals(Ref other) const; @@ -2559,6 +2641,7 @@ private: // file: zxing/datamatrix/detector/Detector.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __DETECTOR_H__ // #define __DETECTOR_H__ @@ -2582,56 +2665,70 @@ private: * limitations under the License. */ - // #include // #include // #include // #include -// #include - +// #include namespace zxing { namespace datamatrix { -class ResultPointsAndTransitions : public Counted { -private: - Ref to_; - Ref from_; - int transitions_; +class ResultPointsAndTransitions: public Counted { + private: + Ref to_; + Ref from_; + int transitions_; -public: - ResultPointsAndTransitions(); - ResultPointsAndTransitions(Ref from, Ref to, int transitions); - Ref getFrom(); - Ref getTo(); - int getTransitions(); + public: + ResultPointsAndTransitions(); + ResultPointsAndTransitions(Ref from, Ref to, int transitions); + Ref getFrom(); + Ref getTo(); + int getTransitions(); }; -class Detector : public Counted { -private: - Ref image_; +class Detector: public Counted { + private: + Ref image_; -protected: - Ref sampleGrid(Ref image, int dimension, Ref transform); + protected: + Ref sampleGrid(Ref image, int dimensionX, int dimensionY, + Ref transform); - void insertionSort(std::vector >& vector); + void insertionSort(std::vector >& vector); - Ref transitionsBetween(Ref from, Ref to); - int min(int a, int b) { return a > b ? b : a; }; + Ref correctTopRightRectangular(Ref bottomLeft, + Ref bottomRight, Ref topLeft, Ref topRight, + int dimensionTop, int dimensionRight); + Ref correctTopRight(Ref bottomLeft, Ref bottomRight, + Ref topLeft, Ref topRight, int dimension); + bool isValid(Ref p); + int distance(Ref a, Ref b); + Ref transitionsBetween(Ref from, Ref to); + int min(int a, int b) { + return a > b ? b : a; + } + /** + * Ends up being a bit faster than round(). This merely rounds its + * argument to the nearest int, where x.5 rounds up. + */ + int round(float d) { + return (int) (d + 0.5f); + } -public: - Ref getImage(); - Detector(Ref image); + public: + Ref getImage(); + Detector(Ref image); - virtual Ref createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref bottomRight, int dimension); + virtual Ref createTransform(Ref topLeft, + Ref topRight, Ref bottomLeft, Ref bottomRight, + int dimensionX, int dimensionY); - Ref detect(); - void orderBestPatterns(std::vector > &patterns); - float distance(float x1, float x2, float y1, float y2); -private: - int compare(Ref a, Ref b); - float crossProductZ(Ref pointA, Ref pointB, Ref pointC); + Ref detect(); + + private: + int compare(Ref a, Ref b); }; } @@ -2694,12 +2791,10 @@ namespace zxing { // file: zxing/oned/Code128Reader.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __CODE_128_READER_H__ // #define __CODE_128_READER_H__ /* - * Code128Reader.h - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -2724,10 +2819,8 @@ namespace zxing { class Code128Reader : public OneDReader { private: - //static const unsigned int MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f); - enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f)}; - //static const int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f); - enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f)}; + enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 250/1000)}; + enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 700/1000)}; static const int CODE_SHIFT = 98; static const int CODE_CODE_C = 99; @@ -2822,13 +2915,11 @@ namespace zxing { // file: zxing/oned/UPCEANReader.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __UPC_EAN_READER_H__ // #define __UPC_EAN_READER_H__ /* - * UPCEANReader.h - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -2858,10 +2949,8 @@ namespace zxing { class UPCEANReader : public OneDReader { private: - //static const unsigned int MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f); - enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f)}; - //static const int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f); - enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f)}; + enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 420/1000)}; + enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 700/1000)}; static bool findStartGuardPattern(Ref row, int* rangeStart, int* rangeEnd); @@ -2995,13 +3084,11 @@ namespace zxing { // file: zxing/oned/ITFReader.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __ITF_READER_H__ // #define __ITF_READER_H__ /* - * ITFReader.h - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -3026,10 +3113,8 @@ namespace zxing { class ITFReader : public OneDReader { private: - //static const unsigned int MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f); - enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f)}; - //static const int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f); - enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f)}; + enum {MAX_AVG_VARIANCE = (unsigned int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 420/1000)}; + enum {MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 800/1000)}; // Stores the actual narrow line width of the image being decoded. int narrowLineWidth; @@ -3164,14 +3249,9 @@ namespace zxing { namespace oned { class OneDResultPoint : public ResultPoint { - private: - float posX_; - float posY_; public: OneDResultPoint(float posX, float posY); - float getX() const; - float getY() const; }; } } @@ -3236,9 +3316,6 @@ namespace zxing { // #define __UPC_E_READER_H__ /* - * UPCEReader.h - * ZXing - * * Copyright 2010 ZXing authors All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -3312,7 +3389,9 @@ namespace qrcode { class ErrorCorrectionLevel { private: int ordinal_; - ErrorCorrectionLevel(int inOrdinal); + int bits_; + std::string name_; + ErrorCorrectionLevel(int inOrdinal, int bits, char const* name); static ErrorCorrectionLevel *FOR_BITS[]; static int N_LEVELS; public: @@ -3321,7 +3400,11 @@ public: static ErrorCorrectionLevel Q; static ErrorCorrectionLevel H; - int ordinal(); + int ordinal() const; + int bits() const; + std::string const& name() const; + operator std::string const& () const; + static ErrorCorrectionLevel& forBits(int bits); }; } @@ -3438,6 +3521,7 @@ public: // file: zxing/qrcode/QRCodeReader.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __QR_CODE_READER_H__ // #define __QR_CODE_READER_H__ @@ -3471,6 +3555,9 @@ namespace zxing { private: Decoder decoder_; + protected: + Decoder& getDecoder(); + public: QRCodeReader(); virtual Ref decode(Ref image, DecodeHints hints); @@ -3737,6 +3824,7 @@ public: // file: zxing/qrcode/decoder/Mode.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __MODE_H__ // #define __MODE_H__ @@ -3770,15 +3858,22 @@ private: int characterCountBitsForVersions0To9_; int characterCountBitsForVersions10To26_; int characterCountBitsForVersions27AndHigher_; + int bits_; + std::string name_; - Mode(int cbv0_9, int cbv10_26, int cbv27); + Mode(int cbv0_9, int cbv10_26, int cbv27, int bits, char const* name); public: static Mode TERMINATOR; static Mode NUMERIC; static Mode ALPHANUMERIC; + static Mode STRUCTURED_APPEND; static Mode BYTE; + static Mode ECI; static Mode KANJI; + static Mode FNC1_FIRST_POSITION; + static Mode FNC1_SECOND_POSITION; + static Mode HANZI; static Mode& forBits(int bits); int getCharacterCountBits(Version *version); @@ -3788,8 +3883,109 @@ public: #endif // __MODE_H__ +// file: zxing/common/ECI.h + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + +#ifndef __ECI__ +#define __ECI__ + +/* + * Copyright 2008-2011 ZXing authors + * + * 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 zxing { + namespace common { + class ECI; + } +} +class zxing::common::ECI { +private: + const int value; + +protected: + ECI(int value); + +public: + int getValue() const; + + static ECI* getECIByValue(int value); +}; + +#endif + +// file: zxing/common/CharacterSetECI.h + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + +#ifndef __CHARACTERSET_ECI__ +#define __CHARACTERSET_ECI__ + +/* + * Copyright 2008-2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include + +namespace zxing { + namespace common { + class CharacterSetECI; + } +} + +class zxing::common::CharacterSetECI : public ECI { +private: + static std::map VALUE_TO_ECI; + static std::map NAME_TO_ECI; + static const bool inited; + static bool init_tables(); + + char const* const encodingName; + + CharacterSetECI(int value, char const* encodingName); + + static void addCharacterSet(int value, char const* encodingName); + static void addCharacterSet(int value, char const* const* encodingNames); + +public: + char const* getEncodingName(); + + static CharacterSetECI* getCharacterSetECIByValue(int value); + static CharacterSetECI* getCharacterSetECIByName(std::string const& name); +}; + +#endif + // file: zxing/qrcode/decoder/DecodedBitStreamParser.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + #ifndef __DECODED_BIT_STREAM_PARSER_H__ // #define __DECODED_BIT_STREAM_PARSER_H__ @@ -3814,35 +4010,46 @@ public: // #include // #include +// #include // #include // #include // #include // #include - - +// #include +// #include +// #include namespace zxing { namespace qrcode { class DecodedBitStreamParser { +public: + typedef std::map Hashtable; + private: - static const char ALPHANUMERIC_CHARS[]; - - static const char *ASCII; - static const char *ISO88591; - static const char *UTF8; - static const char *SHIFT_JIS; - static const char *EUC_JP; + static char const ALPHANUMERIC_CHARS[]; + static char toAlphaNumericChar(size_t value); + static void decodeHanziSegment(Ref bits, std::string &result, int count); static void decodeKanjiSegment(Ref bits, std::string &result, int count); static void decodeByteSegment(Ref bits, std::string &result, int count); - static void decodeAlphanumericSegment(Ref bits, std::string &result, int count); + static void decodeByteSegment(Ref bits_, + std::string& result, + int count, + zxing::common::CharacterSetECI* currentCharacterSetECI, + ArrayRef< ArrayRef >& byteSegments, + Hashtable const& hints); + static void decodeAlphanumericSegment(Ref bits, std::string &result, int count, bool fc1InEffect); static void decodeNumericSegment(Ref bits, std::string &result, int count); - static const char *guessEncoding(unsigned char *bytes, int length); + static void append(std::string &ost, const unsigned char *bufIn, size_t nIn, const char *src); + static void append(std::string &ost, std::string const& in, const char *src); public: - static std::string decode(ArrayRef bytes, Version *version); + static Ref decode(ArrayRef bytes, + Version *version, + ErrorCorrectionLevel const& ecLevel, + Hashtable const& hints); }; } @@ -3852,6 +4059,8 @@ public: // file: zxing/qrcode/detector/AlignmentPattern.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + #ifndef __ALIGNMENT_PATTERN_H__ // #define __ALIGNMENT_PATTERN_H__ @@ -3882,15 +4091,13 @@ namespace zxing { class AlignmentPattern : public ResultPoint { private: - float posX_; - float posY_; float estimatedModuleSize_; public: AlignmentPattern(float posX, float posY, float estimatedModuleSize); - float getX() const; - float getY() const; bool aboutEquals(float moduleSize, float i, float j) const; + Ref combineEstimate(float i, float j, + float newModuleSize) const; }; } @@ -3969,75 +4176,9 @@ private: #endif // __ALIGNMENT_PATTERN_FINDER_H__ -// file: zxing/qrcode/detector/Detector.h - -#ifndef __DETECTOR_H__ -// #define __DETECTOR_H__ - -/* - * Detector.h - * zxing - * - * Copyright 2010 ZXing authors 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. - */ - -// #include -// #include -// #include -// #include -// #include -// #include - -namespace zxing { - -class DecodeHints; - -namespace qrcode { - -class Detector : public Counted { -private: - Ref image_; - Ref callback_; - -protected: - Ref getImage(); - - static Ref sampleGrid(Ref image, int dimension, Ref); - static int computeDimension(Ref topLeft, Ref topRight, Ref bottomLeft, - float moduleSize); - float calculateModuleSize(Ref topLeft, Ref topRight, Ref bottomLeft); - float calculateModuleSizeOneWay(Ref pattern, Ref otherPattern); - float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY); - float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY); - Ref findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, - float allowanceFactor); -public: - - virtual Ref createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref alignmentPattern, int dimension); - - Detector(Ref image); - Ref detect(DecodeHints const& hints); -}; -} -} - -#endif // __DETECTOR_H__ - // file: zxing/qrcode/detector/FinderPattern.h +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- #ifndef __FINDER_PATTERN_H__ // #define __FINDER_PATTERN_H__ @@ -4068,19 +4209,17 @@ namespace zxing { class FinderPattern : public ResultPoint { private: - float posX_; - float posY_; float estimatedModuleSize_; - int counter_; + int count_; public: FinderPattern(float posX, float posY, float estimatedModuleSize); - float getX() const; - float getY() const; + FinderPattern(float posX, float posY, float estimatedModuleSize, int count); int getCount() const; float getEstimatedModuleSize() const; void incrementCount(); bool aboutEquals(float moduleSize, float i, float j) const; + Ref combineEstimate(float i, float j, float newModuleSize) const; }; } } @@ -4137,6 +4276,75 @@ public: #endif // __FINDER_PATTERN_INFO_H__ +// file: zxing/qrcode/detector/Detector.h + +#ifndef __DETECTOR_H__ +// #define __DETECTOR_H__ + +/* + * Detector.h + * zxing + * + * Copyright 2010 ZXing authors 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. + */ + +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +namespace zxing { + +class DecodeHints; + +namespace qrcode { + +class Detector : public Counted { +private: + Ref image_; + Ref callback_; + +protected: + Ref getImage(); + + static Ref sampleGrid(Ref image, int dimension, Ref); + static int computeDimension(Ref topLeft, Ref topRight, Ref bottomLeft, + float moduleSize); + float calculateModuleSize(Ref topLeft, Ref topRight, Ref bottomLeft); + float calculateModuleSizeOneWay(Ref pattern, Ref otherPattern); + float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY); + float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY); + Ref findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY, + float allowanceFactor); + Ref processFinderPatternInfo(Ref info); +public: + + virtual Ref createTransform(Ref topLeft, Ref topRight, Ref < + ResultPoint > bottomLeft, Ref alignmentPattern, int dimension); + + Detector(Ref image); + Ref detect(DecodeHints const& hints); +}; +} +} + +#endif // __DETECTOR_H__ + // file: zxing/qrcode/detector/FinderPatternFinder.h #ifndef __FINDER_PATTERN_FINDER_H__ @@ -4177,6 +4385,8 @@ namespace qrcode { class FinderPatternFinder { private: static int CENTER_QUORUM; + +protected: static int MIN_SKIP; static int MAX_MODULES; @@ -4260,3 +4470,538 @@ private: } #endif // QREDGEDETECTOR_H_ +// file: zxing/FormatException.h + +#ifndef __FORMAT_EXCEPTION_H__ +// #define __FORMAT_EXCEPTION_H__ + +/* + * FormatException.h + * zxing + * + * Copyright 2010 ZXing authors 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. + */ + +// #include + +namespace zxing { + +class FormatException : public ReaderException { +public: + FormatException(); + FormatException(const char *msg); + ~FormatException() throw(); +}; + +} +#endif // __FORMAT_EXCEPTION_H__ + +// file: zxing/NotFoundException.h + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + +#ifndef __NOT_FOUND_EXCEPTION_H__ +// #define __NOT_FOUND_EXCEPTION_H__ + +/* + * Copyright 20011 ZXing authors + * + * 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. + */ + +// #include + +namespace zxing { + + class NotFoundException : public ReaderException { + public: + NotFoundException(const char *msg); + ~NotFoundException() throw(); + }; + +} +#endif // __NOT_FOUND_EXCEPTION_H__ + +// file: zxing/common/StringUtils.h + +// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*- + +#ifndef __STRING_UTILS__ +#define __STRING_UTILS__ + +/* + * Copyright (C) 2010-2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include + +namespace zxing { + namespace common { + class StringUtils; + } +} + +class zxing::common::StringUtils { +private: + static char const* const PLATFORM_DEFAULT_ENCODING; + + StringUtils() {} + +public: + static char const* const ASCII; + static char const* const SHIFT_JIS; + static char const* const GB2312; + static char const* const EUC_JP; + static char const* const UTF8; + static char const* const ISO88591; + static const bool ASSUME_SHIFT_JIS; + + typedef std::map Hashtable; + + static std::string guessEncoding(unsigned char* bytes, int length, Hashtable const& hints); +}; + +#endif + +// file: zxing/common/detector/MonochromeRectangleDetector.h + +#ifndef __MONOCHROMERECTANGLEDETECTOR_H__ +// #define __MONOCHROMERECTANGLEDETECTOR_H__ + +/* + * MonochromeRectangleDetector.h + * y_wmk + * + * Created by Luiz Silva on 09/02/2010. + * Copyright 2010 y_wmk authors 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. + */ + +// #include +// #include +// #include +// #include +// #include +// #include + + +namespace zxing { + +struct TwoInts: public Counted { + int start; + int end; +}; + +class MonochromeRectangleDetector : public Counted { +private: + static const int MAX_MODULES = 32; + Ref image_; + +public: + MonochromeRectangleDetector(Ref image) : image_(image) { }; + + std::vector > detect(); + +private: + Ref findCornerFromCenter(int centerX, int deltaX, int left, int right, + int centerY, int deltaY, int top, int bottom, int maxWhiteRun); + + Ref blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, + bool horizontal); + + int max(int a, float b) { return (float) a > b ? a : (int) b;}; +}; +} + +#endif // __MONOCHROMERECTANGLEDETECTOR_H__ + +// file: zxing/common/detector/WhiteRectangleDetector.h + +#ifndef __WHITERECTANGLEDETECTOR_H__ +// #define __WHITERECTANGLEDETECTOR_H__ + +/* + * WhiteRectangleDetector.h + * + * + * Created by Luiz Silva on 09/02/2010. + * Copyright 2010 authors 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. + */ + +// #include +// #include +// #include +// #include +// #include +// #include + + +namespace zxing { + +class WhiteRectangleDetector : public Counted { + private: + static int INIT_SIZE; + static int CORR; + Ref image_; + int width_; + int height_; + + public: + WhiteRectangleDetector(Ref image); + std::vector > detect(); + + private: + int round(float a); + Ref getBlackPointOnSegment(float aX, float aY, float bX, float bY); + int distanceL2(float aX, float aY, float bX, float bY); + std::vector > centerEdges(Ref y, Ref z, + Ref x, Ref t); + bool containsBlackPoint(int a, int b, int fixed, bool horizontal); +}; +} + +#endif + +// file: zxing/datamatrix/detector/DetectorException.h + +/* + * DetectorException.h + * + * Created on: Aug 26, 2011 + * Author: luiz + */ + +#ifndef DETECTOREXCEPTION_H_ +#define DETECTOREXCEPTION_H_ + +// #include + +namespace zxing { +namespace datamatrix { + +class DetectorException : public Exception { + public: + DetectorException(const char *msg); + virtual ~DetectorException() throw(); +}; +} /* namespace nexxera */ +} /* namespace zxing */ +#endif /* DETECTOREXCEPTION_H_ */ + +// file: zxing/multi/ByQuadrantReader.h + +#ifndef __BY_QUADRANT_READER_H__ +// #define __BY_QUADRANT_READER_H__ + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include +// #include +// #include + +namespace zxing { +namespace multi { +class ByQuadrantReader : public Reader { + private: + Reader& delegate_; + + public: + ByQuadrantReader(Reader& delegate); + virtual ~ByQuadrantReader(); + virtual Ref decode(Ref image); + virtual Ref decode(Ref image, DecodeHints hints); +}; +} // End zxing::multi namespace +} // End zxing namespace + +#endif // __BY_QUADRANT_READER_H__ + +// file: zxing/multi/MultipleBarcodeReader.h + +#ifndef __MULTIPLE_BARCODE_READER_H__ +// #define __MULTIPLE_BARCODE_READER_H__ + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include +// #include +// #include +// #include + +namespace zxing { +namespace multi { +class MultipleBarcodeReader : public Counted { + protected: + MultipleBarcodeReader() {} + public: + virtual std::vector > decodeMultiple(Ref image); + virtual std::vector > decodeMultiple(Ref image, DecodeHints hints) = 0; + virtual ~MultipleBarcodeReader(); +}; +} // End zxing::multi namespace +} // End zxing namespace + +#endif // __MULTIPLE_BARCODE_READER_H__ + +// file: zxing/multi/GenericMultipleBarcodeReader.h + +#ifndef __GENERIC_MULTIPLE_BARCODE_READER_H__ +// #define __GENERIC_MULTIPLE_BARCODE_READER_H__ + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include + +namespace zxing { +namespace multi { +class GenericMultipleBarcodeReader : public MultipleBarcodeReader { + private: + static Ref translateResultPoints(Ref result, + int xOffset, + int yOffset); + void doDecodeMultiple(Ref image, + DecodeHints hints, + std::vector >& results, + int xOffset, + int yOffset); + Reader& delegate_; + static const int MIN_DIMENSION_TO_RECUR = 100; + + public: + GenericMultipleBarcodeReader(Reader& delegate); + virtual ~GenericMultipleBarcodeReader(); + virtual std::vector > decodeMultiple(Ref image, + DecodeHints hints); +}; +} // End zxing::multi namespace +} // End zxing namespace + +#endif // __GENERIC_MULTIPLE_BARCODE_READER_H__ + +// file: zxing/multi/qrcode/QRCodeMultiReader.h + +#ifndef __QRCODE_MULTI_READER_H__ +// #define __QRCODE_MULTI_READER_H__ + +/* + * Copyright 2011 ZXing authors 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. + */ + +// #include +// #include + +namespace zxing { +namespace multi { +class QRCodeMultiReader: public zxing::qrcode::QRCodeReader, public MultipleBarcodeReader { + public: + QRCodeMultiReader(); + virtual ~QRCodeMultiReader(); + virtual std::vector > decodeMultiple(Ref image, DecodeHints hints); +}; +} // End zxing::multi namespace +} // End zxing namespace + +#endif // __QRCODE_MULTI_READER_H__ + +// file: zxing/multi/qrcode/detector/MultiDetector.h + +#ifndef __MULTI_DETECTOR_H__ +// #define __MULTI_DETECTOR_H__ + +/* + * Copyright 2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include + +namespace zxing { +namespace multi { +class MultiDetector : public zxing::qrcode::Detector { + public: + MultiDetector(Ref image); + virtual ~MultiDetector(); + virtual std::vector > detectMulti(DecodeHints hints); +}; +} // End zxing::multi namespace +} // End zxing namespace + +#endif // __MULTI_DETECTOR_H__ + +// file: zxing/multi/qrcode/detector/MultiFinderPatternFinder.h + +#ifndef __MULTI_FINDER_PATTERN_FINDER_H__ +// #define __MULTI_FINDER_PATTERN_FINDER_H__ + +/* + * Copyright 2011 ZXing authors + * + * 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. + */ + +// #include +// #include +// #include + +namespace zxing { +namespace multi { +class MultiFinderPatternFinder : zxing::qrcode::FinderPatternFinder { + private: + std::vector > > selectBestPatterns(); + + static const float MAX_MODULE_COUNT_PER_EDGE; + static const float MIN_MODULE_COUNT_PER_EDGE; + static const float DIFF_MODSIZE_CUTOFF_PERCENT; + static const float DIFF_MODSIZE_CUTOFF; + + public: + MultiFinderPatternFinder(Ref image, Ref resultPointCallback); + virtual ~MultiFinderPatternFinder(); + virtual std::vector > findMulti(DecodeHints const& hints); + + +}; +} +} + +#endif // __MULTI_FINDER_PATTERN_FINDER_H__ + diff --git a/iOS/CalendarPlugin/README b/iOS/CalendarPlugin/README index 92e2892..d463cd9 100644 --- a/iOS/CalendarPlugin/README +++ b/iOS/CalendarPlugin/README @@ -26,29 +26,36 @@ // // function call in Javascript: // -// createEvent : function(title,location,notes, startDate, endDate){ +// createEvent : function(title,location,notes, startDate, endDate, calendarName){ // var title= "My Appt"; // var location = "Los Felix"; // var notes = "me testing"; // var startDate = "2012-01-23 09:30:00"; // var endDate = "2012-01-23 12:30:00"; +// var calendarName = "Work"; // optional // -// cal.createEvent(title,location,notes,startDate,endDate); +// cal.createEvent(title,location,notes,startDate,endDate, calendarName); // }, // // // Which can also be written as: // -// function createEvent(title,location,notes, startDate, endDate){ +// function createEvent(title,location,notes, startDate, endDate, calendarName){ // var title= "My Appt"; // var location = "Los Felix"; // var notes = "me testing"; // var startDate = "2012-01-23 09:30:00"; // var endDate = "2012-01-23 12:30:00"; +// var calendarName = "Work"; // optional // -// cal.createEvent(title,location,notes,startDate,endDate); +// cal.createEvent(title,location,notes,startDate,endDate, calendarName); // } // +// Also, there is a method "getCalendarList" which provides a list of calendar titles if you want +// pass in the title as the last parameter to createEvent to specify a calendar other than the default. +// call with: +// window.plugins.calendarPlugin.getCalendarList(successCallback, failureCallback); +// // The example I've written, although the function is receiving variables, // for testing I declared the necessary variables. You will need to comment these out and pass // your variables normally through the function call. diff --git a/iOS/CalendarPlugin/calendar.js b/iOS/CalendarPlugin/calendar.js index be6cd48..c8de9c4 100644 --- a/iOS/CalendarPlugin/calendar.js +++ b/iOS/CalendarPlugin/calendar.js @@ -13,9 +13,14 @@ function calendarPlugin() -calendarPlugin.prototype.createEvent = function(title,location,notes,startDate,endDate) { - console.log("here"); - cordova.exec(null,null,"calendarPlugin","createEvent", [title,location,notes,startDate,endDate]); +calendarPlugin.prototype.createEvent = function(title,location,notes,startDate,endDate,calendarName) { + console.log("creating event"); + cordova.exec(null,null,"calendarPlugin","createEvent", [title,location,notes,startDate,endDate,calendarName]); +}; + +calendarPlugin.prototype.getCalendarList = function(response, err) { + console.log("getting calendars"); + cordova.exec(response, err, "calendarPlugin", "getCalendarList",[]); }; // More methods will need to be added like fetch events, delete event, edit event diff --git a/iOS/CalendarPlugin/calendarPlugin.h b/iOS/CalendarPlugin/calendarPlugin.h index 500a446..a6a3d2f 100644 --- a/iOS/CalendarPlugin/calendarPlugin.h +++ b/iOS/CalendarPlugin/calendarPlugin.h @@ -33,6 +33,7 @@ // Calendar Instance methods - (void)createEvent:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void)getCalendarList:(NSMutableArray *) arguments withDict:(NSMutableDictionary*)options; //- (void)modifyEvent:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; diff --git a/iOS/CalendarPlugin/calendarPlugin.m b/iOS/CalendarPlugin/calendarPlugin.m index 9dce652..68c9fc8 100644 --- a/iOS/CalendarPlugin/calendarPlugin.m +++ b/iOS/CalendarPlugin/calendarPlugin.m @@ -23,8 +23,7 @@ return self; } - --(void)createEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +-(void)createEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { //Get the Event store object EKEvent *myEvent; @@ -33,12 +32,31 @@ store = [[EKEventStore alloc] init]; myEvent = [EKEvent eventWithEventStore: store]; - NSString* title = [arguments objectAtIndex:1]; - NSString* location = [arguments objectAtIndex:2]; - NSString* message = [arguments objectAtIndex:3]; - NSString *startDate = [arguments objectAtIndex:4]; - NSString *endDate = [arguments objectAtIndex:5]; + NSString* title = [arguments objectAtIndex:1]; + NSString* location = [arguments objectAtIndex:2]; + NSString* message = [arguments objectAtIndex:3]; + NSString* startDate = [arguments objectAtIndex:4]; + NSString* endDate = [arguments objectAtIndex:5]; + NSString* calendarTitle = [arguments objectAtIndex:6]; + EKCalendar* calendar = nil; + if(calendarTitle == nil){ + calendar = store.defaultCalendarForNewEvents; + } else { + NSIndexSet* indexes = [store.calendars indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + *stop = false; + EKCalendar* cal = (EKCalendar*)obj; + if(cal.title == calendarTitle){ + *stop = true; + } + return *stop; + }]; + if (indexes.count == 0) { + calendar = store.defaultCalendarForNewEvents; + } else { + calendar = [store.calendars objectAtIndex:[indexes firstIndex]]; + } + } //creating the dateformatter object NSDateFormatter *sDate = [[[NSDateFormatter alloc] init] autorelease]; @@ -56,7 +74,7 @@ myEvent.notes = message; myEvent.startDate = myStartDate; myEvent.endDate = myEndDate; - myEvent.calendar = store.defaultCalendarForNewEvents; + myEvent.calendar = calendar; EKAlarm *reminder = [EKAlarm alarmWithRelativeOffset:-2*60*60]; @@ -82,10 +100,11 @@ //-(void)deleteEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options {} -/*-(void)findEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { +/* +-(void)findEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { - store = [[EKEventStore alloc] init]; - myEvent = [EKEvent eventWithEventStore: store]; + EKEventStore* store = [[EKEventStore alloc] init]; + EKEvent* myEvent = [EKEvent eventWithEventStore: store]; NSString *startSearchDate = [arguments objectAtIndex:1]; NSString *endSearchDate = [arguments objectAtIndex:2]; @@ -112,22 +131,42 @@ [self setEvents:events]; - } +} + */ + +-(void)getCalendarList:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSLog(@"In plugin method getCalendarList"); + NSString *callback = [arguments objectAtIndex:0]; + EKEventStore* store = [[EKEventStore alloc] init]; + NSString* js = nil; + if (store != nil && store.calendars.count > 0) { + NSMutableArray *titles = [[store.calendars valueForKey:@"title"] mutableCopy]; + NSLog(@"Found %i calendars", titles.count); + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:titles]; + js = [result toSuccessCallbackString:callback]; + } else { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no calendars found"]; + js = [result toErrorCallbackString:callback]; + } + [self writeJavascript:js]; +} - //-(void)modifyEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options{ + +/*-(void)modifyEvent:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options{ EKEventViewController *eventViewController = [[EKEventViewController alloc] init]; eventViewController.event = myEvent; eventViewController.allowsEditing = YES; navigationController we = [[UINavigationController alloc] initWithRootViewController:eventViewController]; - [eventViewController release];*/ -// } + [eventViewController release]; +} */ //delegate method for EKEventEditViewDelegate -(void)eventEditViewController:(EKEventEditViewController *)controller didCompleteWithAction:(EKEventEditViewAction)action { - [self dismissModalViewControllerAnimated:YES]; + [(UIViewController*)self dismissModalViewControllerAnimated:YES]; [self release]; } @end diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left-72@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left-72@2x.png deleted file mode 100644 index 5890ade..0000000 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left-72@2x.png and /dev/null differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left@2x.png index 5890ade..530e12b 100644 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left@2x.png and b/iOS/ChildBrowser/ChildBrowser.bundle/arrow_left@2x.png differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right-72@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right-72@2x.png deleted file mode 100644 index 27f2f44..0000000 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right-72@2x.png and /dev/null differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right@2x.png index 27f2f44..8b3d855 100644 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right@2x.png and b/iOS/ChildBrowser/ChildBrowser.bundle/arrow_right@2x.png differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh-72@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh-72@2x.png deleted file mode 100644 index feee55f..0000000 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh-72@2x.png and /dev/null differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh@2x.png index feee55f..309b6bd 100644 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh@2x.png and b/iOS/ChildBrowser/ChildBrowser.bundle/but_refresh@2x.png differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/compass-72@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/compass-72@2x.png deleted file mode 100644 index f2f2729..0000000 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/compass-72@2x.png and /dev/null differ diff --git a/iOS/ChildBrowser/ChildBrowser.bundle/compass@2x.png b/iOS/ChildBrowser/ChildBrowser.bundle/compass@2x.png index f2f2729..46a8901 100644 Binary files a/iOS/ChildBrowser/ChildBrowser.bundle/compass@2x.png and b/iOS/ChildBrowser/ChildBrowser.bundle/compass@2x.png differ diff --git a/iOS/ChildBrowser/ChildBrowserCommand.h b/iOS/ChildBrowser/ChildBrowserCommand.h index 982f04c..7f8e6ad 100644 --- a/iOS/ChildBrowser/ChildBrowserCommand.h +++ b/iOS/ChildBrowser/ChildBrowserCommand.h @@ -2,25 +2,14 @@ // Copyright 2010 Nitobi. All rights reserved. // Copyright 2012, Randy McMillan - -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "Cordova/CDVPlugin.h" -#endif #import "ChildBrowserViewController.h" +@interface ChildBrowserCommand : CDVPlugin {} -#ifdef CORDOVA_FRAMEWORK - @interface ChildBrowserCommand : CDVPlugin { -#endif - ChildBrowserViewController* childBrowser; -} +@property (nonatomic, strong) ChildBrowserViewController* childBrowser; -@property (nonatomic, retain) ChildBrowserViewController *childBrowser; - - -- (void) showWebPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; --(void) onChildLocationChange:(NSString*)newLoc; +- (void)showWebPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void)onChildLocationChange:(NSString*)newLoc; @end diff --git a/iOS/ChildBrowser/ChildBrowserCommand.m b/iOS/ChildBrowser/ChildBrowserCommand.m index 89d3983..5a9b73e 100644 --- a/iOS/ChildBrowser/ChildBrowserCommand.m +++ b/iOS/ChildBrowser/ChildBrowserCommand.m @@ -3,24 +3,22 @@ // Copyright 2012, Randy McMillan #import "ChildBrowserCommand.h" - -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "Cordova/CDVViewController.h" -#endif - @implementation ChildBrowserCommand @synthesize childBrowser; -- (void) showWebPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options // args: url +- (void)showWebPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options // args: url { - if(childBrowser == NULL) - { - childBrowser = [[ ChildBrowserViewController alloc ] initWithScale:FALSE ]; - childBrowser.delegate = self; + if (self.childBrowser == nil) { +#if __has_feature(objc_arc) + self.childBrowser = [[ChildBrowserViewController alloc] initWithScale:NO]; +#else + self.childBrowser = [[[ChildBrowserViewController alloc] initWithScale:NO] autorelease]; +#endif + self.childBrowser.delegate = self; + self.childBrowser.orientationDelegate = self.viewController; } /* // TODO: Work in progress @@ -28,49 +26,53 @@ NSArray* supportedOrientations = [strOrientations componentsSeparatedByString:@","]; */ -#ifdef CORDOVA_FRAMEWORK - CDVViewController* cont = (CDVViewController*)[ super viewController ]; - childBrowser.supportedOrientations = cont.supportedOrientations; - [ cont presentModalViewController:childBrowser animated:YES ]; -#endif + [self.viewController presentModalViewController:childBrowser animated:YES]; - NSString *url = (NSString*) [arguments objectAtIndex:0]; + NSString* url = (NSString*)[arguments objectAtIndex:0]; - [childBrowser loadURL:url ]; - -} -- (void) getPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { - NSString *url = (NSString*) [arguments objectAtIndex:0]; - [childBrowser loadURL:url ]; + [self.childBrowser loadURL:url]; } --(void) close:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options // args: url +- (void)getPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { - [ childBrowser closeBrowser]; + NSString* url = (NSString*)[arguments objectAtIndex:0]; + [self.childBrowser loadURL:url]; } --(void) onClose +- (void)close:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options // args: url { - NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.childBrowser.onClose();",@""]; - [self.webView stringByEvaluatingJavaScriptFromString:jsCallback]; + [self.childBrowser closeBrowser]; } --(void) onOpenInSafari +- (void)onClose { - NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.childBrowser.onOpenExternal();",@""]; - [self.webView stringByEvaluatingJavaScriptFromString:jsCallback]; + [self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.childBrowser.onClose();"]; } - --(void) onChildLocationChange:(NSString*)newLoc +- (void)onOpenInSafari { + [self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.childBrowser.onOpenExternal();"]; +} - NSString* tempLoc = [NSString stringWithFormat:@"%@",newLoc]; +- (void)onChildLocationChange:(NSString*)newLoc +{ + NSString* tempLoc = [NSString stringWithFormat:@"%@", newLoc]; NSString* encUrl = [tempLoc stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.childBrowser.onLocationChange('%@');",encUrl]; - [self.webView stringByEvaluatingJavaScriptFromString:jsCallback]; + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.childBrowser.onLocationChange('%@');", encUrl]; + [self.webView stringByEvaluatingJavaScriptFromString:jsCallback]; } + + +#if !__has_feature(objc_arc) +- (void)dealloc +{ + self.childBrowser = nil; + + [super dealloc]; +} +#endif + @end diff --git a/iOS/ChildBrowser/ChildBrowserViewController.h b/iOS/ChildBrowser/ChildBrowserViewController.h index a870d69..07f0183 100644 --- a/iOS/ChildBrowser/ChildBrowserViewController.h +++ b/iOS/ChildBrowser/ChildBrowserViewController.h @@ -4,8 +4,7 @@ #import -@protocol ChildBrowserDelegate - +@protocol ChildBrowserDelegate /* * onChildLocationChanging:newLoc @@ -13,38 +12,43 @@ * Discussion: * Invoked when a new page has loaded */ --(void) onChildLocationChange:(NSString*)newLoc; --(void) onOpenInSafari; --(void) onClose; +- (void)onChildLocationChange:(NSString*)newLoc; +- (void)onOpenInSafari; +- (void)onClose; + @end +@protocol CDVOrientationDelegate -@interface ChildBrowserViewController : UIViewController < UIWebViewDelegate > { - IBOutlet UIWebView* webView; - IBOutlet UIBarButtonItem* closeBtn; - IBOutlet UIBarButtonItem* refreshBtn; - IBOutlet UILabel* addressLabel; - IBOutlet UIBarButtonItem* backBtn; - IBOutlet UIBarButtonItem* fwdBtn; - IBOutlet UIBarButtonItem* safariBtn; - IBOutlet UIActivityIndicatorView* spinner; - BOOL scaleEnabled; - BOOL isImage; - NSString* imageURL; - NSArray* supportedOrientations; - id delegate; -} +- (NSUInteger)supportedInterfaceOrientations; +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +- (BOOL)shouldAutorotate; -@property (nonatomic, retain)id delegate; -@property (nonatomic, retain) NSArray* supportedOrientations; -@property(retain) NSString* imageURL; -@property(assign) BOOL isImage; +@end + +@interface ChildBrowserViewController : UIViewController {} + +@property (nonatomic, strong) IBOutlet UIWebView* webView; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeBtn; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* refreshBtn; +@property (nonatomic, strong) IBOutlet UILabel* addressLabel; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* backBtn; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* fwdBtn; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* safariBtn; +@property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner; + +// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the two properties below +@property (nonatomic, unsafe_unretained) id delegate; +@property (nonatomic, unsafe_unretained) id orientationDelegate; + +@property (copy) NSString* imageURL; +@property (assign) BOOL isImage; +@property (assign) BOOL scaleEnabled; -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation; - (ChildBrowserViewController*)initWithScale:(BOOL)enabled; - (IBAction)onDoneButtonPress:(id)sender; - (IBAction)onSafariButtonPress:(id)sender; - (void)loadURL:(NSString*)url; --(void)closeBrowser; +- (void)closeBrowser; @end diff --git a/iOS/ChildBrowser/ChildBrowserViewController.m b/iOS/ChildBrowser/ChildBrowserViewController.m index c445541..8160bd5 100644 --- a/iOS/ChildBrowser/ChildBrowserViewController.m +++ b/iOS/ChildBrowser/ChildBrowserViewController.m @@ -1,4 +1,4 @@ -/// Created by Jesse MacFadyen on 10-05-29. +// / Created by Jesse MacFadyen on 10-05-29. // Copyright 2010 Nitobi. All rights reserved. // Copyright 2012, Randy McMillan @@ -6,10 +6,10 @@ @implementation ChildBrowserViewController -@synthesize imageURL; -@synthesize supportedOrientations; -@synthesize isImage; -@synthesize delegate; +@synthesize imageURL, isImage; +@synthesize delegate, orientationDelegate; +@synthesize spinner, webView, addressLabel; +@synthesize closeBtn, refreshBtn, backBtn, fwdBtn, safariBtn; /* // The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. @@ -21,198 +21,162 @@ } */ -+ (NSString*) resolveImageResource:(NSString*)resource ++ (NSString*)resolveImageResource:(NSString*)resource { NSString* systemVersion = [[UIDevice currentDevice] systemVersion]; BOOL isLessThaniOS4 = ([systemVersion compare:@"4.0" options:NSNumericSearch] == NSOrderedAscending); - // the iPad image (nor retina) differentiation code was not in 3.x, and we have to explicitly set the path if (isLessThaniOS4) { return [NSString stringWithFormat:@"%@.png", resource]; - } - else - { - if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00) { - return [NSString stringWithFormat:@"%@-72@2x.png", resource]; - } else { - // - - return [NSString stringWithFormat:@"%@@2x.png", resource]; - + } else { + if (([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES) && ([[UIScreen mainScreen] scale] == 2.00)) { + return [NSString stringWithFormat:@"%@@2x.png", resource]; } } - return resource; + return resource; // if all else fails } - - (ChildBrowserViewController*)initWithScale:(BOOL)enabled { self = [super init]; - scaleEnabled = enabled; + self.scaleEnabled = enabled; return self; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. -- (void)viewDidLoad { +- (void)viewDidLoad +{ [super viewDidLoad]; - refreshBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/but_refresh"]]; - backBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/arrow_left"]]; - fwdBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/arrow_right"]]; - safariBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/compass"]]; + self.refreshBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/but_refresh"]]; + self.backBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/arrow_left"]]; + self.fwdBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/arrow_right"]]; + self.safariBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/compass"]]; - webView.delegate = self; - webView.scalesPageToFit = TRUE; - webView.backgroundColor = [UIColor whiteColor]; + self.webView.delegate = self; + self.webView.scalesPageToFit = TRUE; + self.webView.backgroundColor = [UIColor whiteColor]; NSLog(@"View did load"); } -- (void)didReceiveMemoryWarning { -// Releases the view if it doesn't have a superview. +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; -// Release any cached data, images, etc that aren't in use. + // Release any cached data, images, etc that aren't in use. } -- (void)viewDidUnload { +- (void)viewDidUnload +{ // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; NSLog(@"View did UN-load"); } +- (void)dealloc +{ + self.webView.delegate = nil; + self.delegate = nil; + self.orientationDelegate = nil; -- (void)dealloc { +#if !__has_feature(objc_arc) + self.webView = nil; + self.closeBtn = nil; + self.refreshBtn = nil; + self.addressLabel = nil; + self.backBtn = nil; + self.fwdBtn = nil; + self.safariBtn = nil; + self.spinner = nil; - webView.delegate = nil; - - [webView release]; - [closeBtn release]; - [refreshBtn release]; - [addressLabel release]; - [backBtn release]; - [fwdBtn release]; - [safariBtn release]; - [spinner release]; - [ supportedOrientations release]; [super dealloc]; +#endif } --(void)closeBrowser +- (void)closeBrowser { - - if(delegate != NULL) { - [delegate onClose]; + if (self.delegate != nil) { + [self.delegate onClose]; } - if ([self respondsToSelector:@selector(presentingViewController)]) { - //Reference UIViewController.h Line:179 for update to iOS 5 difference - @RandyMcMillan + if ([self respondsToSelector:@selector(presentingViewController)]) { + // Reference UIViewController.h Line:179 for update to iOS 5 difference - @RandyMcMillan [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; } else { [[self parentViewController] dismissModalViewControllerAnimated:YES]; } } --(IBAction) onDoneButtonPress:(id)sender +- (IBAction)onDoneButtonPress:(id)sender { - [ self closeBrowser]; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]; -// NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@""]]; - [webView loadRequest:request]; + [self closeBrowser]; + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]; + [self.webView loadRequest:request]; } - --(IBAction) onSafariButtonPress:(id)sender +- (IBAction)onSafariButtonPress:(id)sender { - - if(delegate != NULL) - { - [delegate onOpenInSafari]; + if (self.delegate != nil) { + [self.delegate onOpenInSafari]; } - if(isImage) - { - NSURL* pURL = [ [NSURL alloc] initWithString:imageURL ]; - [ [ UIApplication sharedApplication ] openURL:pURL ]; + if (self.isImage) { + NSURL* pURL = [NSURL URLWithString:self.imageURL]; + [[UIApplication sharedApplication] openURL:pURL]; + } else { + NSURLRequest* request = self.webView.request; + [[UIApplication sharedApplication] openURL:request.URL]; } - else - { - NSURLRequest *request = webView.request; - [[UIApplication sharedApplication] openURL:request.URL]; - } - - } -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation -{ - BOOL autoRotate = [self.supportedOrientations count] > 1; // autorotate if only more than 1 orientation supported - if (autoRotate) - { - if ([self.supportedOrientations containsObject: - [NSNumber numberWithInt:interfaceOrientation]]) { - return YES; - } - } - - return NO; -} - - - - - (void)loadURL:(NSString*)url { - NSLog(@"Opening Url : %@",url); + NSLog(@"Opening Url : %@", url); - if( [url hasSuffix:@".png" ] || - [url hasSuffix:@".jpg" ] || - [url hasSuffix:@".jpeg" ] || - [url hasSuffix:@".bmp" ] || - [url hasSuffix:@".gif" ] ) - { - [ imageURL release ]; - imageURL = [url copy]; - isImage = YES; + if ([url hasSuffix:@".png"] || + [url hasSuffix:@".jpg"] || + [url hasSuffix:@".jpeg"] || + [url hasSuffix:@".bmp"] || + [url hasSuffix:@".gif"]) { + self.imageURL = nil; + self.imageURL = url; + self.isImage = YES; NSString* htmlText = @""; - htmlText = [ htmlText stringByReplacingOccurrencesOfString:@"IMGSRC" withString:url ]; - - [webView loadHTMLString:htmlText baseURL:[NSURL URLWithString:@""]]; + htmlText = [htmlText stringByReplacingOccurrencesOfString:@"IMGSRC" withString:url]; + [self.webView loadHTMLString:htmlText baseURL:[NSURL URLWithString:@""]]; + } else { + self.imageURL = @""; + self.isImage = NO; + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; + [self.webView loadRequest:request]; } - else - { - imageURL = @""; - isImage = NO; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; - [webView loadRequest:request]; - } - webView.hidden = NO; + self.webView.hidden = NO; } - -- (void)webViewDidStartLoad:(UIWebView *)sender { - addressLabel.text = @"Loading..."; - backBtn.enabled = webView.canGoBack; - fwdBtn.enabled = webView.canGoForward; - - [ spinner startAnimating ]; - -} - -- (void)webViewDidFinishLoad:(UIWebView *)sender +- (void)webViewDidStartLoad:(UIWebView*)sender { - NSURLRequest *request = webView.request; - NSLog(@"New Address is : %@",request.URL.absoluteString); - addressLabel.text = request.URL.absoluteString; - backBtn.enabled = webView.canGoBack; - fwdBtn.enabled = webView.canGoForward; - [ spinner stopAnimating ]; + self.addressLabel.text = @"Loading..."; + self.backBtn.enabled = self.webView.canGoBack; + self.fwdBtn.enabled = self.webView.canGoForward; - if(delegate != NULL) - { - [delegate onChildLocationChange:request.URL.absoluteString]; + [self.spinner startAnimating]; +} + +- (void)webViewDidFinishLoad:(UIWebView*)sender +{ + NSURLRequest* request = self.webView.request; + + NSLog(@"New Address is : %@", request.URL.absoluteString); + self.addressLabel.text = request.URL.absoluteString; + self.backBtn.enabled = self.webView.canGoBack; + self.fwdBtn.enabled = self.webView.canGoForward; + [self.spinner stopAnimating]; + + if (self.delegate != NULL) { + [self.delegate onChildLocationChange:request.URL.absoluteString]; } - } /*- (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error { @@ -231,4 +195,34 @@ } } */ + +#pragma mark CDVOrientationDelegate + +- (BOOL)shouldAutorotate +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { + return [self.orientationDelegate shouldAutorotate]; + } + + return YES; +} + +- (NSUInteger)supportedInterfaceOrientations +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { + return [self.orientationDelegate supportedInterfaceOrientations]; + } + + return UIInterfaceOrientationMaskPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { + return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + + return YES; +} + @end diff --git a/iOS/ChildBrowser/index.html b/iOS/ChildBrowser/index.html index 4e4da2c..0cbff01 100644 --- a/iOS/ChildBrowser/index.html +++ b/iOS/ChildBrowser/index.html @@ -1,95 +1,94 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + -

Hey, it's Cordova!

-

Don't know how to get started? Check out our Getting Started Guide -
-

    -
  1. Check your console log for any white-list rejection errors.
  2. -
  3. Add your allowed hosts in Cordova.plist/ExternalHosts (wildcards OK, don't enter the URL scheme)
  4. -
- - +

Hey, it's Cordova!

+

Don't know how to get started? Check out our Getting Started Guide +
+

    +
  1. Check your console log for any white-list rejection errors.
  2. +
  3. Add your allowed hosts in Cordova.plist/ExternalHosts (wildcards OK, don't enter the URL scheme)
  4. +
+ + diff --git a/iOS/DatePicker/DatePicker.js b/iOS/DatePicker/DatePicker.js index 9f92916..541f72f 100644 --- a/iOS/DatePicker/DatePicker.js +++ b/iOS/DatePicker/DatePicker.js @@ -39,7 +39,8 @@ if (!window.plugins.datePicker) { var defaults = { mode: 'datetime', date: '', - allowOldDates: true + allowOldDates: true, + allowFutureDates : true } for (var key in defaults) { if (typeof options[key] !== "undefined") diff --git a/iOS/DatePicker/DatePicker.m b/iOS/DatePicker/DatePicker.m index 5bbdb79..ca4a410 100644 --- a/iOS/DatePicker/DatePicker.m +++ b/iOS/DatePicker/DatePicker.m @@ -158,6 +158,7 @@ NSString *mode = [optionsOrNil objectForKey:@"mode"]; NSString *dateString = [optionsOrNil objectForKey:@"date"]; BOOL allowOldDates = NO; + BOOL allowFutureDates = YES; if ([[optionsOrNil objectForKey:@"allowOldDates"] intValue] == 1) { allowOldDates = YES; @@ -166,6 +167,14 @@ if ( ! allowOldDates) { self.datePicker.minimumDate = [NSDate date]; } + + if ([[optionsOrNil objectForKey:@"allowFutureDates"] intValue] == 0) { + allowFutureDates = NO; + } + + if ( ! allowFutureDates) { + self.datePicker.maximumDate = [NSDate date]; + } self.datePicker.date = [self.isoDateFormatter dateFromString:dateString]; diff --git a/iOS/DeviceDetails/DeviceDetails.h b/iOS/DeviceDetails/DeviceDetails.h new file mode 100644 index 0000000..ebe6ad3 --- /dev/null +++ b/iOS/DeviceDetails/DeviceDetails.h @@ -0,0 +1,13 @@ +#import +#import + +@interface DeviceDetails : CDVPlugin { + NSMutableDictionary* callbackIds; +} + +@property (nonatomic, retain) NSMutableDictionary* callbackIds; + +- (void)getDeviceDetails:(NSMutableArray *)arguments withDict:(NSMutableDictionary*)options; +- (void)getDeviceUUID:(NSMutableArray *)arguments withDict:(NSMutableDictionary*)options; + +@end \ No newline at end of file diff --git a/iOS/DeviceDetails/DeviceDetails.js b/iOS/DeviceDetails/DeviceDetails.js new file mode 100644 index 0000000..262ca79 --- /dev/null +++ b/iOS/DeviceDetails/DeviceDetails.js @@ -0,0 +1,16 @@ +;(function(cordova) { + + function DeviceDetails() {} + + DeviceDetails.prototype.getDetails = function(callback) { + cordova.exec(callback, callback, "DeviceDetails", "getDeviceDetails", []) + } + + DeviceDetails.prototype.getUUID = function(callback) { + cordova.exec(callback, callback, "DeviceDetails", "getDeviceUUID", []) + } + + if (!window.plugins) window.plugins = {} + window.plugins.deviceDetails = new DeviceDetails() + +})(window.cordova || window.Cordova || window.PhoneGap); diff --git a/iOS/DeviceDetails/DeviceDetails.m b/iOS/DeviceDetails/DeviceDetails.m new file mode 100644 index 0000000..efff79b --- /dev/null +++ b/iOS/DeviceDetails/DeviceDetails.m @@ -0,0 +1,49 @@ +#import "DeviceDetails.h" +#import + +@implementation DeviceDetails + +@synthesize callbackIds = _callbackIds; + +- (NSMutableDictionary*)callbackIds { + if(_callbackIds == nil) { + _callbackIds = [[NSMutableDictionary alloc] init]; + } + return _callbackIds; +} + +- (void)getDeviceDetails:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { + [self.callbackIds setValue:[arguments pop] forKey:@"getDeviceDetails"]; + + NSMutableDictionary *results = [NSMutableDictionary dictionary]; + + // Get the users Device Model, Display Name Token & Version Number + UIDevice *dev = [UIDevice currentDevice]; + [results setValue:dev.uniqueIdentifier forKey:@"deviceUuid"]; + [results setValue:dev.name forKey:@"deviceName"]; + [results setValue:dev.model forKey:@"deviceModel"]; + [results setValue:dev.systemVersion forKey:@"deviceSystemVersion"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:results]; + [self writeJavascript:[pluginResult toSuccessCallbackString:[self.callbackIds valueForKey:@"getDeviceDetails"]]]; +} + +- (void)getDeviceUUID:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options { + [self.callbackIds setValue:[arguments pop] forKey:@"getDeviceUUID"]; + + NSMutableDictionary *results = [NSMutableDictionary dictionary]; + + // Get the users Device UUID + UIDevice *dev = [UIDevice currentDevice]; + [results setValue:dev.uniqueIdentifier forKey:@"deviceUuid"]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:results]; + [self writeJavascript:[pluginResult toSuccessCallbackString:[self.callbackIds valueForKey:@"getDeviceUUID"]]]; +} + +- (void) dealloc { + [_callbackIds dealloc]; + [super dealloc]; +} + +@end diff --git a/iOS/DeviceDetails/README.md b/iOS/DeviceDetails/README.md new file mode 100644 index 0000000..d5c53cc --- /dev/null +++ b/iOS/DeviceDetails/README.md @@ -0,0 +1,14 @@ +# Cordova DeviceDetails plugin # + + window.plugins.deviceDetails.getDetails(function(details) { + // {"deviceModel":"iPhone Simulator","deviceName":"iPhone Simulator","deviceSystemVersion":"5.1"} + localStorage.setItem('deviceModel', details.deviceModel) + localStorage.setItem('deviceName', details.deviceName) + localStorage.setItem('deviceSystemVersion', details.deviceSystemVersion) + }) + + // UUID is in a separate call because it got deprecated and requires explicit user permission now + window.plugins.deviceDetails.getUUID(function(response) { + // {"deviceUuid":"AADCC45F-C305-5A7F-96F7-6CF122CD0388"} + localStorage.setItem('deviceUuid', response.deviceUuid) + }) diff --git a/iOS/EmailComposer/EmailComposer.h b/iOS/EmailComposer/EmailComposer.h index b87f4e3..20f3957 100755 --- a/iOS/EmailComposer/EmailComposer.h +++ b/iOS/EmailComposer/EmailComposer.h @@ -8,11 +8,7 @@ #import #import -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "CDVPlugin.h" -#endif @interface EmailComposer : CDVPlugin < MFMailComposeViewControllerDelegate > { diff --git a/iOS/EmailComposer/index.html b/iOS/EmailComposer/index.html index ea038ae..873dc1c 100644 --- a/iOS/EmailComposer/index.html +++ b/iOS/EmailComposer/index.html @@ -1,72 +1,54 @@ - - - - - - - - - - - - - - - -

Hey, it's Cordova!

-

Don't know how to get started? Check out our Getting Started Guide -
-

    -
  1. Check your console log for any white-list rejection errors.
  2. -
  3. Add your allowed hosts in Cordova.plist/ExternalHosts (wildcards OK, don't enter the URL scheme)
  4. -
- + + + + + + + Hello Cordova + + +
+

Apache Cordovaâ„¢

+
+ + +
+
+ + + + + + +
+ + + diff --git a/iOS/ExternalFileUtil/README b/iOS/ExternalFileUtil/README new file mode 100644 index 0000000..6ac3136 --- /dev/null +++ b/iOS/ExternalFileUtil/README @@ -0,0 +1,37 @@ +PhoneGap iOS ExternalFileUtil Plugin +by: Andrew Trice (atrice@adobe.com) + +The PhoneGap iOS ExternalFileUtil Plugin is a native Plugin for PhoneGap/Apache Cordova which provides the ability to open documents in an external program on the iOS device. A sample use case would be that you want to provide the user the ability to open a file in another program, of their choosing. For instance, the user wants to open a DRM-protected PDF in Adobe Reader. Note: the user must already have the appropriate external reader app installed. + +The plugin accepts a URL path to the document which you want to open, as well as the UTI (uniform type identifier). The plugin will then display an "open with" dialog which allows the user to select which file to use for opening the document. + +Sample usage for a PDF document: +ExternalFileUtil.openWith( "http://www.tricedesigns.com/temp/log_samsung.pdf", "com.adobe.pdf" ); + +The PhoneGap native plugin is written in Objective C, with a JavaScript interface to integrate with the client application. + +The plugin workflow is as follows: +1) App requests openWith action, specifying a file URL and UTI. +2) Plugin downloads the file and saves as a local temp file +3) Plugin uses UIDocumentInteractionController to launch an "open with" dialog +4) User selects appropriate app to "preview" the content +5) The appropriate reader app is opened and UI/input is changed to that app +6) Plugin deletes the temp file + +The "plugin" directory contains all source code for the plugin, separated into 2 separate folders: the JavaScript interface and the native Objective-C code. + +The "samples" directory contains a sample application that shows how to open a document with an external viewer using the EternalFilePlugin native plugin. + +Further explanation, plus videos showing the sample applications at: +http://www.tricedesigns.com/2012/08/15/open-with-in-ios-phonegap-apps/ + +THIS SOFTWARE IS PROVIDED BY THE ANDREW TRICE "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/iOS/ExternalFileUtil/plugin/js/ExternalFileUtil.js b/iOS/ExternalFileUtil/plugin/js/ExternalFileUtil.js new file mode 100644 index 0000000..949549f --- /dev/null +++ b/iOS/ExternalFileUtil/plugin/js/ExternalFileUtil.js @@ -0,0 +1,19 @@ +/* +THIS SOFTWARE IS PROVIDED BY ANDREW TRICE "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +window.ExternalFileUtil = { + + openWith: function ( path, uti, success, fail) { + return cordova.exec(success, fail, "ExternalFileUtil", "openWith", [path, uti]); + } +}; diff --git a/iOS/ExternalFileUtil/plugin/obj-c/CDVExternalFileUtil.h b/iOS/ExternalFileUtil/plugin/obj-c/CDVExternalFileUtil.h new file mode 100644 index 0000000..406b1cc --- /dev/null +++ b/iOS/ExternalFileUtil/plugin/obj-c/CDVExternalFileUtil.h @@ -0,0 +1,28 @@ +// +// CDVOpenWith.h +// OpenWith +// +// Created by Andrew Trice on 8/15/12. +// +// THIS SOFTWARE IS PROVIDED BY ANDREW TRICE "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +@interface CDVExternalFileUtil : CDVPlugin { + NSString *localFile; +} + +- (void) openWith:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) cleanupTempFile: (UIDocumentInteractionController *) controller; + +@end diff --git a/iOS/ExternalFileUtil/plugin/obj-c/CDVExternalFileUtil.m b/iOS/ExternalFileUtil/plugin/obj-c/CDVExternalFileUtil.m new file mode 100644 index 0000000..79dbfd6 --- /dev/null +++ b/iOS/ExternalFileUtil/plugin/obj-c/CDVExternalFileUtil.m @@ -0,0 +1,106 @@ +// +// CDVOpenWith.m +// OpenWith +// +// Created by Andrew Trice on 8/15/12. +// +// THIS SOFTWARE IS PROVIDED BY ANDREW TRICE "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#import "CDVExternalFileUtil.h" + +@implementation CDVExternalFileUtil + + +- (void) openWith:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + CDVPluginResult* pluginResult; + NSString* callbackID = [arguments pop]; + [callbackID retain]; + + NSString *path = [arguments objectAtIndex:0]; + [path retain]; + + NSString *uti = [arguments objectAtIndex:1]; + [uti retain]; + + //NSLog(@"path %@, uti:%@", path, uti); + + NSArray *parts = [path componentsSeparatedByString:@"/"]; + NSString *previewDocumentFileName = [parts lastObject]; + //NSLog(@"The file name is %@", previewDocumentFileName); + + NSData *fileRemote = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:path]]; + + // Write file to the Documents directory + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + if (!documentsDirectory) {NSLog(@"Documents directory not found!");} + localFile = [documentsDirectory stringByAppendingPathComponent:previewDocumentFileName]; + [localFile retain]; + [fileRemote writeToFile:localFile atomically:YES]; + //NSLog(@"Resource file '%@' has been written to the Documents directory from online", previewDocumentFileName); + + + // Get file again from Documents directory + NSURL *fileURL = [NSURL fileURLWithPath:localFile]; + + UIDocumentInteractionController *controller = [UIDocumentInteractionController interactionControllerWithURL:fileURL]; + [controller retain]; + controller.delegate = self; + controller.UTI = uti; + + CDVViewController* cont = (CDVViewController*)[ super viewController ]; + CGRect rect = CGRectMake(0, 0, cont.view.bounds.size.width, cont.view.bounds.size.height); + [controller presentOpenInMenuFromRect:rect inView:cont.view animated:YES]; + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @""]; + [self writeJavascript: [pluginResult toSuccessCallbackString:callbackID]]; + + [callbackID release]; + [path release]; + [uti release]; +} + +- (void) documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller { + //NSLog(@"documentInteractionControllerDidDismissOpenInMenu"); + + [self cleanupTempFile:controller]; +} + +- (void) documentInteractionController: (UIDocumentInteractionController *) controller didEndSendingToApplication: (NSString *) application { + //NSLog(@"didEndSendingToApplication: %@", application); + + [self cleanupTempFile:controller]; +} + +- (void) cleanupTempFile: (UIDocumentInteractionController *) controller +{ + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *error; + BOOL fileExists = [fileManager fileExistsAtPath:localFile]; + + //NSLog(@"Path to file: %@", localFile); + //NSLog(@"File exists: %d", fileExists); + //NSLog(@"Is deletable file at path: %d", [fileManager isDeletableFileAtPath:localFile]); + + if (fileExists) + { + BOOL success = [fileManager removeItemAtPath:localFile error:&error]; + if (!success) NSLog(@"Error: %@", [error localizedDescription]); + } + [localFile release]; + [controller release]; +} + +@end diff --git a/iOS/ExternalFileUtil/sample/pdf/README b/iOS/ExternalFileUtil/sample/pdf/README new file mode 100644 index 0000000..83bbafa --- /dev/null +++ b/iOS/ExternalFileUtil/sample/pdf/README @@ -0,0 +1 @@ +The protected PDF can be opened with the password "password". \ No newline at end of file diff --git a/iOS/ExternalFileUtil/sample/pdf/drm.pdf b/iOS/ExternalFileUtil/sample/pdf/drm.pdf new file mode 100644 index 0000000..ee30074 Binary files /dev/null and b/iOS/ExternalFileUtil/sample/pdf/drm.pdf differ diff --git a/iOS/ExternalFileUtil/sample/pdf/no_drm.pdf b/iOS/ExternalFileUtil/sample/pdf/no_drm.pdf new file mode 100644 index 0000000..9deead9 Binary files /dev/null and b/iOS/ExternalFileUtil/sample/pdf/no_drm.pdf differ diff --git a/iOS/ExternalFileUtil/sample/www/cordova-2.0.0.js b/iOS/ExternalFileUtil/sample/www/cordova-2.0.0.js new file mode 100755 index 0000000..c2caa2f --- /dev/null +++ b/iOS/ExternalFileUtil/sample/www/cordova-2.0.0.js @@ -0,0 +1,5240 @@ +// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0 + +// File generated at :: Wed Jul 18 2012 16:47:25 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + */ + fireDocumentEvent: function(type, data) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + // TODO: this is Android only; think about how to do this better + shuttingDown:false, + UsePolling:false, + // END TODO + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'); + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.guid = 1; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; +}; + +module.exports = FileUploadOptions; +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger then file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + me.successCallback(); + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param status The status code (int) + * @param msg The status message (string) + */ +Media.onStatus = function(id, msg, value) { + var media = mediaObjects[id]; + // If state update + if (msg === Media.MEDIA_STATE) { + if (value === Media.MEDIA_STOPPED) { + if (media.successCallback) { + media.successCallback(); + } + } + if (media.statusCallback) { + media.statusCallback(value); + } + } + else if (msg === Media.MEDIA_DURATION) { + media._duration = value; + } + else if (msg === Media.MEDIA_ERROR) { + if (media.errorCallback) { + // value should be a MediaError object when msg == MEDIA_ERROR + media.errorCallback(value); + } + } + else if (msg === Media.MEDIA_POSITION) { + media._position = value; + } +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. + * @constructor + */ +var MediaError = function(code, msg) { + this.code = (code !== undefined ? code : null); + this.message = msg || ""; +}; + +MediaError.MEDIA_ERR_NONE_ACTIVE = 0; +MediaError.MEDIA_ERR_ABORTED = 1; +MediaError.MEDIA_ERR_NETWORK = 2; +MediaError.MEDIA_ERR_DECODE = 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; + +module.exports = MediaError; +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +// TODO: can we axe this? +/** + * Casts a PluginResult message property (array of objects) to an array of MediaFile objects + * (used in Objective-C and Android) + * + * @param {PluginResult} pluginResult + */ +MediaFile.cast = function(pluginResult) { + var mediaFiles = []; + for (var i=0; i.dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retreived a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object who's properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/ios/plugin/ios/Contact.js +define("cordova/plugin/ios/Contact", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'); + +/** + * Provides iOS Contact.display API. + */ +module.exports = { + display : function(errorCB, options) { + /* + * Display a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + */ + + if (this.id === null) { + if (typeof errorCB === "function") { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + } + else { + exec(null, errorCB, "Contacts","displayContact", [this.id, options]); + } + } +}; +}); + +// file: lib/ios/plugin/ios/Entry.js +define("cordova/plugin/ios/Entry", function(require, exports, module) { +module.exports = { + toURL:function() { + // TODO: refactor path in a cross-platform way so we can eliminate + // these kinds of platform-specific hacks. + return "file://localhost" + this.fullPath; + }, + toURI: function() { + console.log("DEPRECATED: Update your code to use 'toURL'"); + return "file://localhost" + this.fullPath; + } +}; +}); + +// file: lib/ios/plugin/ios/FileReader.js +define("cordova/plugin/ios/FileReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + FileReader = require('cordova/plugin/FileReader'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +module.exports = { + readAsText:function(file, encoding) { + // Figure out pathing + this.fileName = ''; + if (typeof file.fullPath === 'undefined') { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + // Default encoding is UTF-8 + var enc = encoding ? encoding : "UTF-8"; + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save result + me.result = decodeURIComponent(r); + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // null result + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + "File", "readAsText", [this.fileName, enc]); + } +}; +}); + +// file: lib/ios/plugin/ios/console.js +define("cordova/plugin/ios/console", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * This class provides access to the debugging console. + * @constructor + */ +var DebugConsole = function() { + this.winConsole = window.console; + this.logLevel = DebugConsole.INFO_LEVEL; +}; + +// from most verbose, to least verbose +DebugConsole.ALL_LEVEL = 1; // same as first level +DebugConsole.INFO_LEVEL = 1; +DebugConsole.WARN_LEVEL = 2; +DebugConsole.ERROR_LEVEL = 4; +DebugConsole.NONE_LEVEL = 8; + +DebugConsole.prototype.setLevel = function(level) { + this.logLevel = level; +}; + +var stringify = function(message) { + try { + if (typeof message === "object" && JSON && JSON.stringify) { + try { + return JSON.stringify(message); + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + } else { + return message.toString(); + } + } catch (e) { + return e.toString(); + } +}; + +/** + * Print a normal log message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.log = function(message) { + if (this.logLevel <= DebugConsole.INFO_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'INFO' } ]); + } + else if (this.winConsole && this.winConsole.log) { + this.winConsole.log(message); + } +}; + +/** + * Print a warning message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.warn = function(message) { + if (this.logLevel <= DebugConsole.WARN_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'WARN' } ]); + } + else if (this.winConsole && this.winConsole.warn) { + this.winConsole.warn(message); + } +}; + +/** + * Print an error message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.error = function(message) { + if (this.logLevel <= DebugConsole.ERROR_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'ERROR' } ]); + } + else if (this.winConsole && this.winConsole.error){ + this.winConsole.error(message); + } +}; + +module.exports = new DebugConsole(); +}); + +// file: lib/ios/plugin/ios/contacts.js +define("cordova/plugin/ios/contacts", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides iOS enhanced contacts API. + */ +module.exports = { + newContactUI : function(successCallback) { + /* + * Create a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * returns: the id of the created contact as param to successCallback + */ + exec(successCallback, null, "Contacts","newContact", []); + }, + chooseContact : function(successCallback, options) { + /* + * Select a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + * + * returns: the id of the selected contact as param to successCallback + */ + exec(successCallback, null, "Contacts","chooseContact", [options]); + } +}; +}); + +// file: lib/ios/plugin/ios/nativecomm.js +define("cordova/plugin/ios/nativecomm", function(require, exports, module) { +var cordova = require('cordova'); + +/** + * Called by native code to retrieve all queued commands and clear the queue. + */ +module.exports = function() { + var json = JSON.stringify(cordova.commandQueue); + cordova.commandQueue = []; + return json; +}; +}); + +// file: lib/ios/plugin/ios/notification.js +define("cordova/plugin/ios/notification", function(require, exports, module) { +var Media = require('cordova/plugin/Media'); + +module.exports = { + beep:function(count) { + (new Media('beep.wav')).play(); + } +}; +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + + Hello Cordova + + +
+

Apache Cordovaâ„¢

+
+ +

+ Device is Ready


+ Click the button to download a pdf file from a remote server and open it in Adobe Acrobat Reader for iOS + +

+
+
+ + + + + + diff --git a/iOS/ExternalFileUtil/sample/www/js/ExternalFileUtil.js b/iOS/ExternalFileUtil/sample/www/js/ExternalFileUtil.js new file mode 100644 index 0000000..e1165da --- /dev/null +++ b/iOS/ExternalFileUtil/sample/www/js/ExternalFileUtil.js @@ -0,0 +1,7 @@ + +window.ExternalFileUtil = { + + openWith: function ( path, uti, success, fail) { + return cordova.exec(success, fail, "ExternalFileUtil", "openWith", [path, uti]); + } +}; diff --git a/iOS/ExternalFileUtil/sample/www/js/index.js b/iOS/ExternalFileUtil/sample/www/js/index.js new file mode 100755 index 0000000..970f370 --- /dev/null +++ b/iOS/ExternalFileUtil/sample/www/js/index.js @@ -0,0 +1,26 @@ + + +var app = { + initialize: function() { + this.bind(); + }, + bind: function() { + document.addEventListener('deviceready', this.deviceready, false); + }, + deviceready: function() { + // note that this is an event handler so the scope is that of the event + // so we need to call app.report(), and not this.report() + app.report('deviceready'); + + }, + report: function(id) { + console.log("report:" + id); + // hide the .pending

and show the .complete

+ document.querySelector('#' + id + ' .pending').className += ' hide'; + var completeElem = document.querySelector('#' + id + ' .complete'); + completeElem.className = completeElem.className.split('hide').join(''); + }, + openExternalDoc: function() { + ExternalFileUtil.openWith( "http://www.tricedesigns.com/temp/drm.pdf", "com.adobe.pdf" ); + } +}; diff --git a/iOS/ExternalScreen/README b/iOS/ExternalScreen/README new file mode 100644 index 0000000..4db936c --- /dev/null +++ b/iOS/ExternalScreen/README @@ -0,0 +1,36 @@ +PhoneGap iOS ExternalScreen Plugin + +THIS SOFTWARE IS PROVIDED BY THE ANDREW TRICE "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The PhoneGap iOS ExternalScreen Plugin is a native Plugin for PhoneGap/Apache Cordova which enables multi-screen application scenarios for PhoneGap projects. This plugin is only for for iOS devices. + +The PhoneGap native plugin is written in Objective C, with a JavaScript interface to integrate with the client application. + +Detail on creating PhoneGap projects for iOS at: +http://phonegap.com/start#ios-x4 + +More detail on PhoneGap Native Plugins at: +http://wiki.phonegap.com/w/page/36752779/PhoneGap%20Plugins +http://wiki.phonegap.com/w/page/36753496/How%20to%20Create%20a%20PhoneGap%20Plugin%20for%20iOS +http://wiki.phonegap.com/w/page/43708792/How%20to%20Install%20a%20PhoneGap%20Plugin%20for%20iOS" + +The ExternalScreen plugin creates a UIWebView for the the external screen, and exposes methods for interacting with the UIWebView.   Note: This is just a normal UIWebView, it does not have support for all PhoneGap libraries... just a standard HTML container. + +The "plugin" directory contains both the Objective-C native code, as well as the client-side javascript libraries. To use this code, you must create PhoneGap project for iOS, copy the PGExternalScreen.h and PGExternalScreen.m files into the native code project under the "Plugins" directory. You will need to copy the ExternalScreen.js file into the www folder for the phonegap project (or any child of that), and add references to your HTML/JS files. You will also need to add a mapping in PhoneGap.plist for the native plugin. + +The "samples" directory contains 3 sample applications: + * Basic usage of javascript functions + * FBI RSS Reader + * Fleet Manager Scenario + +Further explanation, plus videos showing the sample applications at: +http://www.tricedesigns.com/2012/01/12/multi-screen-ios-apps-with-phonegap \ No newline at end of file diff --git a/iOS/ExternalScreen/plugin/ExternalScreen.js b/iOS/ExternalScreen/plugin/ExternalScreen.js new file mode 100644 index 0000000..eff0668 --- /dev/null +++ b/iOS/ExternalScreen/plugin/ExternalScreen.js @@ -0,0 +1,24 @@ +var PGExternalScreen = { + + setupScreenConnectionNotificationHandlers: function (success, fail) { + return cordova.exec(success, fail, "PGExternalScreen", "setupScreenConnectionNotificationHandlers", []); + }, + + loadHTMLResource: function (url, success, fail) { + return cordova.exec(success, fail, "PGExternalScreen", "loadHTMLResource", [url]); + }, + + loadHTML: function (url, success, fail) { + return cordova.exec(success, fail, "PGExternalScreen", "loadHTML", [url]); + }, + + invokeJavaScript: function (scriptString, success, fail) { + return cordova.exec(success, fail, "PGExternalScreen", "invokeJavaScript", [scriptString]); + }, + + checkExternalScreenAvailable: function (success, fail) { + return cordova.exec(success, fail, "PGExternalScreen", "checkExternalScreenAvailable", []); + } + + +}; \ No newline at end of file diff --git a/iOS/ExternalScreen/plugin/PGExternalScreen.h b/iOS/ExternalScreen/plugin/PGExternalScreen.h new file mode 100644 index 0000000..592a985 --- /dev/null +++ b/iOS/ExternalScreen/plugin/PGExternalScreen.h @@ -0,0 +1,46 @@ +// +// PGExternalScreen.h +// MultiScreenPlugin +// +// Created by Andrew Trice on 1/9/12. +// +// +// THIS SOFTWARE IS PROVIDED BY THE ANDREW TRICE "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#import +#import + + +@interface PGExternalScreen : CDVPlugin { + + UIWindow* externalWindow; + UIScreen* externalScreen; + UIWebView* webView; + NSString *baseURLAddress; + NSURL *baseURL; +} + + +//Public Instance Method (visible in phonegap API) +- (void) setupScreenConnectionNotificationHandlers:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options ; +- (void) loadHTMLResource:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) loadHTML:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) invokeJavaScript:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) checkExternalScreenAvailable:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + + +//Instance Method +- (void) attemptSecondScreenView; +- (void) handleScreenConnectNotification:(NSNotification*)aNotification; +- (void) handleScreenDisconnectNotification:(NSNotification*)aNotification; +@end diff --git a/iOS/ExternalScreen/plugin/PGExternalScreen.m b/iOS/ExternalScreen/plugin/PGExternalScreen.m new file mode 100644 index 0000000..db8adf5 --- /dev/null +++ b/iOS/ExternalScreen/plugin/PGExternalScreen.m @@ -0,0 +1,241 @@ +// +// PGExternalScreen.m +// MultiScreenPlugin +// +// Created by Andrew Trice on 1/9/12. +// +// +// THIS SOFTWARE IS PROVIDED BY THE ANDREW TRICE "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL ANDREW TRICE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#import "PGExternalScreen.h" + + +@implementation PGExternalScreen + + +NSString* WEBVIEW_UNAVAILABLE = @"External Web View Unavailable"; +NSString* WEBVIEW_OK = @"OK"; +NSString* SCREEN_NOTIFICATION_HANDLERS_OK =@"External screen notification handlers initialized"; + +//used to load an HTML file in external screen web view +- (void) loadHTMLResource:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + CDVPluginResult* pluginResult; + + if (webView) + { + + NSString *stringObtainedFromJavascript = [arguments objectAtIndex:1]; + [stringObtainedFromJavascript retain]; + + NSRange textRange; + textRange =[[stringObtainedFromJavascript lowercaseString] rangeOfString:@"http://"]; + NSError *error = nil; + NSURL *url; + + //found "http://", so load remote resource + if(textRange.location != NSNotFound) + { + url = [NSURL URLWithString:stringObtainedFromJavascript]; + [url retain]; + } + //load local resource + else + { + + NSString* path = [NSString stringWithFormat:@"%@/%@", baseURLAddress, stringObtainedFromJavascript]; + [path retain]; + url = [NSURL fileURLWithPath:path isDirectory:NO]; + [url retain]; + [path release]; + } + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + [webView loadRequest:request]; + + [url release]; + [stringObtainedFromJavascript release]; + + if(error) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: [error localizedDescription]]; + [self writeJavascript: [pluginResult toErrorCallbackString:callbackId]]; + } + else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: WEBVIEW_OK]; + [self writeJavascript: [pluginResult toSuccessCallbackString:callbackId]]; + } + } + else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: WEBVIEW_UNAVAILABLE]; + [self writeJavascript: [pluginResult toErrorCallbackString:callbackId]]; + } +} + +//used to load an HTML string in external screen web view +- (void) loadHTML:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + + NSString* callbackId = [arguments objectAtIndex:0]; + CDVPluginResult* pluginResult; + + if (webView) + { + NSString *stringObtainedFromJavascript = [arguments objectAtIndex:1]; + [stringObtainedFromJavascript retain]; + [webView loadHTMLString:stringObtainedFromJavascript baseURL:baseURL]; + [stringObtainedFromJavascript release]; + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: WEBVIEW_OK]; + [self writeJavascript: [pluginResult toSuccessCallbackString:callbackId]]; + } + else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: WEBVIEW_UNAVAILABLE]; + [self writeJavascript: [pluginResult toErrorCallbackString:callbackId]]; + } + +} + + +//used to invoke javascript in external screen web view +- (void) invokeJavaScript:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + + NSString* callbackId = [arguments objectAtIndex:0]; + CDVPluginResult* pluginResult; + + if (webView) + { + NSString *stringObtainedFromJavascript = [arguments objectAtIndex:1]; + [stringObtainedFromJavascript retain]; + [webView stringByEvaluatingJavaScriptFromString: stringObtainedFromJavascript]; + [stringObtainedFromJavascript release]; + + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: WEBVIEW_OK]; + [self writeJavascript: [pluginResult toSuccessCallbackString:callbackId]]; + } + else + { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: WEBVIEW_UNAVAILABLE]; + [self writeJavascript: [pluginResult toErrorCallbackString:callbackId]]; + } + +} + +//used to initialize monitoring of external screen +- (void)setupScreenConnectionNotificationHandlers:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + [center addObserver:self selector:@selector(handleScreenConnectNotification:) + name:UIScreenDidConnectNotification object:nil]; + [center addObserver:self selector:@selector(handleScreenDisconnectNotification:) + name:UIScreenDidDisconnectNotification object:nil]; + + [self attemptSecondScreenView]; + + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: SCREEN_NOTIFICATION_HANDLERS_OK]; + + [self writeJavascript: [pluginResult toSuccessCallbackString:callbackId]]; +} + +//used to determine if an external screen is available +- (void) checkExternalScreenAvailable:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + NSString* result = nil; + if ([[UIScreen screens] count] > 1) { + result = @"YES"; + } + else + { + result = @"NO"; + } + [result retain]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: result]; + [self writeJavascript: [pluginResult toSuccessCallbackString:callbackId]]; + [result release]; +} + + + +//invoked when an additional screen is connected to iOS device (VGA or Airplay) +- (void)handleScreenConnectNotification:(NSNotification*)aNotification +{ + if (!externalWindow) + { + [self attemptSecondScreenView]; + } +} + +//invoked when an additional screen is disconnected +- (void)handleScreenDisconnectNotification:(NSNotification*)aNotification +{ + if (externalWindow) + { + externalWindow.hidden = YES; + [externalWindow release]; + externalWindow = nil; } + + if (webView) + { + [webView release]; + webView = nil; + } + +} + + +- (void) attemptSecondScreenView +{ + if ([[UIScreen screens] count] > 1) { + + // Internal display is 0, external is 1. + externalScreen = [[[UIScreen screens] objectAtIndex:1] retain]; + + CGRect screenBounds = externalScreen.bounds; + + externalWindow = [[UIWindow alloc] initWithFrame:screenBounds]; + externalWindow.screen = externalScreen; + + externalWindow.frame = screenBounds; + externalWindow.clipsToBounds = YES; + + webView = [[UIWebView alloc] initWithFrame:screenBounds]; + [webView retain]; + + baseURLAddress = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"www"]; + [baseURLAddress retain]; + + baseURL = [NSURL URLWithString:baseURLAddress]; + [baseURL retain]; + + [webView loadHTMLString:@"loading..." baseURL:baseURL]; + + [externalWindow addSubview:webView]; + [externalWindow makeKeyAndVisible]; + externalWindow.hidden = NO; + } + else + { + externalWindow.hidden = YES; + } +} + + +@end diff --git a/iOS/ExternalScreen/samples/basic usage/www/index.html b/iOS/ExternalScreen/samples/basic usage/www/index.html new file mode 100644 index 0000000..14b3bba --- /dev/null +++ b/iOS/ExternalScreen/samples/basic usage/www/index.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + +

Hey, it's PhoneGap!

+

Don't know how to get started? Check out PhoneGap Start +
+

    +
  1. Check isExternalScreenAvailable
  2. +
  3. Load local file: secondary.html
  4. +
  5. Try to load a missing file (error checking)
  6. +
  7. Load remote url: http://tricedesigns.com
  8. +
  9. Load HTML
  10. +
  11. Invoke JavaScript: document.write("hello world")
  12. +
+ + diff --git a/iOS/ExternalScreen/samples/basic usage/www/secondary.html b/iOS/ExternalScreen/samples/basic usage/www/secondary.html new file mode 100644 index 0000000..2f7b8a4 --- /dev/null +++ b/iOS/ExternalScreen/samples/basic usage/www/secondary.html @@ -0,0 +1,11 @@ +

This is a secondary experience

+ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pulvinar ullamcorper justo, quis dapibus lacus scelerisque eget. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce egestas pellentesque urna, nec auctor leo dictum sed. Fusce suscipit, tortor eget aliquam eleifend, ipsum nisl pulvinar tellus, vel lobortis leo nisi non mauris. Quisque sodales ante a dolor lobortis tincidunt. Aliquam ut purus odio. Donec mi quam, ultrices eu vehicula ac, luctus sed velit. Pellentesque turpis diam, vehicula lacinia molestie in, pharetra et augue. Duis commodo tempor egestas. Phasellus auctor augue ac neque tempus sodales. Praesent mattis, dui nec blandit convallis, mi justo eleifend magna, ut convallis neque erat sed purus. Etiam commodo rhoncus libero sit amet lacinia. Sed varius hendrerit erat, sit amet tempor sem aliquet id. Sed eu magna in nisi vestibulum aliquet. Vestibulum vel sem in purus semper congue. +

+Duis ornare, arcu vel rhoncus mattis, massa odio varius elit, nec rhoncus diam elit in diam. Ut condimentum, felis vitae viverra pulvinar, est eros fringilla tortor, vel tincidunt risus nisl in dolor. Maecenas lacinia eleifend augue, in pulvinar nibh fermentum et. Aenean id nisl sed eros viverra lobortis in non nisl. Phasellus facilisis aliquet mattis. Quisque tincidunt urna in arcu dignissim iaculis. Vivamus at placerat est. Donec egestas adipiscing diam, id malesuada dolor sodales at. +

+Aliquam ligula leo, pulvinar eget dictum ut, adipiscing at lorem. Nullam venenatis felis augue. Cras eget quam diam, a scelerisque neque. Ut ut neque neque. Proin elementum varius congue. Curabitur euismod nisl nec diam venenatis ut tempor elit aliquet. Maecenas purus tellus, viverra sit amet ultrices ac, commodo eu neque. Morbi dolor mauris, pellentesque in sollicitudin nec, tincidunt quis purus. Sed ut mattis justo. Fusce imperdiet, orci eget hendrerit commodo, lacus augue interdum nisi, rutrum bibendum dolor felis molestie mauris. Donec tristique aliquet velit sit amet ultrices. +

+Vivamus sit amet velit ligula, tempor scelerisque nibh. Quisque eros augue, ullamcorper a molestie eget, vehicula tincidunt purus. Aenean ac sem tincidunt nisi tincidunt aliquam. Etiam eget nibh ut mi tristique tincidunt. Nunc euismod mauris vitae felis congue pretium. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras nisl nisi, scelerisque vel ullamcorper et, ultricies at sem. Aenean in eros id nisi scelerisque facilisis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis at lacus sapien. Proin eget nisi tellus. Praesent laoreet lectus nec mauris euismod vel volutpat diam rutrum. Nullam sed gravida augue. Nam accumsan convallis est ac tempus. Aliquam dapibus rhoncus blandit. Sed ut leo eros. +

+Morbi quis justo in lectus adipiscing ultricies eget nec elit. Suspendisse bibendum felis vel nisl auctor aliquam. Integer tempus libero vel felis sodales eu tincidunt libero accumsan. Suspendisse ac tellus turpis. Sed sed lectus tellus, non eleifend elit. Vestibulum vitae condimentum nunc. Nulla facilisi. \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fbi rss/www/assets/activityIndicator.css b/iOS/ExternalScreen/samples/fbi rss/www/assets/activityIndicator.css new file mode 100644 index 0000000..a2684f2 --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/assets/activityIndicator.css @@ -0,0 +1,20 @@ +.activityIndicator { + /* + The original height is: + height: 304px; + width: 305px; + */ + height: 40px; + width: 40px; + -webkit-background-size: 40px 40px; + margin: 0px auto; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATEAAAEwCAYAAADfOUbNAAAgAElEQVR4nO3de3SU1bkw8Od999ySkISbBKGC/fpZdNV6rIbqkUO+5YFKQsByXGdCKwFCAwERUuBUJdVlF6v4AUuLJVxqAjECQcvg8tBTgaDYrtJDP672stpa6vIoFBCR2yTkMjPv5ftjMiCQmcxlX995fmu5RMnsvWfmzTPv3vM8ewMghJDCNNEDQM7i9/vJV77yldvy8/Onm6Y5HwCAELKhvb1960cfffSPHTt2mKLHiJwFgxiiYtGiRbcWFRWdSeZnP/vss2E//elPP2U9JpQdMIihjCxcuLBg2LBhwXQee+bMmcK1a9e20R4Tyi666AEgdT3//PMT0g1gAADDhg0LPv/88xNojgllHwxiKC0vvPDCUzk5Oa2ZtpOTk9P6wgsvPEVjTCg7EdEDQOp5/vnnJ3g8ns202tM07Vtjxow5+Jvf/OYjWm2i7IFrYiglmayB9QXXyFA6cDqJUsIqgLFuGzkXBjGUtEWLFt3qhD6Qs2AQQ0lLNg9M9j6Qs2AQQ0nx+/3cvgTi2RdSHwYxlJRRo0aNcGJfSH0YxFBSfD5fjRP7QurDIIaSQghZ6sS+kPowiCGElIZBDCGkNAxiCCGlYRBDCCkNgxhCSGku0QNQVX19fYFlWVPdbndjvJ8xDKO2s7Ozpa6u7hLPsSE5LF26dIBpmpW6rtcn+LGaU6dObd+2bRsWvqcJd7FIUUNDQ6FhGJdTfVwoFLp7yZIlf2ExJh5WrFhh8+yvrq5O2Wtz8eLFX/N4PH9O46H9V61ahUXwKcLpZArWrFmzMJ0ABgDg9Xr/vH79eru+vt5Le1xIDmVlZd5nnnnGTjOAAQBcfuqppxZSHVQWwCCWBNu2tfXr19sulyvRtCAphJDuDRs2DKAxLiSPFStWDLjnnnu6M21H1/X6Z555xrZtW9k7Ud4wiCVhw4YNFs32bNu+iHdkzlFWVua9fPnyRZptLl26lOo152QYxPqwZs0aJrf3hJCMP7WRHGjcgfUGp5bJwSCWQENDQyGNKWQ8L7744j+zahvxsWjRImbvYc/UspBV+06BQSyBdBfxk5Wbm/s73DtLXX6/n3i93t8x7obpNegEGMTiqK+vL+DRT0lJSROPfhB9I0aM4PLeLVy4kMu1qCoMYnFYljWVRz+6rs9ct27dIB59IXrq6uoGuVyumTz6ys3N5XItqgqDWByJMvFp0zTtPK++EB2WZfF8z7hdiyrCICaJ1atXjxU9BpScuro6fK8kgkFMEl6vd38gEMBFfsn5/X5iWdZ+0eNA12AQk8jZs2c3iB4DSmzkyJH4HkkGg5hECCE1uMgvr7q6ukGEEDzERDIYxCSDi/zy4ryYj5KEQUxCTU1NxaLHgK73ox/9CN8TSWEQi8MwjFpRfXd3dx8B3OtNJlrPeyKEZVnCrkUVYBCLwzTNbSL7X7NmTZnI/tE1dXV1Qt8LwzCEXouywyAWx5IlS6hurZIql8u1C1MuxOtJqdglcgwvv/yy0GtRdhjEEiCE3Cuy/88++wzrKgXjVR8Zj8fjEXoNqgCDWALz5s37o8j+sa5SLJ71kfH8+Mc/FnoNqgCDWB9s2x4ssn9MuRBHdEqFrutCrz1VYBDrw4IFCy5YlrVZ5Bgw5YK/Z599VuhrbhjG5hUrVlwQOQZVYBBLQlFRUbXI/jHlgjvNMAxhKRUAACdPnhR6zakEg1gSKioqTMMwykWOAVMu+FmyZInQ11rX9fIdO3aYIsegEgxiSfr+97+/R2T/mHLBh9/vJ263W2hKxYoVK4Rea6rBIJY82+fzjRY5AEy5YE90SoXL5RoNAFxPW1cdBrEUVFdXHxXZP6ZcsCVDSsULL7wg9BpTEQaxFGHKhXNhSoWaMIilaMGCBRdM0xS65/natWsx5YIy0SkVpmk2YkpFejCIpWHo0KHzRfav6zqmXNAlPKXixIkTQq8plWEQS0NFRYUZCoVKRI4BUy7okSClogRTKtKHQSxNS5Ys+a3I/jHlgg5JUiqEXkuqwyCWAdGL/JhykTnRKRW4mJ85DGIZEF1XiSkXmRGdUoH1kXRgEMuQ6LpKTLlIn+iUCqyPpAODWIZkqKvE08NTJ/oU70gkgvWRlGAQo0B0XSWeHp4aGU7xXr16NdZHUoJBjA7bsiyhdZV4enjyRJ/ijfWRdGEQo2ThwoVCa97w9PDkyHCKN9ZH0oVBjCLRKRe4yN830Yv5mFJBHwYxikSnXADgVtaJiK6PxJQKNjCIUSY65QK3so5LeH0kplSwgUGMMhlSLrCu8maiT/HGlAp2MIgxIDrlAusqryfDKd6YUsEOBjE2cCtriYiuj8SUCrYwiDGCW1nLQXR9JACmVLCGQYwhTLkQD1MqnA+DGEMypFw0NDTcJ7J/kX74wx8Kfe6YUsGHi3eHLS0tBd3d3VMJIXH3qbcsq9bn87VMmzbtEs+xsVBUVFT9+eefi9zu5RhEP6yybU1GM03zmMgBOCWl4oknnhjQ1dVVSQipj/czpmnWRCKR7du2bWvjOTYAjvlEDQ0NhR6P53Kqj3O5XPdOnz79jyzGxMuaNWsmulwuYd+OhUKhkkx3ol2+fPkKQshSWmNKxDTNlc8991xdJm0sXrx4rMfjEVbkret6+YoVK3aL6p+G733ve/+kadofUn0cIaR/Y2NjkMWYesNlOtnU1LQwnQAGAGAYxh+am5ttlRepRadceL3e/ZDhB1Z3dze3E54o9KWJDGAAap/iPWPGjEHV1dV2OgEMAMA0zctVVVULaY8rHqZBzLZtrbm52dZ1Pe5taLLy8vLOv/rqq68pmv8kPOXixRdffDCTxx8/fvwkrbGw7mvRokUZPddM9bzXyk3f/X4/qaqqes3tdmf8ZQghpL66utoGDrM9pkHstddes2i2p2nazI6ODmPjxo0TQbHSGtEpF7m5ub/L5PE8s80z7cvr9Wb0XDO1bNky1VIqtOnTp08sKCgwCCFU12+rq6upxoDeMAtiTU1NzG4nXS7XrubmZmvLli1KFTuLTrlYvXr1wEweb1nWUFpjYdWH3+/P6DlmSrWUisWLFxdXV1dbHo+H2Zot66klkyDW0NBQSGMK2RfTNI+otF4m+vRwQsi0TB7/7LPPfkZrLKz6uP322zN6jplQ6RTvurq6QdXV1XZbWxvzonhCSH1NTU0hq/aZBLF0F/HTlZeXd37Tpk0NKqyXiTw93OVy0Vib9NEYC6u2eXx4xqPCKd5+v5/MmjWr4dy5c1yTgE3TZBYTqAexlpaWAtptJoMQUtPR0WE0NzdLfWiGDKeHZ6K2tjbk8/mG0W7X5/MNq62tDdFulxcVTvGePXv22IKCAkPXdSE7206bNo1JbKAexLq7u6fSbjNF+2WfYoo+PTxTixcv/pRmIPP5fMMWL178Ka32RJD5FO9YyoRt20LTTtxuN5PYQD2IJcrE50n2lAzRi/yZWrx48ac0pn95eXk+1QOYrIv5NFMmaGAVGxxdOxlLyXjllVekS8mQoa4yU7W1taG6ujotnW8ULcsaWldXp6k8hQSQtj5Se/zxx5mkTMiIe+2kCF6vd1dzczMQQkbPmDFDmhwe0XWVtPR8o6j5/X4yatSoET6fr+bGEiXTNFd2d3c3Hj9+/KTsa0epkK0+csGCBcVdXV1Ct+HmLSuCWExPSgZ0dHQMXrBggfBPz4qKCnPNmjXlIusqaeoJTh8DQF3PP44m05bTM2bMGOR2u893dXWJHgp3jp5OxiPTehnPuspIJCL0vEVOuD1HGbaclm3dS4SsDGIAUpUw2QDwDR4d6bq+nUc/Ip06dYrLcySEfAPE1kcyKxVSDfVf3ubmZuUKXwFA+HrZ+vXrmb9uTz75pFRfbrDyzDPPMH8tV61aJey1VHndq6mpifrrRn1NzLKsWpFZ0+kSvV527ty5nCFDhjBb0Ghvb+/Pqm0J9QcAZhniPp8vh1Xbiai+7mWaZi2LdqlPJ23b3ka7TZ5ErZctW7asOxQKfYlF24Zh1C5dupTbJnWirVq1KmhZFpNfmGAw+KVly5Z1s2g7Hqese3V0dDCJDUxuiVWdUt7IMIzyOXPm7AGOax9r1669U9f1D2i2mS3TyBvRnlYahnHXT37yk7/RbLMP2vTp08tY7jDBE4upJACjhf2Ojo67WbTLW2zLnzfeeIPblj8LFy78m23bo2i1N3/+/Kz98mbVqlXUnvuVK1dG8QxgPLbI4ckwDGYxgdkntFPuxr6I53rZz372syGWZaW9LY1pmpW1tbVKT+1pefrpp6dpmtaS7uNN0yx66aWXztEcUzx1dXWDeO8wwQOruzAAhkGsvr7em5+fz3XtgAfTNBsLCgrmV1RUME9yDAQC5NNPP13ucrmSPqAjEom8uXfv3so9e/YoXc5DW1lZmffrX/96i6Zp/57sYwzDWHny5MnneCS0+v1+0q9fvw2idphg6cyZMz6W1yPTtZJt27YNCIfDF1n2IVDJrFmzeO1coL/00ku3eTye2YSQ5278S9M0l4fD4U0/+MEP/gEAzLcDVpz+5JNP3pabmztb1/WbXkvLspZ3dnZuWr9+PbfXcvbs2WNF7zDByi233DJw5cqVTI9eZL7g69Q7shhZSpiQemIpE6LHwQrrO7AYbt9aNTY2PuR2uw/w6o8n27Y39+vXr5rHFBOpz+/3k7y8vCanZtp3dHSM+fnPf87tsBauX70HAgHS1ta2gRDiuHk/AEAoFCqfN28e15QMpBTt8ccfL8vJyXHEN443siyr8cqVK/N5F8ULyR9at27doLy8PMfeRkcikW/U1NSkdfAocqY5c+bca1nW70WPg5VIJDJ4y5YtQpZVhCZB9uyH78gFTQCATz75JId3djeSS1VVlY8QomadUBI0TSvZtGmT0K25hWdyO32Kqev6l2bOnHla9DgQf36/f3hBQcEp0eNgQdTUsTfCg1iMk6eY4XD4rrlz5/IsV0GCVVVV3UkIoVo+JoshQ4YMlmlLbmmCWMwbb7xR3N3dreQ2I4l0dHSMWrBgwd9FjwOxN2PGjK+63e7josdBW0FBweiXX35Zmu3dY6QLYj20jRs3ljll2+aY7u7uoieeeIJL+QoSY/r06UM8Hg/zk9J5CofD5Vu3bpX2W3dZgxgARNfLrly50qRpmmPyafLy8lyYT+ZMfr+fFBQUGKLHQYtpmps7OjqqZVj3SkTqg0J6ftmr1q1b9x9OWS9rb29fDllwiEY2ysnJWS56DLSITJlIldR3YjfasmVLsWmayq+XzZo1iwDWODqNXl1dLfUdSzJycnJGr1u3Trp1r0SUCmI9lF8v6+jouH3BggUnRI8D0fPYY4+NHDBgwCeix5Eu2de9ElFxwzx7zpw5u/Py8ly2bSt5gnZOTs480WNAdPXr10/J99Q0zc1tbW2urVu37gYFAxiAmndi11E1v2zWrFnKv/bomurqauUCgErrXolIvbCfjJ5tcDSnrJchxJqK616JKB/EYnrOjNRfeeWVMq/Xq+x6GUKsdHV1lb/++utKrnslouKaWCL2vHnzlF4vQ4i22LrX66+/ruy6VyKOuRP7IifmlyGUDqeseyXiyCAWE1svk23LH8uyVooeA6IrEomsdLvdSR/owpoMW+Tw4rTpZK9mzZr127y8PJdpmo2ixwIA0NXV9YroMSC6rly5IsV7allWY1tbmytbAhiAA1IsUiVDSgZm7DuS8Ix92bbI4cXR08nexKaYorb8MU3zTcAA5kRWJBJ50+12J32uJS2ybpHDS9bdid2AewlTe3u7r7a2Fg+2daCysjLvsGHDuG1HrnKpEE1ZsSaWANcSJk3TKjGAOdeePXtCoVCoknU/TigVoinb78Suw3q9DEuNsgPLEqRsSJlIFf5S9eLVV1+9T9O0YzTbrKqq0jVNy/pPzSyhVVdXU133JITc39jY+D7NNp0Cg1h82saNG//F5XJllF9mWVZtdXX1WlqDQuqoqqpaSAipz6SNUChU0tLS8t+A08a4MIj1TWtqanpQ1/WUj2UPh8P9586dG2QxKKSGmpqaQtM0L6f6uEgk8tCWLVsOAgavPmEQS8Hq1asHFhYWTtN1Pe6nq2maNZ2dndtra2vbeI4Nya2srKygqKhoKiEkbsK1aZq1HR0d23bs2HGR59gQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIpbcXj9/uJ3+8fYRhGjcvlkuagUFYMw1jpcrkaAeBkz6niCHHh9/tJMBgcEQ6Ha7xer+N/10Kh0ErLshr3799/EgBS+l1LKog1NDTcOmDAgDNpjc5BgsHg0Dlz5nwmehzIub773e8WXbx48azocYh2/vz5YceOHfs0mZ9NGMRaWloKPB4P7kx6g7Nnz+Kxa4iqsrIyr2VZ3I57U8Xly5cLDx06lHCD0bhHtm3fvn0CBrDeDR06tHvr1q23ih4HcobKyspbMYD1rn///sGHH354QqKf6TWIBQKBpzRNa2UzLGfwer1nMJChTFVWVt76+eefZ/1STSIej6f14Ycffire3980ndy+ffsEDGDJw6klShdOIVMTDodLf/3rX++98f9fdyfW0tJSgAEsNUOHDsWLEKUFA1hqPB5Pa1lZWcGN/1+/4YdwDSwNGzduLBI9BqSWcePG4TWTBsuybopRV4NYQ0MDru+kqbCwMOu/EkepcblceM2k6f77778uVl0NYpgHlhm/309EjwEpA6+VDAwePPi6WKUDAAQCAXxRM+T3+0eIHgNSQ0lJCV4rGfriTYMOANDR0XGbuOE4g2EYNaLHgNSg6zpeKxlqb2+/GrN0AACfzzdd3HCcIRtqSREd2VALyVooFLoas3QAAELIfHHDQQih1Oi6fjVmxS07QgghFegAAKZpbhA9EIQQSpZlWVdjlg4A0N3dvVXccJzBMIyVoseA1BAKhfBayZDX670as3QAgLy8vH+IG44z9GyeiFCfLMvCayVD+fn5V2OWDgCAu5ZmbseOHSdFjwGpoWf3UpSBHTt2XI1ZVxf2L126NEzMcJzhiy8qQn3AayUD58+fvy5WXQ1ic+fOTWorWHSzYDA4VPQYkFoMw8BrJk03blt9XYpFOBwu5DscZ8B991Gq3nvvPbxm0qDr+k0x6rogVllZ2Wbbdim/Ianv7NmzPtFjQGrSdR2vnRSEw+HSPXv23LTf/k3JrlOnTt0LAE9zGZXiQqHQMNzVFaVrz549oVtuuQXXopMQDoef7m1XV4AEpx3hNtWJhUKhYdOnT8d1RJQx3Gc/sXjbUsfgkW1pwH31EW24337vkjmyDQ/PTQEenotYw8Nzo6gdnnsjv99P/H7/CMMwarJh6xnDMFb2ZOKfxIRgxJPf7yfBYHBEOByuyYate0Kh0ErLshp7EoHxdw0hhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIcZbSVjzZLhAIDPT5fNM0XauP9zOWadW0t7dvr6ysTLiRG8o6BQAwFQASHZxbCwDbAOAilxE5BAaxvmmBQODBnNyc36X6wK7Orv4VFRW4M252KwSAy2k87iEAOAgANt3hOA8Gsfi0t99++19ssPdn0oht2bWPPvroWlqDQkpZCABx79qTVAIA/w0YzOLCINaL1tbW+yJG5BjNNieVT9I1TcMLMTtoAGBRbvN+AHifcpuOgEHsCzZv3jxo4KCB51m1P3nSZHy9swOzD6vhw4cPPn369AVW7avopnMns1EgECC/+MUvXmMZwAAA3nrrrWks20dSYPoenz59+jwAvAYAhGU/Ksn2OwNt586dZcRFdvHq8OP/+RiPe3MuLwDwPHatHAD2QJavl7lED0CUXbt2FVu2dYR3vyNGjGgBAD/vfhEXLZz72wUAkJubO7qzs/Mo576lkXV3Yvv27RvU1d3FdNrYl8mTJhOgv/CLxNJB8FFj2bpeljVBLBAIEHeuZ4MLSI3oseiafnt5efkJ0eNAVI0EgE9EDwKiybTzIYvObsyKhf3W1taxObk5hgwBDAAgHA7PEz0GRJ0s72kNABgAMFb0QHhx9J0Y65SJTGC6hePIurg+GAAcPcV05MJ+IBAgXq+3SSf6TNFjQUiw8wCwGQCqwaFTTKdNJ7WdO3dOzMnNMTCAIXTVTIhOMSeCA2dfjrkT27lzZzFxEe4pEwgpJJYPORoAHJOSoXwQk3ndCyFJxT7sHbFepux0klepEAtGxFgpegyIOhXfU0eUMKkYxJRf9/J4PK+IHgOiTtX3VPn1MqUGvXPXzmJiq7/uhRn7jiQ8Y58S5dbLlLgT27x586Bfvv1L2wkBrGcqiQHMeSxQc0p5oyMAYA8fPnyQ6IEkS+o7MSfme3V1drkqKiqc8ImNbkYgOjVzCiXyy2QNYty3yOEhEo4UPfbYY+dEjwMxNQQAPhM9CMqk3vJHuiAmaosc1jqudIz6zne+83fR40BcfBUAjoseBG1f2PJHBwAP8N07LS5pgpgMW+Sw0t7Wftfjjz/+N9HjQFzdCQAfiB4EC9/85jcHHz58OJZf5gPBwUx4EJNpixwWujq7vlRRUXFa9DiQEMMB4JToQTDyn/n5+TXt7e3nIXpXBgAQhmhM4TrtFBrEWltbx0aMSEZHosns6JGjOcuWLZPilhsJ4wOALtGDYGgVACzt+bMLol9suCD6ZQCXYCYkiDm9VEjX9G+Ul5f/QfQ4kFTuBYDfix4EQ68BwA/gWhmTu+ffEdYdcw1iTp86moZZPmXKFGm/xUHCaQBQBtcKsZ3oNxCtAjgN0YNTIhCdZjLDLYjt3LnzIeIiB3j1x5NlWptDoVA15n+hJBEAaILoL7sT2QDwbwDwXxCdWuoAwOyEL+ZBrL6+3vvl//Vlx64LXbxwcfDMmTOV3wkACTEIokXYTmS7XK7/axjGMoiuk+UBwBUWHTENYm+//fYAG+yLLPsQxe1yl5SWlv6WU3f6rl27bnN5XLOJTp678S9Ny1xuhI1N5eXl/wAsaeqL/t57792Wl5c3m5BeXkvTXN7R0bFp3LhxPF/LsQDgyC+4dF1/7c4775y7ZcsWu7i42AUMvuRgFsScegdmgNkY6QzP5zF1DAQCJD8/f7nb417a909HmYb55vHjxyvxgN7r1dfXe0ePHt3icrn+PdnHRCKRladOnXqO0zIBAYANED3ow1E0TXvHtu1qiKabeKCPNTLbtnVN05L+AGEWxH759i8dt7id48sZPH78eC5Tx7feemtIQWFB+uUrNlSOHz9+G8UhKevw4cPTNE1L+2DbixcvFk2YMIFLudjw4cMHnT592nFTTF3X11mW9QwAdEI0YFP7YGASxFpbW78WMSJ/ZtG2CLqmjy4vL+e2Pcm+ffu+ChqdspVx/zpO1zTNcR8oybBtWzt69CiVKaFlWaMeeOABbmVjubm5xZ2dnU4qv7MIIXNM03wVAPpBdH2MSmIskyDmlLswESkTr+99884hrv5Uy1XGjxsvvDJDhCNHjlB93zRNu6u4uJhn+ZijUjJcLtenhmEMg2jqRRgo/V5R308sEAgMpN0mb5Zpbe7q7HJNmTJlN3AMYIFAYDjtAAYAsHfv3oW025TdoUOHqD9n27Y/eO+994bTbjdRlwCwG6JpCps59suEYRhFXq93AkTTLaid70E9iPl8vmm02+Tp4oWLg7/97W9X8c75am5u9g0cNJBJnR1xkfp33323kEXbMjp69Gihruv1LNouKCg41dzc7GPRdgImAFQNHz58MOd+adMjkcimsrIyL0Tvxq5j23ZaMwbqpx1pusbk4mHN1MzRU8qnCNuW97YRtzGtr9N07TJIUPDPg23bl1m2f/fdd3eBgNfy9OnTF3r6LYZrJxYpxbKsoUeOHInlx+lAIY1Fie2pWTINs3zypMm6yAD2zjvv3Mujn927dxfw6EckXs/x8OHDXN6zOGJ7epULHEO6XBcuXGiEaALsdR8E6X4BlbVBTNS6Vy80nehcCoPdbvdUHv2INHjwYC7PUdO036c7/aFE5fUyC6IBjMpRccofnpsOmUqF9uzZU8arL03XGgFgI6/+RNA0rZFXX4cOHSqDaCARyQSAKgD4D1CnhOnLEN3lgsqHQFYFMdMwR0+ZIm7aeKNAIEDcHrcjvj4HiD6fkSNHjghFQjVEI9dVGZi2udLr9jaeOHHipFMK5QkhuwKBgCwHv6i0XmZA9E6SyuuWFUFM1i1yCgsLm0SPgYaDBw8WmaZ5FgDAMAwg2s2zBKKRpYZhLB0+fDgcOHAACCFDH3zwQeUP1Bg5cmQTRO+EZBFbL5M2v4wQ8l+GYYTh2o6w8SSVDOvoNTGJ1r1usm/fvkHERZTeiuXDDz/0HjhwwI4FsFSYpnn2wIED9ocffnjTV+0q0XV95r59+2Q7o1Hm9TJL07T/hOh6WF9rYjYAgG3bbtu2c+P9EPUgZpmWFAWsovK9kqYps37Rq/3799967ty5jAv8z507171///5baYxJlMLCQlnfy9h6mUz5ZWYkErnU8+ek0is0TYtomtYZ7++pB7H29vbttNtMhQZayeRJkzVZFu5709raOlb0GDJx9OjRWwkhZ2i1Rwg5c/ToUaUD2bFjx2R+T2PrZSWCx2H2jOEEAOQCpR1fqQexysrKNtptJsMAs7Grs8s1adIkXnt8pSUQCBCX26Xs3lEffvihNxQKUQtgMaFQ6IzKU0vLsvYHAgEqKQMM/RaiU0xu3+DeIAgAsf0FLZC1dhIAoKuzqz+LduPJ8eUM/rdJU+ZKO3X8gsLCwg2i+jYNszbTNmhMIVm2bVlWxs8xXSNHjhT23qbABIC5gkqY1gPA3yG6i0UHyBzEKioqgrZlM7+YdE0fPXnSZI3XHl+Z6lnMF7ZmGAwGM9pf7ODBg0W0xsKqj0uXLgnbQ03X9ZoPPvhAtkX+XvWUMBEAGM2py7MAsK7nz7EdLKjkiTH7dvLRRx9dy6rtWKkQzz2+qBC8mF9RUZHRVuHpfAvJu9sQ6rQAAAfkSURBVI/S0lKh26FfuXJF1kX+3lgA8EeIxoEFjPt6HgDOAUAOXDvGTd47sZhJ5ZOoti9zykRf3vn1O8Ui++/q7Hook8fzXO/JtK9wOJzRc83U+++/L/S9TlEEor9LLQDwvzVNO8SgjyqIVoq4IboND9XfXaZBTNM0e/KkyRqNqaX0KROJabqlC82injx58sFMHj9y5MgRtMbCuq8xY8Zk9FwzZZrmEcF1leloA4CPbNv+P4SQxym1eRIAvgLRXDUC0Ux96oevcEl2ffTRR9d2edNc7LfhXtlTJvrCsz6yN0bEKIEMP/1CkRC3tTwKfdm6rgtNJ+ipq1RJbI0qZJrmGxD9FjOTvQGXQfTU848hGmdMYDR74v5p0dLSUpCfnz9VJ3rcr3lty67Vdb1l0qRJl+L9jCoCgQAZOGigIXIM48eN1yHDC+jAgQNcp+9jxozJ9NrUjhw5IvT4uo8//liWuspUeSB6vcSu2wcAoBoAZvfxOAMAfgUAqyCazhEBSnuGJcK9drInj2wjOHw3hRjR9ZGaW7sfFFs/pMTWNO1+27aPiRqAhHWVyYolobohGoQOAcBBAJgLAHcAwIief48CAB9Ej2I7DwB/g+gXBZchGriYBzCALNnpU5R9+/YNEv2NJK1DQhS8EwMA+oeFpCoYDHI75o+hWDAz4NrOEwSiN0Gx/w9wLYFVA4pHsvXF0QXgwomuj7SlqpkTIhgMCn0NJK6rTEUEot8qWhANaLGgZsK1dIkYalvsJAuDGCPvvCM2pcI0zM0OuAPI2Pjx4y9YliV0JwfFUi4SsSEatGL/GD3/xP6b2eJ9IhjE2NB0IjalIhgMVovsXyYnTpwQ+loomnKhDAxiDIhOqYiEI+WKfivGREVFhWmaptBDNRRMuVAGBjHKZNhyuqysbI/I/mX0wAMPCH1Nerayln2XCyVhEKNMdEqFpVujITtTKhLSNM0mhPAqdu5VT8oFogyDGEUybDn9yMOPqFUUz9F9990n9LWRdCtr5WEQowlTKqTXr18/TLlwGAxilEiQUtGIKRV9u+uuuy5YliVqZ1MAcFTKhRQwiNEhQ0rFfJH9q+TEiRNCXytMuaALgxgFolMqjIhRgikVyauoqDBxlwvnwCCWIRlSKkpLS6U+HEVG999/v9DXDFMu6MEgliHRKRW4mJ8+0XWVmHJBBwaxDIhOqcD6yMyIrqvElAs6MIhlQnBKBdZHZk50XSWmXGQOg1iaRJ/ijfWRdMhQVyn56eHSwyCWBhlO8cb6SHpE11Uqcnq4tDCIpUHkKd4AAJaJ9ZE0SVJXqcLp4VLCIJYi0ad4AwA88gjWR9ImQV2lMqeHywaDWKqwPtKxRKdcKHZ6uDQwiKVAgvpITKlgSHTKBQDWVaYDg1jyZKiPxJQKxkSnXGBdZeowiCVJdH0kplTwIUPKBdZVpgaDWBJkqI/ElAp+RKdcYF1lajCIJUF0fSSmVPAlScoF1lUmCYNYH0TXRwJgSoUIEqRcYF1lkjCI9QVTKrKW6JQLrKtMDgaxBH71q1/9k8j+MaVCLBlSLg4dOiT0GlQBBrEELNv6g8j+MaVCPNEpF7quC70GVYBBLI5AIDBQZP8RE1MqZCBDykVra6vQa1F2GMTiKCwsnCay/7JHMKVCFqJTLgYMGCD0WpQdBrE4iIvUi+obT/GWi+iUC13XhV2LKsAgJiE8xVs+olMuUHwYxGSDKRXSEn16OOodBjGJ4CnecpPh9HB0MwxiEsFTvOUn+vRwdDMMYpLAU7zVIMPp4eh6GMQkgad4q0P06eHoehjE4rAtm98++riYrxyedZW2zfFaVBAGsTgikch2Hv1gfaSaeNZVDhgwgMu1qCoMYnFMnDixjUc/WB+pLl51lXfccQeXa1FVGMQSsC27P8v2uzq7HsLFfHVVVFSY4XD4IZZ9aJrG9Bp0AgxiCXzrW98KmoZZy6r9yZMn/z9WbSM+xowZw+w9tCyrtri4OMiqfafAINaHCRMmrGXR7l//8lcfi3YRf/3792fyXj7wwANMrj2nwSCWhHH/Oo7q6+T1eAfW1taGaLaJxLnjjjtCXq+X6nY5xcXF+LuZJHyhkqBpmj1+3HiNxtTyr3/5q2/s2LGXaIwLyeOee+65ROOOzLKs2tGjR2uapuEuJknCQzpT9O677xZqunY51ccZEePu0tLSv7AYEw8HDhzg+ks1ZswYZa/Nw4cPf03TtD+n+jhN0/rjGljqlL1QRGtpaSkoKiqaqula3IJg0zBrI7mRlkljJyl/54VBLHV/+tOfBnR1dVUm2g/Mtu2a8+fPb+eV0uNEyl8oiA8MYkhWuCaGEFIaBjGEkNIwiCGElIZBDCGkNAxiCCGlYRBDSTFtc6UT+0LqwyCGkuJ1e7kdkMGzL6Q+DGIoKSdOnDjpxL6Q+jChECWNV8IrJrqiVOCdGEpae3v7MCf0gZwFP/FQSljfjeFdGEoV3omhlASDwUIV20bOhUEMpWTixIltpmmW0m7XNM1S3MkBpQODGEpZSUnJXtM0n6bVnmmaT5eUlOyl1R7KLrj+gNK2f//+CYSQ1kzaME2zFAMYygQGMZSR3bt3FxQWFqa1G2kwGCzEKSTKFAYxREVra+ut+fn5Z5L52fb29mGlpaWfsh4Tyg4YxBBVgUCA3HLLLbe5ve7pRsSYDwDgcrs2REKRrZ9//vk/8LBghBBC6Av+P556OTenadBoAAAAAElFTkSuQmCC"); + -webkit-animation-duration: 1s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; + -webkit-animation-name: spinnerAnim; + } + @-webkit-keyframes spinnerAnim { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } + } \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fbi rss/www/assets/styles.css b/iOS/ExternalScreen/samples/fbi rss/www/assets/styles.css new file mode 100644 index 0000000..bfe2814 --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/assets/styles.css @@ -0,0 +1,121 @@ +body,ul,li { + padding:0; + margin:0; + border:0; +} + +body { + font-size:12px; + -webkit-user-select:none; + -webkit-text-size-adjust:none; + font-family:helvetica; +} + +#header { + position:absolute; z-index:2; + top:0; left:0; + width:100%; + height:45px; + line-height:45px; + background-color:#222; + background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(0.02, #666), color-stop(1, #222)); + background-image:-moz-linear-gradient(top, #999, #666 2%, #222); + background-image:-o-linear-gradient(top, #999, #666 2%, #222); + padding:0; + color:#eee; + font-size:20px; + text-align:center; +} + +#header a { + color:#f3f3f3; + text-decoration:none; + font-weight:bold; + text-shadow:0 -1px 0 rgba(0,0,0,0.5); +} + +nav { + padding: 6px; +} +#back { + position:absolute; z-index:3; + top:15; left:15; + -webkit-border-radius: 10px; + border:1px solid black; + padding: 8px; + background-color:#222; + background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(1, #555)); + background-image:-moz-linear-gradient(top, #999, #555); + background-image:-o-linear-gradient(top, #999, #555); + color:white; + font-size: 14px; + font-weight: bold; +} + +#wrapper { + position:absolute; z-index:1; + top:45px; bottom:0px; left:0; + width:100%; + background:#fff; + overflow:auto; +} + +#scroller { + position:absolute; z-index:1; +/* -webkit-touch-callout:none;*/ + -webkit-tap-highlight-color:rgba(0,0,0,0); + width:100%; + padding:0; +} + +#scroller ul { + list-style:none; + padding:0; + margin:0; + width:100%; + text-align:left; +} + +#scroller li { + padding:0 10px; + height:40px; + line-height:40px; + border-bottom:1px solid #ccc; + border-top:1px solid #fff; + background-color:#fafafa; + font-size:14px; + vertical-align: middle; +} + +.feedLink { + width:100%; + text-decoration:none; + color:#000; +} + +.listItem { + position: relative; + height: 65px; + padding:10px; + border-top: 1; + border-bottom:1px solid #ccc; + border-top:1px solid #fff; + background-color:#fafafa; +} + +.listItem img{ + float:left; + padding-right:10px; +} + +.title { + font-size: large; +} + +.selectedRow { + font-weight: bolder; + background-color:#999; + background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #CCC), color-stop(1, #999)); + background-image:-moz-linear-gradient(top, #CCC, #999); + background-image:-o-linear-gradient(top, #CCC, #999); +} \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fbi rss/www/index.html b/iOS/ExternalScreen/samples/fbi rss/www/index.html new file mode 100644 index 0000000..fbee52d --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/index.html @@ -0,0 +1,36 @@ + + + + FBI Fugitive Feeds + + + + + + + + + + + + + + + + + +

+
+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fbi rss/www/js/application.js b/iOS/ExternalScreen/samples/fbi rss/www/js/application.js new file mode 100644 index 0000000..9521882 --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/js/application.js @@ -0,0 +1,140 @@ +/* iScroll */ + +document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false); +document.addEventListener("deviceready", onDeviceReady, false); + +/* Application */ +var feeds = [ { feed:"http://www.fbi.gov/wanted/topten/wanted-feed.xml", name:"Top Ten Fugitives" }, + { feed:"http://www.fbi.gov/wanted/wanted_terrorists/wanted-feed.xml", name:"Most Wanted Terrorists" }, + { feed:"http://www.fbi.gov/wanted/terrorinfo/wanted-feed.xml", name:"Seeking Terror Information" }, + { feed:"http://www.fbi.gov/wanted/dt/wanted-feed.xml", name:"Domestic Terrorism" }, + { feed:"http://www.fbi.gov/wanted/alert/wanted-feed.xml", name:"Crime Alerts" }, + { feed:"http://www.fbi.gov/wanted/seeking-info/wanted-feed.xml", name:"Seeking Information" }, + { feed:"http://www.fbi.gov/wanted/unknown/wanted-feed.xml", name:"Unknown Bank Robbers" }, + { feed:"http://www.fbi.gov/wanted/murders/wanted-feed.xml", name:"Murders" }, + { feed:"http://www.fbi.gov/wanted/additional/wanted-feed.xml", name:"Violent Crimes" }, + { feed:"http://www.fbi.gov/wanted/cyber/wanted-feed.xml", name:"Cyber Crimes" }, + { feed:"http://www.fbi.gov/wanted/cac/wanted-feed.xml", name:"Crimes Against Children" }, + { feed:"http://www.fbi.gov/wanted/cei/wanted-feed.xml", name:"Criminal Enterprise Investigations" }, + { feed:"http://www.fbi.gov/wanted/wcc/wanted-feed.xml??", name:"White Collar Crimes" } ]; + +function onDeviceReady() +{ + + PGExternalScreen.setupScreenConnectionNotificationHandlers( secondScreenResultHandler, secondScreenErrorHandler ); + scroller = new iScroll('wrapper'); + + var backLink = $("#back"); + backLink.hide(); + NoClickDelay( backLink.get(0) ); + + loadFeeds(); +} + +function back() { + + $("#back").hide(); + loadFeeds(); +} + +function loadFeeds() { + + var content = ""; + + $("#content").html( content ); + scroller.refresh(); + scroller.scrollTo( 0,0 ); + + $("#content").find('li').each(function() { + var div = $(this).get(0); + NoClickDelay( div ); + }); +} + +function requestFeed( event ) { + + $("#content").html( '
' ); + + var feedURL = $(event.currentTarget).attr("href"); + + $.ajax({ + url: feedURL, + success: onFeedSuccess, + error: onFeedError + }); + + return false; +} + + +function onFeedSuccess(data, textStatus, jqXHR) { + + $("#back").show(); + var html = ""; + //find each 'item' in the file and parse it + $(data).find('item').each(function() { + + //name the current found item this for this particular loop run + var $item = $(this); + var title = $item.find('title').text(); + var link = $item.find('link').text(); + var description = $item.find('description').text(); + var $description = $(description); + var img = $description.find('img').attr("src"); + var lines = description.split( "
" ); + var status = lines[1]; + var category = lines[2]; + + // now create a var 'html' to store the markup we're using to output the feed to the browser window + html += "
" + + "
" + title + "
" + + "
" + status + "
" + + "
" + category + "
" + + "
"; + + }); + + $("#content").html( html ); + scroller.refresh(); + scroller.scrollTo( 0,0 ); + + $("#content").find('div').each(function() { + var div = $(this).get(0); + NoClickDelay( div ); + }); +} + +function onFeedError(jqXHR, textStatus, errorThrown) { + + $("#content").html( errorThrown ); +} + +function showDetails( event ) { + var sourceDiv = $( event.currentTarget ); + var link = sourceDiv.attr("id"); + //alert( link ); + PGExternalScreen.loadHTMLResource( link, secondScreenResultHandler, secondScreenErrorHandler); + $("#content").find('div').each(function() { + var div = $(this); + if ( sourceDiv.get(0) == div.get(0) ) { + sourceDiv.addClass( "selectedRow" ); + } + else { + div.removeClass( "selectedRow" ); + } + }); + +} + +function secondScreenResultHandler (result) { + //alert("SUCCESS: \r\n"+result); +} + +function secondScreenErrorHandler (error) { + // alert("ERROR: \r\n"+error); +} + diff --git a/iOS/ExternalScreen/samples/fbi rss/www/js/iscroll.js b/iOS/ExternalScreen/samples/fbi rss/www/js/iscroll.js new file mode 100755 index 0000000..1613558 --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/js/iscroll.js @@ -0,0 +1,1073 @@ +/*! + * iScroll v4.1.9 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org + * Released under MIT license, http://cubiq.org/license + */ +(function(){ +var m = Math, + mround = function (r) { return r >> 0; }, + vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : + (/firefox/i).test(navigator.userAgent) ? 'Moz' : + 'opera' in window ? 'O' : '', + + // Browser capabilities + isAndroid = (/android/gi).test(navigator.appVersion), + isIDevice = (/iphone|ipad/gi).test(navigator.appVersion), + isPlaybook = (/playbook/gi).test(navigator.appVersion), + isTouchPad = (/hp-tablet/gi).test(navigator.appVersion), + + has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), + hasTouch = 'ontouchstart' in window && !isTouchPad, + hasTransform = vendor + 'Transform' in document.documentElement.style, + hasTransitionEnd = isIDevice || isPlaybook, + + nextFrame = (function() { + return window.requestAnimationFrame + || window.webkitRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.oRequestAnimationFrame + || window.msRequestAnimationFrame + || function(callback) { return setTimeout(callback, 1); } + })(), + cancelFrame = (function () { + return window.cancelRequestAnimationFrame + || window.webkitCancelAnimationFrame + || window.webkitCancelRequestAnimationFrame + || window.mozCancelRequestAnimationFrame + || window.oCancelRequestAnimationFrame + || window.msCancelRequestAnimationFrame + || clearTimeout + })(), + + // Events + RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize', + START_EV = hasTouch ? 'touchstart' : 'mousedown', + MOVE_EV = hasTouch ? 'touchmove' : 'mousemove', + END_EV = hasTouch ? 'touchend' : 'mouseup', + CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup', + WHEEL_EV = vendor == 'Moz' ? 'DOMMouseScroll' : 'mousewheel', + + // Helpers + trnOpen = 'translate' + (has3d ? '3d(' : '('), + trnClose = has3d ? ',0)' : ')', + + // Constructor + iScroll = function (el, options) { + var that = this, + doc = document, + i; + + that.wrapper = typeof el == 'object' ? el : doc.getElementById(el); + that.wrapper.style.overflow = 'hidden'; + that.scroller = that.wrapper.children[0]; + + // Default options + that.options = { + hScroll: true, + vScroll: true, + x: 0, + y: 0, + bounce: true, + bounceLock: false, + momentum: true, + lockDirection: true, + useTransform: true, + useTransition: false, + topOffset: 0, + checkDOMChanges: false, // Experimental + + // Scrollbar + hScrollbar: true, + vScrollbar: true, + fixedScrollbar: isAndroid, + hideScrollbar: isIDevice, + fadeScrollbar: isIDevice && has3d, + scrollbarClass: '', + + // Zoom + zoom: false, + zoomMin: 1, + zoomMax: 4, + doubleTapZoom: 2, + wheelAction: 'scroll', + + // Snap + snap: false, + snapThreshold: 1, + + // Events + onRefresh: null, + onBeforeScrollStart: function (e) { e.preventDefault(); }, + onScrollStart: null, + onBeforeScrollMove: null, + onScrollMove: null, + onBeforeScrollEnd: null, + onScrollEnd: null, + onTouchEnd: null, + onDestroy: null, + onZoomStart: null, + onZoom: null, + onZoomEnd: null + }; + + // User defined options + for (i in options) that.options[i] = options[i]; + + // Set starting position + that.x = that.options.x; + that.y = that.options.y; + + // Normalize options + that.options.useTransform = hasTransform ? that.options.useTransform : false; + that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar; + that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar; + that.options.zoom = that.options.useTransform && that.options.zoom; + that.options.useTransition = hasTransitionEnd && that.options.useTransition; + + // Helpers FIX ANDROID BUG! + // translate3d and scale doesn't work together! + // Ignoring 3d ONLY WHEN YOU SET that.options.zoom + if ( that.options.zoom && isAndroid ){ + trnOpen = 'translate('; + trnClose = ')'; + } + + // Set some default styles + that.scroller.style[vendor + 'TransitionProperty'] = that.options.useTransform ? '-' + vendor.toLowerCase() + '-transform' : 'top left'; + that.scroller.style[vendor + 'TransitionDuration'] = '0'; + that.scroller.style[vendor + 'TransformOrigin'] = '0 0'; + if (that.options.useTransition) that.scroller.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(0.33,0.66,0.66,1)'; + + if (that.options.useTransform) that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose; + else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; + + if (that.options.useTransition) that.options.fixedScrollbar = true; + + that.refresh(); + + that._bind(RESIZE_EV, window); + that._bind(START_EV); + if (!hasTouch) { + that._bind('mouseout', that.wrapper); + if (that.options.wheelAction != 'none') + that._bind(WHEEL_EV); + } + + if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () { + that._checkDOMChanges(); + }, 500); + }; + +// Prototype +iScroll.prototype = { + enabled: true, + x: 0, + y: 0, + steps: [], + scale: 1, + currPageX: 0, currPageY: 0, + pagesX: [], pagesY: [], + aniTime: null, + wheelZoomCount: 0, + + handleEvent: function (e) { + var that = this; + switch(e.type) { + case START_EV: + if (!hasTouch && e.button !== 0) return; + that._start(e); + break; + case MOVE_EV: that._move(e); break; + case END_EV: + case CANCEL_EV: that._end(e); break; + case RESIZE_EV: that._resize(); break; + case WHEEL_EV: that._wheel(e); break; + case 'mouseout': that._mouseout(e); break; + case 'webkitTransitionEnd': that._transitionEnd(e); break; + } + }, + + _checkDOMChanges: function () { + if (this.moved || this.zoomed || this.animating || + (this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) return; + + this.refresh(); + }, + + _scrollbar: function (dir) { + var that = this, + doc = document, + bar; + + if (!that[dir + 'Scrollbar']) { + if (that[dir + 'ScrollbarWrapper']) { + if (hasTransform) that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = ''; + that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']); + that[dir + 'ScrollbarWrapper'] = null; + that[dir + 'ScrollbarIndicator'] = null; + } + + return; + } + + if (!that[dir + 'ScrollbarWrapper']) { + // Create the scrollbar wrapper + bar = doc.createElement('div'); + + if (that.options.scrollbarClass) bar.className = that.options.scrollbarClass + dir.toUpperCase(); + else bar.style.cssText = 'position:absolute;z-index:100;' + (dir == 'h' ? 'height:7px;bottom:1px;left:2px;right:' + (that.vScrollbar ? '7' : '2') + 'px' : 'width:7px;bottom:' + (that.hScrollbar ? '7' : '2') + 'px;top:2px;right:1px'); + + bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:opacity;-' + vendor + '-transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1'); + + that.wrapper.appendChild(bar); + that[dir + 'ScrollbarWrapper'] = bar; + + // Create the scrollbar indicator + bar = doc.createElement('div'); + if (!that.options.scrollbarClass) { + bar.style.cssText = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);-' + vendor + '-background-clip:padding-box;-' + vendor + '-box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';-' + vendor + '-border-radius:3px;border-radius:3px'; + } + bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:-' + vendor + '-transform;-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);-' + vendor + '-transition-duration:0;-' + vendor + '-transform:' + trnOpen + '0,0' + trnClose; + if (that.options.useTransition) bar.style.cssText += ';-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)'; + + that[dir + 'ScrollbarWrapper'].appendChild(bar); + that[dir + 'ScrollbarIndicator'] = bar; + } + + if (dir == 'h') { + that.hScrollbarSize = that.hScrollbarWrapper.clientWidth; + that.hScrollbarIndicatorSize = m.max(mround(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8); + that.hScrollbarIndicator.style.width = that.hScrollbarIndicatorSize + 'px'; + that.hScrollbarMaxScroll = that.hScrollbarSize - that.hScrollbarIndicatorSize; + that.hScrollbarProp = that.hScrollbarMaxScroll / that.maxScrollX; + } else { + that.vScrollbarSize = that.vScrollbarWrapper.clientHeight; + that.vScrollbarIndicatorSize = m.max(mround(that.vScrollbarSize * that.vScrollbarSize / that.scrollerH), 8); + that.vScrollbarIndicator.style.height = that.vScrollbarIndicatorSize + 'px'; + that.vScrollbarMaxScroll = that.vScrollbarSize - that.vScrollbarIndicatorSize; + that.vScrollbarProp = that.vScrollbarMaxScroll / that.maxScrollY; + } + + // Reset position + that._scrollbarPos(dir, true); + }, + + _resize: function () { + var that = this; + setTimeout(function () { that.refresh(); }, isAndroid ? 200 : 0); + }, + + _pos: function (x, y) { + x = this.hScroll ? x : 0; + y = this.vScroll ? y : 0; + + if (this.options.useTransform) { + this.scroller.style[vendor + 'Transform'] = trnOpen + x + 'px,' + y + 'px' + trnClose + ' scale(' + this.scale + ')'; + } else { + x = mround(x); + y = mround(y); + this.scroller.style.left = x + 'px'; + this.scroller.style.top = y + 'px'; + } + + this.x = x; + this.y = y; + + this._scrollbarPos('h'); + this._scrollbarPos('v'); + }, + + _scrollbarPos: function (dir, hidden) { + var that = this, + pos = dir == 'h' ? that.x : that.y, + size; + + if (!that[dir + 'Scrollbar']) return; + + pos = that[dir + 'ScrollbarProp'] * pos; + + if (pos < 0) { + if (!that.options.fixedScrollbar) { + size = that[dir + 'ScrollbarIndicatorSize'] + mround(pos * 3); + if (size < 8) size = 8; + that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; + } + pos = 0; + } else if (pos > that[dir + 'ScrollbarMaxScroll']) { + if (!that.options.fixedScrollbar) { + size = that[dir + 'ScrollbarIndicatorSize'] - mround((pos - that[dir + 'ScrollbarMaxScroll']) * 3); + if (size < 8) size = 8; + that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; + pos = that[dir + 'ScrollbarMaxScroll'] + (that[dir + 'ScrollbarIndicatorSize'] - size); + } else { + pos = that[dir + 'ScrollbarMaxScroll']; + } + } + + that[dir + 'ScrollbarWrapper'].style[vendor + 'TransitionDelay'] = '0'; + that[dir + 'ScrollbarWrapper'].style.opacity = hidden && that.options.hideScrollbar ? '0' : '1'; + that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = trnOpen + (dir == 'h' ? pos + 'px,0' : '0,' + pos + 'px') + trnClose; + }, + + _start: function (e) { + var that = this, + point = hasTouch ? e.touches[0] : e, + matrix, x, y, + c1, c2; + + if (!that.enabled) return; + + if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e); + + if (that.options.useTransition || that.options.zoom) that._transitionTime(0); + + that.moved = false; + that.animating = false; + that.zoomed = false; + that.distX = 0; + that.distY = 0; + that.absDistX = 0; + that.absDistY = 0; + that.dirX = 0; + that.dirY = 0; + + // Gesture start + if (that.options.zoom && hasTouch && e.touches.length > 1) { + c1 = m.abs(e.touches[0].pageX-e.touches[1].pageX); + c2 = m.abs(e.touches[0].pageY-e.touches[1].pageY); + that.touchesDistStart = m.sqrt(c1 * c1 + c2 * c2); + + that.originX = m.abs(e.touches[0].pageX + e.touches[1].pageX - that.wrapperOffsetLeft * 2) / 2 - that.x; + that.originY = m.abs(e.touches[0].pageY + e.touches[1].pageY - that.wrapperOffsetTop * 2) / 2 - that.y; + + if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); + } + + if (that.options.momentum) { + if (that.options.useTransform) { + // Very lame general purpose alternative to CSSMatrix + matrix = getComputedStyle(that.scroller, null)[vendor + 'Transform'].replace(/[^0-9-.,]/g, '').split(','); + x = matrix[4] * 1; + y = matrix[5] * 1; + } else { + x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1; + y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1; + } + + if (x != that.x || y != that.y) { + if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + else cancelFrame(that.aniTime); + that.steps = []; + that._pos(x, y); + } + } + + that.absStartX = that.x; // Needed by snap threshold + that.absStartY = that.y; + + that.startX = that.x; + that.startY = that.y; + that.pointX = point.pageX; + that.pointY = point.pageY; + + that.startTime = e.timeStamp || Date.now(); + + if (that.options.onScrollStart) that.options.onScrollStart.call(that, e); + + that._bind(MOVE_EV); + that._bind(END_EV); + that._bind(CANCEL_EV); + }, + + _move: function (e) { + var that = this, + point = hasTouch ? e.touches[0] : e, + deltaX = point.pageX - that.pointX, + deltaY = point.pageY - that.pointY, + newX = that.x + deltaX, + newY = that.y + deltaY, + c1, c2, scale, + timestamp = e.timeStamp || Date.now(); + + if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e); + + // Zoom + if (that.options.zoom && hasTouch && e.touches.length > 1) { + c1 = m.abs(e.touches[0].pageX - e.touches[1].pageX); + c2 = m.abs(e.touches[0].pageY - e.touches[1].pageY); + that.touchesDist = m.sqrt(c1*c1+c2*c2); + + that.zoomed = true; + + scale = 1 / that.touchesDistStart * that.touchesDist * this.scale; + + if (scale < that.options.zoomMin) scale = 0.5 * that.options.zoomMin * Math.pow(2.0, scale / that.options.zoomMin); + else if (scale > that.options.zoomMax) scale = 2.0 * that.options.zoomMax * Math.pow(0.5, that.options.zoomMax / scale); + + that.lastScale = scale / this.scale; + + newX = this.originX - this.originX * that.lastScale + this.x, + newY = this.originY - this.originY * that.lastScale + this.y; + + this.scroller.style[vendor + 'Transform'] = trnOpen + newX + 'px,' + newY + 'px' + trnClose + ' scale(' + scale + ')'; + + if (that.options.onZoom) that.options.onZoom.call(that, e); + return; + } + + that.pointX = point.pageX; + that.pointY = point.pageY; + + // Slow down if outside of the boundaries + if (newX > 0 || newX < that.maxScrollX) { + newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX; + } + if (newY > that.minScrollY || newY < that.maxScrollY) { + newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= that.minScrollY || that.maxScrollY >= 0 ? that.minScrollY : that.maxScrollY; + } + + if (that.absDistX < 6 && that.absDistY < 6) { + that.distX += deltaX; + that.distY += deltaY; + that.absDistX = m.abs(that.distX); + that.absDistY = m.abs(that.distY); + + return; + } + + // Lock direction + if (that.options.lockDirection) { + if (that.absDistX > that.absDistY + 5) { + newY = that.y; + deltaY = 0; + } else if (that.absDistY > that.absDistX + 5) { + newX = that.x; + deltaX = 0; + } + } + + that.moved = true; + that._pos(newX, newY); + that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; + that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; + + if (timestamp - that.startTime > 300) { + that.startTime = timestamp; + that.startX = that.x; + that.startY = that.y; + } + + if (that.options.onScrollMove) that.options.onScrollMove.call(that, e); + }, + + _end: function (e) { + if (hasTouch && e.touches.length != 0) return; + + var that = this, + point = hasTouch ? e.changedTouches[0] : e, + target, ev, + momentumX = { dist:0, time:0 }, + momentumY = { dist:0, time:0 }, + duration = (e.timeStamp || Date.now()) - that.startTime, + newPosX = that.x, + newPosY = that.y, + distX, distY, + newDuration, + snap, + scale; + + that._unbind(MOVE_EV); + that._unbind(END_EV); + that._unbind(CANCEL_EV); + + if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e); + + if (that.zoomed) { + scale = that.scale * that.lastScale; + scale = Math.max(that.options.zoomMin, scale); + scale = Math.min(that.options.zoomMax, scale); + that.lastScale = scale / that.scale; + that.scale = scale; + + that.x = that.originX - that.originX * that.lastScale + that.x; + that.y = that.originY - that.originY * that.lastScale + that.y; + + that.scroller.style[vendor + 'TransitionDuration'] = '200ms'; + that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + that.scale + ')'; + + that.zoomed = false; + that.refresh(); + + if (that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); + return; + } + + if (!that.moved) { + if (hasTouch) { + if (that.doubleTapTimer && that.options.zoom) { + // Double tapped + clearTimeout(that.doubleTapTimer); + that.doubleTapTimer = null; + if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); + that.zoom(that.pointX, that.pointY, that.scale == 1 ? that.options.doubleTapZoom : 1); + if (that.options.onZoomEnd) { + setTimeout(function() { + that.options.onZoomEnd.call(that, e); + }, 200); // 200 is default zoom duration + } + } else { + that.doubleTapTimer = setTimeout(function () { + that.doubleTapTimer = null; + + // Find the last touched element + target = point.target; + while (target.nodeType != 1) target = target.parentNode; + + if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { + ev = document.createEvent('MouseEvents'); + ev.initMouseEvent('click', true, true, e.view, 1, + point.screenX, point.screenY, point.clientX, point.clientY, + e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, + 0, null); + ev._fake = true; + target.dispatchEvent(ev); + } + }, that.options.zoom ? 250 : 0); + } + } + + that._resetPos(200); + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + if (duration < 300 && that.options.momentum) { + momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX; + momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y - that.minScrollY : 0), that.options.bounce ? that.wrapperH : 0) : momentumY; + + newPosX = that.x + momentumX.dist; + newPosY = that.y + momentumY.dist; + + if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 }; + if ((that.y > that.minScrollY && newPosY > that.minScrollY) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 }; + } + + if (momentumX.dist || momentumY.dist) { + newDuration = m.max(m.max(momentumX.time, momentumY.time), 10); + + // Do we need to snap? + if (that.options.snap) { + distX = newPosX - that.absStartX; + distY = newPosY - that.absStartY; + if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) { that.scrollTo(that.absStartX, that.absStartY, 200); } + else { + snap = that._snap(newPosX, newPosY); + newPosX = snap.x; + newPosY = snap.y; + newDuration = m.max(snap.time, newDuration); + } + } + + that.scrollTo(mround(newPosX), mround(newPosY), newDuration); + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + // Do we need to snap? + if (that.options.snap) { + distX = newPosX - that.absStartX; + distY = newPosY - that.absStartY; + if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) that.scrollTo(that.absStartX, that.absStartY, 200); + else { + snap = that._snap(that.x, that.y); + if (snap.x != that.x || snap.y != that.y) that.scrollTo(snap.x, snap.y, snap.time); + } + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + that._resetPos(200); + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + }, + + _resetPos: function (time) { + var that = this, + resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x, + resetY = that.y >= that.minScrollY || that.maxScrollY > 0 ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; + + if (resetX == that.x && resetY == that.y) { + if (that.moved) { + that.moved = false; + if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); // Execute custom code on scroll end + } + + if (that.hScrollbar && that.options.hideScrollbar) { + if (vendor == 'webkit') that.hScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + that.hScrollbarWrapper.style.opacity = '0'; + } + if (that.vScrollbar && that.options.hideScrollbar) { + if (vendor == 'webkit') that.vScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + that.vScrollbarWrapper.style.opacity = '0'; + } + + return; + } + + that.scrollTo(resetX, resetY, time || 0); + }, + + _wheel: function (e) { + var that = this, + wheelDeltaX, wheelDeltaY, + deltaX, deltaY, + deltaScale; + + if ('wheelDeltaX' in e) { + wheelDeltaX = e.wheelDeltaX / 12; + wheelDeltaY = e.wheelDeltaY / 12; + } else if ('detail' in e) { + wheelDeltaX = wheelDeltaY = -e.detail * 3; + } else { + wheelDeltaX = wheelDeltaY = -e.wheelDelta; + } + + if (that.options.wheelAction == 'zoom') { + deltaScale = that.scale * Math.pow(2, 1/3 * (wheelDeltaY ? wheelDeltaY / Math.abs(wheelDeltaY) : 0)); + if (deltaScale < that.options.zoomMin) deltaScale = that.options.zoomMin; + if (deltaScale > that.options.zoomMax) deltaScale = that.options.zoomMax; + + if (deltaScale != that.scale) { + if (!that.wheelZoomCount && that.options.onZoomStart) that.options.onZoomStart.call(that, e); + that.wheelZoomCount++; + + that.zoom(e.pageX, e.pageY, deltaScale, 400); + + setTimeout(function() { + that.wheelZoomCount--; + if (!that.wheelZoomCount && that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); + }, 400); + } + + return; + } + + deltaX = that.x + wheelDeltaX; + deltaY = that.y + wheelDeltaY; + + if (deltaX > 0) deltaX = 0; + else if (deltaX < that.maxScrollX) deltaX = that.maxScrollX; + + if (deltaY > that.minScrollY) deltaY = that.minScrollY; + else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY; + + that.scrollTo(deltaX, deltaY, 0); + }, + + _mouseout: function (e) { + var t = e.relatedTarget; + + if (!t) { + this._end(e); + return; + } + + while (t = t.parentNode) if (t == this.wrapper) return; + + this._end(e); + }, + + _transitionEnd: function (e) { + var that = this; + + if (e.target != that.scroller) return; + + that._unbind('webkitTransitionEnd'); + + that._startAni(); + }, + + + /** + * + * Utilities + * + */ + _startAni: function () { + var that = this, + startX = that.x, startY = that.y, + startTime = Date.now(), + step, easeOut, + animate; + + if (that.animating) return; + + if (!that.steps.length) { + that._resetPos(400); + return; + } + + step = that.steps.shift(); + + if (step.x == startX && step.y == startY) step.time = 0; + + that.animating = true; + that.moved = true; + + if (that.options.useTransition) { + that._transitionTime(step.time); + that._pos(step.x, step.y); + that.animating = false; + if (step.time) that._bind('webkitTransitionEnd'); + else that._resetPos(0); + return; + } + + animate = function () { + var now = Date.now(), + newX, newY; + + if (now >= startTime + step.time) { + that._pos(step.x, step.y); + that.animating = false; + if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that); // Execute custom code on animation end + that._startAni(); + return; + } + + now = (now - startTime) / step.time - 1; + easeOut = m.sqrt(1 - now * now); + newX = (step.x - startX) * easeOut + startX; + newY = (step.y - startY) * easeOut + startY; + that._pos(newX, newY); + if (that.animating) that.aniTime = nextFrame(animate); + }; + + animate(); + }, + + _transitionTime: function (time) { + time += 'ms'; + this.scroller.style[vendor + 'TransitionDuration'] = time; + if (this.hScrollbar) this.hScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; + if (this.vScrollbar) this.vScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; + }, + + _momentum: function (dist, time, maxDistUpper, maxDistLower, size) { + var deceleration = 0.0006, + speed = m.abs(dist) / time, + newDist = (speed * speed) / (2 * deceleration), + newTime = 0, outsideDist = 0; + + // Proportinally reduce speed if we are outside of the boundaries + if (dist > 0 && newDist > maxDistUpper) { + outsideDist = size / (6 / (newDist / speed * deceleration)); + maxDistUpper = maxDistUpper + outsideDist; + speed = speed * maxDistUpper / newDist; + newDist = maxDistUpper; + } else if (dist < 0 && newDist > maxDistLower) { + outsideDist = size / (6 / (newDist / speed * deceleration)); + maxDistLower = maxDistLower + outsideDist; + speed = speed * maxDistLower / newDist; + newDist = maxDistLower; + } + + newDist = newDist * (dist < 0 ? -1 : 1); + newTime = speed / deceleration; + + return { dist: newDist, time: mround(newTime) }; + }, + + _offset: function (el) { + var left = -el.offsetLeft, + top = -el.offsetTop; + + while (el = el.offsetParent) { + left -= el.offsetLeft; + top -= el.offsetTop; + } + + if (el != this.wrapper) { + left *= this.scale; + top *= this.scale; + } + + return { left: left, top: top }; + }, + + _snap: function (x, y) { + var that = this, + i, l, + page, time, + sizeX, sizeY; + + // Check page X + page = that.pagesX.length - 1; + for (i=0, l=that.pagesX.length; i= that.pagesX[i]) { + page = i; + break; + } + } + if (page == that.currPageX && page > 0 && that.dirX < 0) page--; + x = that.pagesX[page]; + sizeX = m.abs(x - that.pagesX[that.currPageX]); + sizeX = sizeX ? m.abs(that.x - x) / sizeX * 500 : 0; + that.currPageX = page; + + // Check page Y + page = that.pagesY.length-1; + for (i=0; i= that.pagesY[i]) { + page = i; + break; + } + } + if (page == that.currPageY && page > 0 && that.dirY < 0) page--; + y = that.pagesY[page]; + sizeY = m.abs(y - that.pagesY[that.currPageY]); + sizeY = sizeY ? m.abs(that.y - y) / sizeY * 500 : 0; + that.currPageY = page; + + // Snap with constant speed (proportional duration) + time = mround(m.max(sizeX, sizeY)) || 200; + + return { x: x, y: y, time: time }; + }, + + _bind: function (type, el, bubble) { + (el || this.scroller).addEventListener(type, this, !!bubble); + }, + + _unbind: function (type, el, bubble) { + (el || this.scroller).removeEventListener(type, this, !!bubble); + }, + + + /** + * + * Public methods + * + */ + destroy: function () { + var that = this; + + that.scroller.style[vendor + 'Transform'] = ''; + + // Remove the scrollbars + that.hScrollbar = false; + that.vScrollbar = false; + that._scrollbar('h'); + that._scrollbar('v'); + + // Remove the event listeners + that._unbind(RESIZE_EV, window); + that._unbind(START_EV); + that._unbind(MOVE_EV); + that._unbind(END_EV); + that._unbind(CANCEL_EV); + + if (!that.options.hasTouch) { + that._unbind('mouseout', that.wrapper); + that._unbind(WHEEL_EV); + } + + if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + + if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime); + + if (that.options.onDestroy) that.options.onDestroy.call(that); + }, + + refresh: function () { + var that = this, + offset, + i, l, + els, + pos = 0, + page = 0; + + if (that.scale < that.options.zoomMin) that.scale = that.options.zoomMin; + that.wrapperW = that.wrapper.clientWidth || 1; + that.wrapperH = that.wrapper.clientHeight || 1; + + that.minScrollY = -that.options.topOffset || 0; + that.scrollerW = mround(that.scroller.offsetWidth * that.scale); + that.scrollerH = mround((that.scroller.offsetHeight + that.minScrollY) * that.scale); + that.maxScrollX = that.wrapperW - that.scrollerW; + that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY; + that.dirX = 0; + that.dirY = 0; + + if (that.options.onRefresh) that.options.onRefresh.call(that); + + that.hScroll = that.options.hScroll && that.maxScrollX < 0; + that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH); + + that.hScrollbar = that.hScroll && that.options.hScrollbar; + that.vScrollbar = that.vScroll && that.options.vScrollbar && that.scrollerH > that.wrapperH; + + offset = that._offset(that.wrapper); + that.wrapperOffsetLeft = -offset.left; + that.wrapperOffsetTop = -offset.top; + + // Prepare snap + if (typeof that.options.snap == 'string') { + that.pagesX = []; + that.pagesY = []; + els = that.scroller.querySelectorAll(that.options.snap); + for (i=0, l=els.length; i= that.maxScrollX) { + that.pagesX[page] = pos; + pos = pos - that.wrapperW; + page++; + } + if (that.maxScrollX%that.wrapperW) that.pagesX[that.pagesX.length] = that.maxScrollX - that.pagesX[that.pagesX.length-1] + that.pagesX[that.pagesX.length-1]; + + pos = 0; + page = 0; + that.pagesY = []; + while (pos >= that.maxScrollY) { + that.pagesY[page] = pos; + pos = pos - that.wrapperH; + page++; + } + if (that.maxScrollY%that.wrapperH) that.pagesY[that.pagesY.length] = that.maxScrollY - that.pagesY[that.pagesY.length-1] + that.pagesY[that.pagesY.length-1]; + } + + // Prepare the scrollbars + that._scrollbar('h'); + that._scrollbar('v'); + + if (!that.zoomed) { + that.scroller.style[vendor + 'TransitionDuration'] = '0'; + that._resetPos(200); + } + }, + + scrollTo: function (x, y, time, relative) { + var that = this, + step = x, + i, l; + + that.stop(); + + if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }]; + + for (i=0, l=step.length; i 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left; + pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top; + time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time; + + that.scrollTo(pos.left, pos.top, time); + }, + + scrollToPage: function (pageX, pageY, time) { + var that = this, x, y; + + time = time === undefined ? 400 : time; + + if (that.options.onScrollStart) that.options.onScrollStart.call(that); + + if (that.options.snap) { + pageX = pageX == 'next' ? that.currPageX+1 : pageX == 'prev' ? that.currPageX-1 : pageX; + pageY = pageY == 'next' ? that.currPageY+1 : pageY == 'prev' ? that.currPageY-1 : pageY; + + pageX = pageX < 0 ? 0 : pageX > that.pagesX.length-1 ? that.pagesX.length-1 : pageX; + pageY = pageY < 0 ? 0 : pageY > that.pagesY.length-1 ? that.pagesY.length-1 : pageY; + + that.currPageX = pageX; + that.currPageY = pageY; + x = that.pagesX[pageX]; + y = that.pagesY[pageY]; + } else { + x = -that.wrapperW * pageX; + y = -that.wrapperH * pageY; + if (x < that.maxScrollX) x = that.maxScrollX; + if (y < that.maxScrollY) y = that.maxScrollY; + } + + that.scrollTo(x, y, time); + }, + + disable: function () { + this.stop(); + this._resetPos(0); + this.enabled = false; + + // If disabled after touchstart we make sure that there are no left over events + this._unbind(MOVE_EV); + this._unbind(END_EV); + this._unbind(CANCEL_EV); + }, + + enable: function () { + this.enabled = true; + }, + + stop: function () { + if (this.options.useTransition) this._unbind('webkitTransitionEnd'); + else cancelFrame(this.aniTime); + this.steps = []; + this.moved = false; + this.animating = false; + }, + + zoom: function (x, y, scale, time) { + var that = this, + relScale = scale / that.scale; + + if (!that.options.useTransform) return; + + that.zoomed = true; + time = time === undefined ? 200 : time; + x = x - that.wrapperOffsetLeft - that.x; + y = y - that.wrapperOffsetTop - that.y; + that.x = x - x * relScale + that.x; + that.y = y - y * relScale + that.y; + + that.scale = scale; + that.refresh(); + + that.x = that.x > 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x; + that.y = that.y > that.minScrollY ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; + + that.scroller.style[vendor + 'TransitionDuration'] = time + 'ms'; + that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + scale + ')'; + that.zoomed = false; + }, + + isReady: function () { + return !this.moved && !this.zoomed && !this.animating; + } +}; + +if (typeof exports !== 'undefined') exports.iScroll = iScroll; +else window.iScroll = iScroll; + +})(); diff --git a/iOS/ExternalScreen/samples/fbi rss/www/js/jquery-1.7.1.min.js b/iOS/ExternalScreen/samples/fbi rss/www/js/jquery-1.7.1.min.js new file mode 100644 index 0000000..198b3ff --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/js/jquery-1.7.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fbi rss/www/js/noClickDelay.js b/iOS/ExternalScreen/samples/fbi rss/www/js/noClickDelay.js new file mode 100644 index 0000000..93fa84c --- /dev/null +++ b/iOS/ExternalScreen/samples/fbi rss/www/js/noClickDelay.js @@ -0,0 +1,47 @@ +// source from http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone + +function NoClickDelay(el) { + this.element = typeof el == 'object' ? el : document.getElementById(el); + if( window.Touch ) this.element.addEventListener('touchstart', this, false); +} + +NoClickDelay.prototype = { + handleEvent: function(e) { + switch(e.type) { + case 'touchstart': this.onTouchStart(e); break; + case 'touchmove': this.onTouchMove(e); break; + case 'touchend': this.onTouchEnd(e); break; + } + }, + + onTouchStart: function(e) { + e.preventDefault(); + this.moved = false; + + this.theTarget = document.elementFromPoint(e.targetTouches[0].clientX, e.targetTouches[0].clientY); + if(this.theTarget.nodeType == 3) this.theTarget = theTarget.parentNode; + this.theTarget.className+= ' pressed'; + + this.element.addEventListener('touchmove', this, false); + this.element.addEventListener('touchend', this, false); + }, + + onTouchMove: function(e) { + this.moved = true; + this.theTarget.className = this.theTarget.className.replace(/ ?pressed/gi, ''); + }, + + onTouchEnd: function(e) { + this.element.removeEventListener('touchmove', this, false); + this.element.removeEventListener('touchend', this, false); + + if( !this.moved && this.theTarget ) { + this.theTarget.className = this.theTarget.className.replace(/ ?pressed/gi, ''); + var theEvent = document.createEvent('MouseEvents'); + theEvent.initEvent('click', true, true); + this.theTarget.dispatchEvent(theEvent); + } + + this.theTarget = undefined; + } +}; \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/assets/activityIndicator.css b/iOS/ExternalScreen/samples/fleet manager/www/assets/activityIndicator.css new file mode 100644 index 0000000..a2684f2 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/assets/activityIndicator.css @@ -0,0 +1,20 @@ +.activityIndicator { + /* + The original height is: + height: 304px; + width: 305px; + */ + height: 40px; + width: 40px; + -webkit-background-size: 40px 40px; + margin: 0px auto; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATEAAAEwCAYAAADfOUbNAAAgAElEQVR4nO3de3SU1bkw8Od999ySkISbBKGC/fpZdNV6rIbqkUO+5YFKQsByXGdCKwFCAwERUuBUJdVlF6v4AUuLJVxqAjECQcvg8tBTgaDYrtJDP672stpa6vIoFBCR2yTkMjPv5ftjMiCQmcxlX995fmu5RMnsvWfmzTPv3vM8ewMghJDCNNEDQM7i9/vJV77yldvy8/Onm6Y5HwCAELKhvb1960cfffSPHTt2mKLHiJwFgxiiYtGiRbcWFRWdSeZnP/vss2E//elPP2U9JpQdMIihjCxcuLBg2LBhwXQee+bMmcK1a9e20R4Tyi666AEgdT3//PMT0g1gAADDhg0LPv/88xNojgllHwxiKC0vvPDCUzk5Oa2ZtpOTk9P6wgsvPEVjTCg7EdEDQOp5/vnnJ3g8ns202tM07Vtjxow5+Jvf/OYjWm2i7IFrYiglmayB9QXXyFA6cDqJUsIqgLFuGzkXBjGUtEWLFt3qhD6Qs2AQQ0lLNg9M9j6Qs2AQQ0nx+/3cvgTi2RdSHwYxlJRRo0aNcGJfSH0YxFBSfD5fjRP7QurDIIaSQghZ6sS+kPowiCGElIZBDCGkNAxiCCGlYRBDCCkNgxhCSGku0QNQVX19fYFlWVPdbndjvJ8xDKO2s7Ozpa6u7hLPsSE5LF26dIBpmpW6rtcn+LGaU6dObd+2bRsWvqcJd7FIUUNDQ6FhGJdTfVwoFLp7yZIlf2ExJh5WrFhh8+yvrq5O2Wtz8eLFX/N4PH9O46H9V61ahUXwKcLpZArWrFmzMJ0ABgDg9Xr/vH79eru+vt5Le1xIDmVlZd5nnnnGTjOAAQBcfuqppxZSHVQWwCCWBNu2tfXr19sulyvRtCAphJDuDRs2DKAxLiSPFStWDLjnnnu6M21H1/X6Z555xrZtW9k7Ud4wiCVhw4YNFs32bNu+iHdkzlFWVua9fPnyRZptLl26lOo152QYxPqwZs0aJrf3hJCMP7WRHGjcgfUGp5bJwSCWQENDQyGNKWQ8L7744j+zahvxsWjRImbvYc/UspBV+06BQSyBdBfxk5Wbm/s73DtLXX6/n3i93t8x7obpNegEGMTiqK+vL+DRT0lJSROPfhB9I0aM4PLeLVy4kMu1qCoMYnFYljWVRz+6rs9ct27dIB59IXrq6uoGuVyumTz6ys3N5XItqgqDWByJMvFp0zTtPK++EB2WZfF8z7hdiyrCICaJ1atXjxU9BpScuro6fK8kgkFMEl6vd38gEMBFfsn5/X5iWdZ+0eNA12AQk8jZs2c3iB4DSmzkyJH4HkkGg5hECCE1uMgvr7q6ukGEEDzERDIYxCSDi/zy4ryYj5KEQUxCTU1NxaLHgK73ox/9CN8TSWEQi8MwjFpRfXd3dx8B3OtNJlrPeyKEZVnCrkUVYBCLwzTNbSL7X7NmTZnI/tE1dXV1Qt8LwzCEXouywyAWx5IlS6hurZIql8u1C1MuxOtJqdglcgwvv/yy0GtRdhjEEiCE3Cuy/88++wzrKgXjVR8Zj8fjEXoNqgCDWALz5s37o8j+sa5SLJ71kfH8+Mc/FnoNqgCDWB9s2x4ssn9MuRBHdEqFrutCrz1VYBDrw4IFCy5YlrVZ5Bgw5YK/Z599VuhrbhjG5hUrVlwQOQZVYBBLQlFRUbXI/jHlgjvNMAxhKRUAACdPnhR6zakEg1gSKioqTMMwykWOAVMu+FmyZInQ11rX9fIdO3aYIsegEgxiSfr+97+/R2T/mHLBh9/vJ263W2hKxYoVK4Rea6rBIJY82+fzjRY5AEy5YE90SoXL5RoNAFxPW1cdBrEUVFdXHxXZP6ZcsCVDSsULL7wg9BpTEQaxFGHKhXNhSoWaMIilaMGCBRdM0xS65/natWsx5YIy0SkVpmk2YkpFejCIpWHo0KHzRfav6zqmXNAlPKXixIkTQq8plWEQS0NFRYUZCoVKRI4BUy7okSClogRTKtKHQSxNS5Ys+a3I/jHlgg5JUiqEXkuqwyCWAdGL/JhykTnRKRW4mJ85DGIZEF1XiSkXmRGdUoH1kXRgEMuQ6LpKTLlIn+iUCqyPpAODWIZkqKvE08NTJ/oU70gkgvWRlGAQo0B0XSWeHp4aGU7xXr16NdZHUoJBjA7bsiyhdZV4enjyRJ/ijfWRdGEQo2ThwoVCa97w9PDkyHCKN9ZH0oVBjCLRKRe4yN830Yv5mFJBHwYxikSnXADgVtaJiK6PxJQKNjCIUSY65QK3so5LeH0kplSwgUGMMhlSLrCu8maiT/HGlAp2MIgxIDrlAusqryfDKd6YUsEOBjE2cCtriYiuj8SUCrYwiDGCW1nLQXR9JACmVLCGQYwhTLkQD1MqnA+DGEMypFw0NDTcJ7J/kX74wx8Kfe6YUsGHi3eHLS0tBd3d3VMJIXH3qbcsq9bn87VMmzbtEs+xsVBUVFT9+eefi9zu5RhEP6yybU1GM03zmMgBOCWl4oknnhjQ1dVVSQipj/czpmnWRCKR7du2bWvjOTYAjvlEDQ0NhR6P53Kqj3O5XPdOnz79jyzGxMuaNWsmulwuYd+OhUKhkkx3ol2+fPkKQshSWmNKxDTNlc8991xdJm0sXrx4rMfjEVbkret6+YoVK3aL6p+G733ve/+kadofUn0cIaR/Y2NjkMWYesNlOtnU1LQwnQAGAGAYxh+am5ttlRepRadceL3e/ZDhB1Z3dze3E54o9KWJDGAAap/iPWPGjEHV1dV2OgEMAMA0zctVVVULaY8rHqZBzLZtrbm52dZ1Pe5taLLy8vLOv/rqq68pmv8kPOXixRdffDCTxx8/fvwkrbGw7mvRokUZPddM9bzXyk3f/X4/qaqqes3tdmf8ZQghpL66utoGDrM9pkHstddes2i2p2nazI6ODmPjxo0TQbHSGtEpF7m5ub/L5PE8s80z7cvr9Wb0XDO1bNky1VIqtOnTp08sKCgwCCFU12+rq6upxoDeMAtiTU1NzG4nXS7XrubmZmvLli1KFTuLTrlYvXr1wEweb1nWUFpjYdWH3+/P6DlmSrWUisWLFxdXV1dbHo+H2Zot66klkyDW0NBQSGMK2RfTNI+otF4m+vRwQsi0TB7/7LPPfkZrLKz6uP322zN6jplQ6RTvurq6QdXV1XZbWxvzonhCSH1NTU0hq/aZBLF0F/HTlZeXd37Tpk0NKqyXiTw93OVy0Vib9NEYC6u2eXx4xqPCKd5+v5/MmjWr4dy5c1yTgE3TZBYTqAexlpaWAtptJoMQUtPR0WE0NzdLfWiGDKeHZ6K2tjbk8/mG0W7X5/MNq62tDdFulxcVTvGePXv22IKCAkPXdSE7206bNo1JbKAexLq7u6fSbjNF+2WfYoo+PTxTixcv/pRmIPP5fMMWL178Ka32RJD5FO9YyoRt20LTTtxuN5PYQD2IJcrE50n2lAzRi/yZWrx48ac0pn95eXk+1QOYrIv5NFMmaGAVGxxdOxlLyXjllVekS8mQoa4yU7W1taG6ujotnW8ULcsaWldXp6k8hQSQtj5Se/zxx5mkTMiIe+2kCF6vd1dzczMQQkbPmDFDmhwe0XWVtPR8o6j5/X4yatSoET6fr+bGEiXTNFd2d3c3Hj9+/KTsa0epkK0+csGCBcVdXV1Ct+HmLSuCWExPSgZ0dHQMXrBggfBPz4qKCnPNmjXlIusqaeoJTh8DQF3PP44m05bTM2bMGOR2u893dXWJHgp3jp5OxiPTehnPuspIJCL0vEVOuD1HGbaclm3dS4SsDGIAUpUw2QDwDR4d6bq+nUc/Ip06dYrLcySEfAPE1kcyKxVSDfVf3ubmZuUKXwFA+HrZ+vXrmb9uTz75pFRfbrDyzDPPMH8tV61aJey1VHndq6mpifrrRn1NzLKsWpFZ0+kSvV527ty5nCFDhjBb0Ghvb+/Pqm0J9QcAZhniPp8vh1Xbiai+7mWaZi2LdqlPJ23b3ka7TZ5ErZctW7asOxQKfYlF24Zh1C5dupTbJnWirVq1KmhZFpNfmGAw+KVly5Z1s2g7Hqese3V0dDCJDUxuiVWdUt7IMIzyOXPm7AGOax9r1669U9f1D2i2mS3TyBvRnlYahnHXT37yk7/RbLMP2vTp08tY7jDBE4upJACjhf2Ojo67WbTLW2zLnzfeeIPblj8LFy78m23bo2i1N3/+/Kz98mbVqlXUnvuVK1dG8QxgPLbI4ckwDGYxgdkntFPuxr6I53rZz372syGWZaW9LY1pmpW1tbVKT+1pefrpp6dpmtaS7uNN0yx66aWXztEcUzx1dXWDeO8wwQOruzAAhkGsvr7em5+fz3XtgAfTNBsLCgrmV1RUME9yDAQC5NNPP13ucrmSPqAjEom8uXfv3so9e/YoXc5DW1lZmffrX/96i6Zp/57sYwzDWHny5MnneCS0+v1+0q9fvw2idphg6cyZMz6W1yPTtZJt27YNCIfDF1n2IVDJrFmzeO1coL/00ku3eTye2YSQ5278S9M0l4fD4U0/+MEP/gEAzLcDVpz+5JNP3pabmztb1/WbXkvLspZ3dnZuWr9+PbfXcvbs2WNF7zDByi233DJw5cqVTI9eZL7g69Q7shhZSpiQemIpE6LHwQrrO7AYbt9aNTY2PuR2uw/w6o8n27Y39+vXr5rHFBOpz+/3k7y8vCanZtp3dHSM+fnPf87tsBauX70HAgHS1ta2gRDiuHk/AEAoFCqfN28e15QMpBTt8ccfL8vJyXHEN443siyr8cqVK/N5F8ULyR9at27doLy8PMfeRkcikW/U1NSkdfAocqY5c+bca1nW70WPg5VIJDJ4y5YtQpZVhCZB9uyH78gFTQCATz75JId3djeSS1VVlY8QomadUBI0TSvZtGmT0K25hWdyO32Kqev6l2bOnHla9DgQf36/f3hBQcEp0eNgQdTUsTfCg1iMk6eY4XD4rrlz5/IsV0GCVVVV3UkIoVo+JoshQ4YMlmlLbmmCWMwbb7xR3N3dreQ2I4l0dHSMWrBgwd9FjwOxN2PGjK+63e7josdBW0FBweiXX35Zmu3dY6QLYj20jRs3ljll2+aY7u7uoieeeIJL+QoSY/r06UM8Hg/zk9J5CofD5Vu3bpX2W3dZgxgARNfLrly50qRpmmPyafLy8lyYT+ZMfr+fFBQUGKLHQYtpmps7OjqqZVj3SkTqg0J6ftmr1q1b9x9OWS9rb29fDllwiEY2ysnJWS56DLSITJlIldR3YjfasmVLsWmayq+XzZo1iwDWODqNXl1dLfUdSzJycnJGr1u3Trp1r0SUCmI9lF8v6+jouH3BggUnRI8D0fPYY4+NHDBgwCeix5Eu2de9ElFxwzx7zpw5u/Py8ly2bSt5gnZOTs480WNAdPXr10/J99Q0zc1tbW2urVu37gYFAxiAmndi11E1v2zWrFnKv/bomurqauUCgErrXolIvbCfjJ5tcDSnrJchxJqK616JKB/EYnrOjNRfeeWVMq/Xq+x6GUKsdHV1lb/++utKrnslouKaWCL2vHnzlF4vQ4i22LrX66+/ruy6VyKOuRP7IifmlyGUDqeseyXiyCAWE1svk23LH8uyVooeA6IrEomsdLvdSR/owpoMW+Tw4rTpZK9mzZr127y8PJdpmo2ixwIA0NXV9YroMSC6rly5IsV7allWY1tbmytbAhiAA1IsUiVDSgZm7DuS8Ix92bbI4cXR08nexKaYorb8MU3zTcAA5kRWJBJ50+12J32uJS2ybpHDS9bdid2AewlTe3u7r7a2Fg+2daCysjLvsGHDuG1HrnKpEE1ZsSaWANcSJk3TKjGAOdeePXtCoVCoknU/TigVoinb78Suw3q9DEuNsgPLEqRsSJlIFf5S9eLVV1+9T9O0YzTbrKqq0jVNy/pPzSyhVVdXU133JITc39jY+D7NNp0Cg1h82saNG//F5XJllF9mWVZtdXX1WlqDQuqoqqpaSAipz6SNUChU0tLS8t+A08a4MIj1TWtqanpQ1/WUj2UPh8P9586dG2QxKKSGmpqaQtM0L6f6uEgk8tCWLVsOAgavPmEQS8Hq1asHFhYWTtN1Pe6nq2maNZ2dndtra2vbeI4Nya2srKygqKhoKiEkbsK1aZq1HR0d23bs2HGR59gQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIpbcXj9/uJ3+8fYRhGjcvlkuagUFYMw1jpcrkaAeBkz6niCHHh9/tJMBgcEQ6Ha7xer+N/10Kh0ErLshr3799/EgBS+l1LKog1NDTcOmDAgDNpjc5BgsHg0Dlz5nwmehzIub773e8WXbx48azocYh2/vz5YceOHfs0mZ9NGMRaWloKPB4P7kx6g7Nnz+Kxa4iqsrIyr2VZ3I57U8Xly5cLDx06lHCD0bhHtm3fvn0CBrDeDR06tHvr1q23ih4HcobKyspbMYD1rn///sGHH354QqKf6TWIBQKBpzRNa2UzLGfwer1nMJChTFVWVt76+eefZ/1STSIej6f14Ycffire3980ndy+ffsEDGDJw6klShdOIVMTDodLf/3rX++98f9fdyfW0tJSgAEsNUOHDsWLEKUFA1hqPB5Pa1lZWcGN/1+/4YdwDSwNGzduLBI9BqSWcePG4TWTBsuybopRV4NYQ0MDru+kqbCwMOu/EkepcblceM2k6f77778uVl0NYpgHlhm/309EjwEpA6+VDAwePPi6WKUDAAQCAXxRM+T3+0eIHgNSQ0lJCV4rGfriTYMOANDR0XGbuOE4g2EYNaLHgNSg6zpeKxlqb2+/GrN0AACfzzdd3HCcIRtqSREd2VALyVooFLoas3QAAELIfHHDQQih1Oi6fjVmxS07QgghFegAAKZpbhA9EIQQSpZlWVdjlg4A0N3dvVXccJzBMIyVoseA1BAKhfBayZDX670as3QAgLy8vH+IG44z9GyeiFCfLMvCayVD+fn5V2OWDgCAu5ZmbseOHSdFjwGpoWf3UpSBHTt2XI1ZVxf2L126NEzMcJzhiy8qQn3AayUD58+fvy5WXQ1ic+fOTWorWHSzYDA4VPQYkFoMw8BrJk03blt9XYpFOBwu5DscZ8B991Gq3nvvPbxm0qDr+k0x6rogVllZ2Wbbdim/Ianv7NmzPtFjQGrSdR2vnRSEw+HSPXv23LTf/k3JrlOnTt0LAE9zGZXiQqHQMNzVFaVrz549oVtuuQXXopMQDoef7m1XV4AEpx3hNtWJhUKhYdOnT8d1RJQx3Gc/sXjbUsfgkW1pwH31EW24337vkjmyDQ/PTQEenotYw8Nzo6gdnnsjv99P/H7/CMMwarJh6xnDMFb2ZOKfxIRgxJPf7yfBYHBEOByuyYate0Kh0ErLshp7EoHxdw0hhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIcZbSVjzZLhAIDPT5fNM0XauP9zOWadW0t7dvr6ysTLiRG8o6BQAwFQASHZxbCwDbAOAilxE5BAaxvmmBQODBnNyc36X6wK7Orv4VFRW4M252KwSAy2k87iEAOAgANt3hOA8Gsfi0t99++19ssPdn0oht2bWPPvroWlqDQkpZCABx79qTVAIA/w0YzOLCINaL1tbW+yJG5BjNNieVT9I1TcMLMTtoAGBRbvN+AHifcpuOgEHsCzZv3jxo4KCB51m1P3nSZHy9swOzD6vhw4cPPn369AVW7avopnMns1EgECC/+MUvXmMZwAAA3nrrrWks20dSYPoenz59+jwAvAYAhGU/Ksn2OwNt586dZcRFdvHq8OP/+RiPe3MuLwDwPHatHAD2QJavl7lED0CUXbt2FVu2dYR3vyNGjGgBAD/vfhEXLZz72wUAkJubO7qzs/Mo576lkXV3Yvv27RvU1d3FdNrYl8mTJhOgv/CLxNJB8FFj2bpeljVBLBAIEHeuZ4MLSI3oseiafnt5efkJ0eNAVI0EgE9EDwKiybTzIYvObsyKhf3W1taxObk5hgwBDAAgHA7PEz0GRJ0s72kNABgAMFb0QHhx9J0Y65SJTGC6hePIurg+GAAcPcV05MJ+IBAgXq+3SSf6TNFjQUiw8wCwGQCqwaFTTKdNJ7WdO3dOzMnNMTCAIXTVTIhOMSeCA2dfjrkT27lzZzFxEe4pEwgpJJYPORoAHJOSoXwQk3ndCyFJxT7sHbFepux0klepEAtGxFgpegyIOhXfU0eUMKkYxJRf9/J4PK+IHgOiTtX3VPn1MqUGvXPXzmJiq7/uhRn7jiQ8Y58S5dbLlLgT27x586Bfvv1L2wkBrGcqiQHMeSxQc0p5oyMAYA8fPnyQ6IEkS+o7MSfme3V1drkqKiqc8ImNbkYgOjVzCiXyy2QNYty3yOEhEo4UPfbYY+dEjwMxNQQAPhM9CMqk3vJHuiAmaosc1jqudIz6zne+83fR40BcfBUAjoseBG1f2PJHBwAP8N07LS5pgpgMW+Sw0t7Wftfjjz/+N9HjQFzdCQAfiB4EC9/85jcHHz58OJZf5gPBwUx4EJNpixwWujq7vlRRUXFa9DiQEMMB4JToQTDyn/n5+TXt7e3nIXpXBgAQhmhM4TrtFBrEWltbx0aMSEZHosns6JGjOcuWLZPilhsJ4wOALtGDYGgVACzt+bMLol9suCD6ZQCXYCYkiDm9VEjX9G+Ul5f/QfQ4kFTuBYDfix4EQ68BwA/gWhmTu+ffEdYdcw1iTp86moZZPmXKFGm/xUHCaQBQBtcKsZ3oNxCtAjgN0YNTIhCdZjLDLYjt3LnzIeIiB3j1x5NlWptDoVA15n+hJBEAaILoL7sT2QDwbwDwXxCdWuoAwOyEL+ZBrL6+3vvl//Vlx64LXbxwcfDMmTOV3wkACTEIokXYTmS7XK7/axjGMoiuk+UBwBUWHTENYm+//fYAG+yLLPsQxe1yl5SWlv6WU3f6rl27bnN5XLOJTp678S9Ny1xuhI1N5eXl/wAsaeqL/t57792Wl5c3m5BeXkvTXN7R0bFp3LhxPF/LsQDgyC+4dF1/7c4775y7ZcsWu7i42AUMvuRgFsScegdmgNkY6QzP5zF1DAQCJD8/f7nb417a909HmYb55vHjxyvxgN7r1dfXe0ePHt3icrn+PdnHRCKRladOnXqO0zIBAYANED3ow1E0TXvHtu1qiKabeKCPNTLbtnVN05L+AGEWxH759i8dt7id48sZPH78eC5Tx7feemtIQWFB+uUrNlSOHz9+G8UhKevw4cPTNE1L+2DbixcvFk2YMIFLudjw4cMHnT592nFTTF3X11mW9QwAdEI0YFP7YGASxFpbW78WMSJ/ZtG2CLqmjy4vL+e2Pcm+ffu+ChqdspVx/zpO1zTNcR8oybBtWzt69CiVKaFlWaMeeOABbmVjubm5xZ2dnU4qv7MIIXNM03wVAPpBdH2MSmIskyDmlLswESkTr+99884hrv5Uy1XGjxsvvDJDhCNHjlB93zRNu6u4uJhn+ZijUjJcLtenhmEMg2jqRRgo/V5R308sEAgMpN0mb5Zpbe7q7HJNmTJlN3AMYIFAYDjtAAYAsHfv3oW025TdoUOHqD9n27Y/eO+994bTbjdRlwCwG6JpCps59suEYRhFXq93AkTTLaid70E9iPl8vmm02+Tp4oWLg7/97W9X8c75am5u9g0cNJBJnR1xkfp33323kEXbMjp69Gihruv1LNouKCg41dzc7GPRdgImAFQNHz58MOd+adMjkcimsrIyL0Tvxq5j23ZaMwbqpx1pusbk4mHN1MzRU8qnCNuW97YRtzGtr9N07TJIUPDPg23bl1m2f/fdd3eBgNfy9OnTF3r6LYZrJxYpxbKsoUeOHInlx+lAIY1Fie2pWTINs3zypMm6yAD2zjvv3Mujn927dxfw6EckXs/x8OHDXN6zOGJ7epULHEO6XBcuXGiEaALsdR8E6X4BlbVBTNS6Vy80nehcCoPdbvdUHv2INHjwYC7PUdO036c7/aFE5fUyC6IBjMpRccofnpsOmUqF9uzZU8arL03XGgFgI6/+RNA0rZFXX4cOHSqDaCARyQSAKgD4D1CnhOnLEN3lgsqHQFYFMdMwR0+ZIm7aeKNAIEDcHrcjvj4HiD6fkSNHjghFQjVEI9dVGZi2udLr9jaeOHHipFMK5QkhuwKBgCwHv6i0XmZA9E6SyuuWFUFM1i1yCgsLm0SPgYaDBw8WmaZ5FgDAMAwg2s2zBKKRpYZhLB0+fDgcOHAACCFDH3zwQeUP1Bg5cmQTRO+EZBFbL5M2v4wQ8l+GYYTh2o6w8SSVDOvoNTGJ1r1usm/fvkHERZTeiuXDDz/0HjhwwI4FsFSYpnn2wIED9ocffnjTV+0q0XV95r59+2Q7o1Hm9TJL07T/hOh6WF9rYjYAgG3bbtu2c+P9EPUgZpmWFAWsovK9kqYps37Rq/3799967ty5jAv8z507171///5baYxJlMLCQlnfy9h6mUz5ZWYkErnU8+ek0is0TYtomtYZ7++pB7H29vbttNtMhQZayeRJkzVZFu5709raOlb0GDJx9OjRWwkhZ2i1Rwg5c/ToUaUD2bFjx2R+T2PrZSWCx2H2jOEEAOQCpR1fqQexysrKNtptJsMAs7Grs8s1adIkXnt8pSUQCBCX26Xs3lEffvihNxQKUQtgMaFQ6IzKU0vLsvYHAgEqKQMM/RaiU0xu3+DeIAgAsf0FLZC1dhIAoKuzqz+LduPJ8eUM/rdJU+ZKO3X8gsLCwg2i+jYNszbTNmhMIVm2bVlWxs8xXSNHjhT23qbABIC5gkqY1gPA3yG6i0UHyBzEKioqgrZlM7+YdE0fPXnSZI3XHl+Z6lnMF7ZmGAwGM9pf7ODBg0W0xsKqj0uXLgnbQ03X9ZoPPvhAtkX+XvWUMBEAGM2py7MAsK7nz7EdLKjkiTH7dvLRRx9dy6rtWKkQzz2+qBC8mF9RUZHRVuHpfAvJu9sQ6rQAAAfkSURBVI/S0lKh26FfuXJF1kX+3lgA8EeIxoEFjPt6HgDOAUAOXDvGTd47sZhJ5ZOoti9zykRf3vn1O8Ui++/q7Hook8fzXO/JtK9wOJzRc83U+++/L/S9TlEEor9LLQDwvzVNO8SgjyqIVoq4IboND9XfXaZBTNM0e/KkyRqNqaX0KROJabqlC82injx58sFMHj9y5MgRtMbCuq8xY8Zk9FwzZZrmEcF1leloA4CPbNv+P4SQxym1eRIAvgLRXDUC0Ux96oevcEl2ffTRR9d2edNc7LfhXtlTJvrCsz6yN0bEKIEMP/1CkRC3tTwKfdm6rgtNJ+ipq1RJbI0qZJrmGxD9FjOTvQGXQfTU848hGmdMYDR74v5p0dLSUpCfnz9VJ3rcr3lty67Vdb1l0qRJl+L9jCoCgQAZOGigIXIM48eN1yHDC+jAgQNcp+9jxozJ9NrUjhw5IvT4uo8//liWuspUeSB6vcSu2wcAoBoAZvfxOAMAfgUAqyCazhEBSnuGJcK9drInj2wjOHw3hRjR9ZGaW7sfFFs/pMTWNO1+27aPiRqAhHWVyYolobohGoQOAcBBAJgLAHcAwIief48CAB9Ej2I7DwB/g+gXBZchGriYBzCALNnpU5R9+/YNEv2NJK1DQhS8EwMA+oeFpCoYDHI75o+hWDAz4NrOEwSiN0Gx/w9wLYFVA4pHsvXF0QXgwomuj7SlqpkTIhgMCn0NJK6rTEUEot8qWhANaLGgZsK1dIkYalvsJAuDGCPvvCM2pcI0zM0OuAPI2Pjx4y9YliV0JwfFUi4SsSEatGL/GD3/xP6b2eJ9IhjE2NB0IjalIhgMVovsXyYnTpwQ+loomnKhDAxiDIhOqYiEI+WKfivGREVFhWmaptBDNRRMuVAGBjHKZNhyuqysbI/I/mX0wAMPCH1Nerayln2XCyVhEKNMdEqFpVujITtTKhLSNM0mhPAqdu5VT8oFogyDGEUybDn9yMOPqFUUz9F9990n9LWRdCtr5WEQowlTKqTXr18/TLlwGAxilEiQUtGIKRV9u+uuuy5YliVqZ1MAcFTKhRQwiNEhQ0rFfJH9q+TEiRNCXytMuaALgxgFolMqjIhRgikVyauoqDBxlwvnwCCWIRlSKkpLS6U+HEVG999/v9DXDFMu6MEgliHRKRW4mJ8+0XWVmHJBBwaxDIhOqcD6yMyIrqvElAs6MIhlQnBKBdZHZk50XSWmXGQOg1iaRJ/ijfWRdMhQVyn56eHSwyCWBhlO8cb6SHpE11Uqcnq4tDCIpUHkKd4AAJaJ9ZE0SVJXqcLp4VLCIJYi0ad4AwA88gjWR9ImQV2lMqeHywaDWKqwPtKxRKdcKHZ6uDQwiKVAgvpITKlgSHTKBQDWVaYDg1jyZKiPxJQKxkSnXGBdZeowiCVJdH0kplTwIUPKBdZVpgaDWBJkqI/ElAp+RKdcYF1lajCIJUF0fSSmVPAlScoF1lUmCYNYH0TXRwJgSoUIEqRcYF1lkjCI9QVTKrKW6JQLrKtMDgaxBH71q1/9k8j+MaVCLBlSLg4dOiT0GlQBBrEELNv6g8j+MaVCPNEpF7quC70GVYBBLI5AIDBQZP8RE1MqZCBDykVra6vQa1F2GMTiKCwsnCay/7JHMKVCFqJTLgYMGCD0WpQdBrE4iIvUi+obT/GWi+iUC13XhV2LKsAgJiE8xVs+olMuUHwYxGSDKRXSEn16OOodBjGJ4CnecpPh9HB0MwxiEsFTvOUn+vRwdDMMYpLAU7zVIMPp4eh6GMQkgad4q0P06eHoehjE4rAtm98++riYrxyedZW2zfFaVBAGsTgikch2Hv1gfaSaeNZVDhgwgMu1qCoMYnFMnDixjUc/WB+pLl51lXfccQeXa1FVGMQSsC27P8v2uzq7HsLFfHVVVFSY4XD4IZZ9aJrG9Bp0AgxiCXzrW98KmoZZy6r9yZMn/z9WbSM+xowZw+w9tCyrtri4OMiqfafAINaHCRMmrGXR7l//8lcfi3YRf/3792fyXj7wwANMrj2nwSCWhHH/Oo7q6+T1eAfW1taGaLaJxLnjjjtCXq+X6nY5xcXF+LuZJHyhkqBpmj1+3HiNxtTyr3/5q2/s2LGXaIwLyeOee+65ROOOzLKs2tGjR2uapuEuJknCQzpT9O677xZqunY51ccZEePu0tLSv7AYEw8HDhzg+ks1ZswYZa/Nw4cPf03TtD+n+jhN0/rjGljqlL1QRGtpaSkoKiqaqula3IJg0zBrI7mRlkljJyl/54VBLHV/+tOfBnR1dVUm2g/Mtu2a8+fPb+eV0uNEyl8oiA8MYkhWuCaGEFIaBjGEkNIwiCGElIZBDCGkNAxiCCGlYRBDSTFtc6UT+0LqwyCGkuJ1e7kdkMGzL6Q+DGIoKSdOnDjpxL6Q+jChECWNV8IrJrqiVOCdGEpae3v7MCf0gZwFP/FQSljfjeFdGEoV3omhlASDwUIV20bOhUEMpWTixIltpmmW0m7XNM1S3MkBpQODGEpZSUnJXtM0n6bVnmmaT5eUlOyl1R7KLrj+gNK2f//+CYSQ1kzaME2zFAMYygQGMZSR3bt3FxQWFqa1G2kwGCzEKSTKFAYxREVra+ut+fn5Z5L52fb29mGlpaWfsh4Tyg4YxBBVgUCA3HLLLbe5ve7pRsSYDwDgcrs2REKRrZ9//vk/8LBghBBC6Av+P556OTenadBoAAAAAElFTkSuQmCC"); + -webkit-animation-duration: 1s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; + -webkit-animation-name: spinnerAnim; + } + @-webkit-keyframes spinnerAnim { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } + } \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/assets/airplane.png b/iOS/ExternalScreen/samples/fleet manager/www/assets/airplane.png new file mode 100644 index 0000000..d5ccaa6 Binary files /dev/null and b/iOS/ExternalScreen/samples/fleet manager/www/assets/airplane.png differ diff --git a/iOS/ExternalScreen/samples/fleet manager/www/assets/styles.css b/iOS/ExternalScreen/samples/fleet manager/www/assets/styles.css new file mode 100644 index 0000000..ceef535 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/assets/styles.css @@ -0,0 +1,153 @@ +body,ul,li { + padding:0; + margin:0; + border:0; +} + +body { + font-size:12px; + -webkit-user-select:none; + -webkit-text-size-adjust:none; + font-family:helvetica; +} + +#header { + position:absolute; z-index:2; + top:0; left:0; + width:100%; + height:45px; + line-height:45px; + background-color:#222; + background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(0.02, #666), color-stop(1, #222)); + background-image:-moz-linear-gradient(top, #999, #666 2%, #222); + background-image:-o-linear-gradient(top, #999, #666 2%, #222); + padding:0; + color:#eee; + font-size:20px; + text-align:center; +} + +#reset { + position:absolute; + top:5px; + right:5px; + color:white; + display:inline; + z-index:999; + -webkit-border-radius: 10px; + border:1px solid #666; + background:#888; + padding:8px; +} + +#header a { + color:#f3f3f3; + text-decoration:none; + font-weight:bold; + text-shadow:0 -1px 0 rgba(0,0,0,0.5); +} + +nav { + padding: 6px; +} +#back { + position:absolute; z-index:3; + top:15; left:15; + -webkit-border-radius: 10px; + border:1px solid black; + padding: 8px; + background-color:#222; + background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(1, #555)); + background-image:-moz-linear-gradient(top, #999, #555); + background-image:-o-linear-gradient(top, #999, #555); + color:white; + font-size: 14px; + font-weight: bold; +} + +#wrapper { + position:absolute; z-index:1; + top:45px; bottom:0px; left:0; + width:100%; + background:#fff; + overflow:auto; +} + +#scroller { + position:absolute; z-index:1; +/* -webkit-touch-callout:none;*/ + -webkit-tap-highlight-color:rgba(0,0,0,0); + width:100%; + padding:0; +} + +#scroller ul { + list-style:none; + padding:0; + margin:0; + width:100%; + text-align:left; +} + +#scroller li { + padding:0 10px; + height:40px; + line-height:40px; + border-bottom:1px solid #ccc; + border-top:1px solid #fff; + background-color:#fafafa; + font-size:14px; + vertical-align: middle; +} + +.feedLink { + width:100%; + text-decoration:none; + color:#000; +} + +.listItem { + position: relative; + height: 65px; + padding:10px; + border-top: 1; + border-bottom:1px solid #ccc; + border-top:1px solid #fff; + background-color:#fafafa; +} + +.listItem img{ + float:left; + padding-right:10px; +} + +.title { + font-size: large; + font-weight: bold; +} + +.selectedRow { + font-weight: bolder; + background-color:#999; + background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #CCC), color-stop(1, #999)); + background-image:-moz-linear-gradient(top, #CCC, #999); + background-image:-o-linear-gradient(top, #CCC, #999); +} + + + +.imgDiv { + position: absolute; + left:10px; + top:10px; + width: 160; + text-align: center; + vertical-align: middle; +} + +.contentDiv { + + position: absolute; + left:80px; + top:10px; +} \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/index.html b/iOS/ExternalScreen/samples/fleet manager/www/index.html new file mode 100644 index 0000000..5033ad6 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/index.html @@ -0,0 +1,36 @@ + + + + FBI Fugitive Feeds + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+Reset View + + + \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/application.js b/iOS/ExternalScreen/samples/fleet manager/www/js/application.js new file mode 100644 index 0000000..79a33d6 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/application.js @@ -0,0 +1,125 @@ +/* iScroll */ + +document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false); +document.addEventListener("deviceready", onDeviceReady, false); + +/* Application */ +var vehicles = []; + +function onDeviceReady() +{ + $("#reset").click( function(){ + PGExternalScreen.invokeJavaScript( "resetMap()" ); + }); + + var div = $("#reset").get(0); + NoClickDelay( div ); + + PGExternalScreen.setupScreenConnectionNotificationHandlers( secondScreenResultHandler, secondScreenErrorHandler ); + scroller = new iScroll('wrapper'); + + vehicles = generateVehicles(); + displayVehicles(); +} + +function generateVehicles() { + var result = []; + for ( var x=0; x<50; x++) { + result.push( { name:("Vehicle " + (x+1)), + lat:generateLatitude(), + lon:generateLongitude(), + direction:generateDirection(), + lastUpdate:generateDate(), + speed:generateSpeed() } ); + } + return result; +} + +function generateSpeed() { + var min = 300; + var max = 500; + + return (min + Math.random() * (max-min)); +} + +function generateLatitude() { + var min = -70; + var max = 70; + + return (min + Math.random() * (max-min)); +} + +function generateLongitude() { + var min = -180; + var max = 180; + + return (min + Math.random() * (max-min)); +} + +function generateDirection() { + return Math.random() * 360; +} + +function generateDate() { + return new Date(); +} + + +function displayVehicles() { + var html = ""; + for ( var x=0; x< vehicles.length; x++) { + //id=\"" + link + "\")'>" + + "
" + + "
" + + "
" + vehicles[x].name + "
" + + "
Location:" + vehicles[x].lat + ", " + vehicles[x].lon + "
" + + "
Last Update:" + vehicles[x].lastUpdate + "
" + + "
" + + "
"; + } + $("#content").html( html ); + scroller.refresh(); + + $("#content").find('div').each(function() { + var div = $(this).get(0); + NoClickDelay( div ); + }); +} + +function showDetails( event ) { + var sourceDiv = $( event.currentTarget ); + var location = sourceDiv.attr("location"); + var jsString = "gotoLocation(" + location + ")"; + //alert( jsString ); + PGExternalScreen.invokeJavaScript( jsString ); + $("#content").find('div').each(function() { + var div = $(this); + if ( sourceDiv.get(0) == div.get(0) ) { + sourceDiv.addClass( "selectedRow" ); + } + else { + div.removeClass( "selectedRow" ); + } + }); +} + +function showVehiclesOnMap() { + + for ( var x=0; x< vehicles.length; x++) { + var jsString = "addImageMarker(" + vehicles[x].lat + ", " + vehicles[x].lon + ", \"" + vehicles[x].name + "\", " + vehicles[x].direction + ")"; + //alert( jsString ); + PGExternalScreen.invokeJavaScript( jsString ); + } + +} + +function secondScreenResultHandler (result) { + PGExternalScreen.loadHTMLResource( "map.html" ); + setTimeout( showVehiclesOnMap, 500 ); + //alert("SUCCESS: \r\n"+result); +} + +function secondScreenErrorHandler (error) { + // alert("ERROR: \r\n"+error); +} diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/iscroll.js b/iOS/ExternalScreen/samples/fleet manager/www/js/iscroll.js new file mode 100755 index 0000000..1613558 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/iscroll.js @@ -0,0 +1,1073 @@ +/*! + * iScroll v4.1.9 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org + * Released under MIT license, http://cubiq.org/license + */ +(function(){ +var m = Math, + mround = function (r) { return r >> 0; }, + vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : + (/firefox/i).test(navigator.userAgent) ? 'Moz' : + 'opera' in window ? 'O' : '', + + // Browser capabilities + isAndroid = (/android/gi).test(navigator.appVersion), + isIDevice = (/iphone|ipad/gi).test(navigator.appVersion), + isPlaybook = (/playbook/gi).test(navigator.appVersion), + isTouchPad = (/hp-tablet/gi).test(navigator.appVersion), + + has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), + hasTouch = 'ontouchstart' in window && !isTouchPad, + hasTransform = vendor + 'Transform' in document.documentElement.style, + hasTransitionEnd = isIDevice || isPlaybook, + + nextFrame = (function() { + return window.requestAnimationFrame + || window.webkitRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.oRequestAnimationFrame + || window.msRequestAnimationFrame + || function(callback) { return setTimeout(callback, 1); } + })(), + cancelFrame = (function () { + return window.cancelRequestAnimationFrame + || window.webkitCancelAnimationFrame + || window.webkitCancelRequestAnimationFrame + || window.mozCancelRequestAnimationFrame + || window.oCancelRequestAnimationFrame + || window.msCancelRequestAnimationFrame + || clearTimeout + })(), + + // Events + RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize', + START_EV = hasTouch ? 'touchstart' : 'mousedown', + MOVE_EV = hasTouch ? 'touchmove' : 'mousemove', + END_EV = hasTouch ? 'touchend' : 'mouseup', + CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup', + WHEEL_EV = vendor == 'Moz' ? 'DOMMouseScroll' : 'mousewheel', + + // Helpers + trnOpen = 'translate' + (has3d ? '3d(' : '('), + trnClose = has3d ? ',0)' : ')', + + // Constructor + iScroll = function (el, options) { + var that = this, + doc = document, + i; + + that.wrapper = typeof el == 'object' ? el : doc.getElementById(el); + that.wrapper.style.overflow = 'hidden'; + that.scroller = that.wrapper.children[0]; + + // Default options + that.options = { + hScroll: true, + vScroll: true, + x: 0, + y: 0, + bounce: true, + bounceLock: false, + momentum: true, + lockDirection: true, + useTransform: true, + useTransition: false, + topOffset: 0, + checkDOMChanges: false, // Experimental + + // Scrollbar + hScrollbar: true, + vScrollbar: true, + fixedScrollbar: isAndroid, + hideScrollbar: isIDevice, + fadeScrollbar: isIDevice && has3d, + scrollbarClass: '', + + // Zoom + zoom: false, + zoomMin: 1, + zoomMax: 4, + doubleTapZoom: 2, + wheelAction: 'scroll', + + // Snap + snap: false, + snapThreshold: 1, + + // Events + onRefresh: null, + onBeforeScrollStart: function (e) { e.preventDefault(); }, + onScrollStart: null, + onBeforeScrollMove: null, + onScrollMove: null, + onBeforeScrollEnd: null, + onScrollEnd: null, + onTouchEnd: null, + onDestroy: null, + onZoomStart: null, + onZoom: null, + onZoomEnd: null + }; + + // User defined options + for (i in options) that.options[i] = options[i]; + + // Set starting position + that.x = that.options.x; + that.y = that.options.y; + + // Normalize options + that.options.useTransform = hasTransform ? that.options.useTransform : false; + that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar; + that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar; + that.options.zoom = that.options.useTransform && that.options.zoom; + that.options.useTransition = hasTransitionEnd && that.options.useTransition; + + // Helpers FIX ANDROID BUG! + // translate3d and scale doesn't work together! + // Ignoring 3d ONLY WHEN YOU SET that.options.zoom + if ( that.options.zoom && isAndroid ){ + trnOpen = 'translate('; + trnClose = ')'; + } + + // Set some default styles + that.scroller.style[vendor + 'TransitionProperty'] = that.options.useTransform ? '-' + vendor.toLowerCase() + '-transform' : 'top left'; + that.scroller.style[vendor + 'TransitionDuration'] = '0'; + that.scroller.style[vendor + 'TransformOrigin'] = '0 0'; + if (that.options.useTransition) that.scroller.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(0.33,0.66,0.66,1)'; + + if (that.options.useTransform) that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose; + else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; + + if (that.options.useTransition) that.options.fixedScrollbar = true; + + that.refresh(); + + that._bind(RESIZE_EV, window); + that._bind(START_EV); + if (!hasTouch) { + that._bind('mouseout', that.wrapper); + if (that.options.wheelAction != 'none') + that._bind(WHEEL_EV); + } + + if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () { + that._checkDOMChanges(); + }, 500); + }; + +// Prototype +iScroll.prototype = { + enabled: true, + x: 0, + y: 0, + steps: [], + scale: 1, + currPageX: 0, currPageY: 0, + pagesX: [], pagesY: [], + aniTime: null, + wheelZoomCount: 0, + + handleEvent: function (e) { + var that = this; + switch(e.type) { + case START_EV: + if (!hasTouch && e.button !== 0) return; + that._start(e); + break; + case MOVE_EV: that._move(e); break; + case END_EV: + case CANCEL_EV: that._end(e); break; + case RESIZE_EV: that._resize(); break; + case WHEEL_EV: that._wheel(e); break; + case 'mouseout': that._mouseout(e); break; + case 'webkitTransitionEnd': that._transitionEnd(e); break; + } + }, + + _checkDOMChanges: function () { + if (this.moved || this.zoomed || this.animating || + (this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) return; + + this.refresh(); + }, + + _scrollbar: function (dir) { + var that = this, + doc = document, + bar; + + if (!that[dir + 'Scrollbar']) { + if (that[dir + 'ScrollbarWrapper']) { + if (hasTransform) that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = ''; + that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']); + that[dir + 'ScrollbarWrapper'] = null; + that[dir + 'ScrollbarIndicator'] = null; + } + + return; + } + + if (!that[dir + 'ScrollbarWrapper']) { + // Create the scrollbar wrapper + bar = doc.createElement('div'); + + if (that.options.scrollbarClass) bar.className = that.options.scrollbarClass + dir.toUpperCase(); + else bar.style.cssText = 'position:absolute;z-index:100;' + (dir == 'h' ? 'height:7px;bottom:1px;left:2px;right:' + (that.vScrollbar ? '7' : '2') + 'px' : 'width:7px;bottom:' + (that.hScrollbar ? '7' : '2') + 'px;top:2px;right:1px'); + + bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:opacity;-' + vendor + '-transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1'); + + that.wrapper.appendChild(bar); + that[dir + 'ScrollbarWrapper'] = bar; + + // Create the scrollbar indicator + bar = doc.createElement('div'); + if (!that.options.scrollbarClass) { + bar.style.cssText = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);-' + vendor + '-background-clip:padding-box;-' + vendor + '-box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';-' + vendor + '-border-radius:3px;border-radius:3px'; + } + bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:-' + vendor + '-transform;-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);-' + vendor + '-transition-duration:0;-' + vendor + '-transform:' + trnOpen + '0,0' + trnClose; + if (that.options.useTransition) bar.style.cssText += ';-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)'; + + that[dir + 'ScrollbarWrapper'].appendChild(bar); + that[dir + 'ScrollbarIndicator'] = bar; + } + + if (dir == 'h') { + that.hScrollbarSize = that.hScrollbarWrapper.clientWidth; + that.hScrollbarIndicatorSize = m.max(mround(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8); + that.hScrollbarIndicator.style.width = that.hScrollbarIndicatorSize + 'px'; + that.hScrollbarMaxScroll = that.hScrollbarSize - that.hScrollbarIndicatorSize; + that.hScrollbarProp = that.hScrollbarMaxScroll / that.maxScrollX; + } else { + that.vScrollbarSize = that.vScrollbarWrapper.clientHeight; + that.vScrollbarIndicatorSize = m.max(mround(that.vScrollbarSize * that.vScrollbarSize / that.scrollerH), 8); + that.vScrollbarIndicator.style.height = that.vScrollbarIndicatorSize + 'px'; + that.vScrollbarMaxScroll = that.vScrollbarSize - that.vScrollbarIndicatorSize; + that.vScrollbarProp = that.vScrollbarMaxScroll / that.maxScrollY; + } + + // Reset position + that._scrollbarPos(dir, true); + }, + + _resize: function () { + var that = this; + setTimeout(function () { that.refresh(); }, isAndroid ? 200 : 0); + }, + + _pos: function (x, y) { + x = this.hScroll ? x : 0; + y = this.vScroll ? y : 0; + + if (this.options.useTransform) { + this.scroller.style[vendor + 'Transform'] = trnOpen + x + 'px,' + y + 'px' + trnClose + ' scale(' + this.scale + ')'; + } else { + x = mround(x); + y = mround(y); + this.scroller.style.left = x + 'px'; + this.scroller.style.top = y + 'px'; + } + + this.x = x; + this.y = y; + + this._scrollbarPos('h'); + this._scrollbarPos('v'); + }, + + _scrollbarPos: function (dir, hidden) { + var that = this, + pos = dir == 'h' ? that.x : that.y, + size; + + if (!that[dir + 'Scrollbar']) return; + + pos = that[dir + 'ScrollbarProp'] * pos; + + if (pos < 0) { + if (!that.options.fixedScrollbar) { + size = that[dir + 'ScrollbarIndicatorSize'] + mround(pos * 3); + if (size < 8) size = 8; + that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; + } + pos = 0; + } else if (pos > that[dir + 'ScrollbarMaxScroll']) { + if (!that.options.fixedScrollbar) { + size = that[dir + 'ScrollbarIndicatorSize'] - mround((pos - that[dir + 'ScrollbarMaxScroll']) * 3); + if (size < 8) size = 8; + that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; + pos = that[dir + 'ScrollbarMaxScroll'] + (that[dir + 'ScrollbarIndicatorSize'] - size); + } else { + pos = that[dir + 'ScrollbarMaxScroll']; + } + } + + that[dir + 'ScrollbarWrapper'].style[vendor + 'TransitionDelay'] = '0'; + that[dir + 'ScrollbarWrapper'].style.opacity = hidden && that.options.hideScrollbar ? '0' : '1'; + that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = trnOpen + (dir == 'h' ? pos + 'px,0' : '0,' + pos + 'px') + trnClose; + }, + + _start: function (e) { + var that = this, + point = hasTouch ? e.touches[0] : e, + matrix, x, y, + c1, c2; + + if (!that.enabled) return; + + if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e); + + if (that.options.useTransition || that.options.zoom) that._transitionTime(0); + + that.moved = false; + that.animating = false; + that.zoomed = false; + that.distX = 0; + that.distY = 0; + that.absDistX = 0; + that.absDistY = 0; + that.dirX = 0; + that.dirY = 0; + + // Gesture start + if (that.options.zoom && hasTouch && e.touches.length > 1) { + c1 = m.abs(e.touches[0].pageX-e.touches[1].pageX); + c2 = m.abs(e.touches[0].pageY-e.touches[1].pageY); + that.touchesDistStart = m.sqrt(c1 * c1 + c2 * c2); + + that.originX = m.abs(e.touches[0].pageX + e.touches[1].pageX - that.wrapperOffsetLeft * 2) / 2 - that.x; + that.originY = m.abs(e.touches[0].pageY + e.touches[1].pageY - that.wrapperOffsetTop * 2) / 2 - that.y; + + if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); + } + + if (that.options.momentum) { + if (that.options.useTransform) { + // Very lame general purpose alternative to CSSMatrix + matrix = getComputedStyle(that.scroller, null)[vendor + 'Transform'].replace(/[^0-9-.,]/g, '').split(','); + x = matrix[4] * 1; + y = matrix[5] * 1; + } else { + x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1; + y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1; + } + + if (x != that.x || y != that.y) { + if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + else cancelFrame(that.aniTime); + that.steps = []; + that._pos(x, y); + } + } + + that.absStartX = that.x; // Needed by snap threshold + that.absStartY = that.y; + + that.startX = that.x; + that.startY = that.y; + that.pointX = point.pageX; + that.pointY = point.pageY; + + that.startTime = e.timeStamp || Date.now(); + + if (that.options.onScrollStart) that.options.onScrollStart.call(that, e); + + that._bind(MOVE_EV); + that._bind(END_EV); + that._bind(CANCEL_EV); + }, + + _move: function (e) { + var that = this, + point = hasTouch ? e.touches[0] : e, + deltaX = point.pageX - that.pointX, + deltaY = point.pageY - that.pointY, + newX = that.x + deltaX, + newY = that.y + deltaY, + c1, c2, scale, + timestamp = e.timeStamp || Date.now(); + + if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e); + + // Zoom + if (that.options.zoom && hasTouch && e.touches.length > 1) { + c1 = m.abs(e.touches[0].pageX - e.touches[1].pageX); + c2 = m.abs(e.touches[0].pageY - e.touches[1].pageY); + that.touchesDist = m.sqrt(c1*c1+c2*c2); + + that.zoomed = true; + + scale = 1 / that.touchesDistStart * that.touchesDist * this.scale; + + if (scale < that.options.zoomMin) scale = 0.5 * that.options.zoomMin * Math.pow(2.0, scale / that.options.zoomMin); + else if (scale > that.options.zoomMax) scale = 2.0 * that.options.zoomMax * Math.pow(0.5, that.options.zoomMax / scale); + + that.lastScale = scale / this.scale; + + newX = this.originX - this.originX * that.lastScale + this.x, + newY = this.originY - this.originY * that.lastScale + this.y; + + this.scroller.style[vendor + 'Transform'] = trnOpen + newX + 'px,' + newY + 'px' + trnClose + ' scale(' + scale + ')'; + + if (that.options.onZoom) that.options.onZoom.call(that, e); + return; + } + + that.pointX = point.pageX; + that.pointY = point.pageY; + + // Slow down if outside of the boundaries + if (newX > 0 || newX < that.maxScrollX) { + newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX; + } + if (newY > that.minScrollY || newY < that.maxScrollY) { + newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= that.minScrollY || that.maxScrollY >= 0 ? that.minScrollY : that.maxScrollY; + } + + if (that.absDistX < 6 && that.absDistY < 6) { + that.distX += deltaX; + that.distY += deltaY; + that.absDistX = m.abs(that.distX); + that.absDistY = m.abs(that.distY); + + return; + } + + // Lock direction + if (that.options.lockDirection) { + if (that.absDistX > that.absDistY + 5) { + newY = that.y; + deltaY = 0; + } else if (that.absDistY > that.absDistX + 5) { + newX = that.x; + deltaX = 0; + } + } + + that.moved = true; + that._pos(newX, newY); + that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; + that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; + + if (timestamp - that.startTime > 300) { + that.startTime = timestamp; + that.startX = that.x; + that.startY = that.y; + } + + if (that.options.onScrollMove) that.options.onScrollMove.call(that, e); + }, + + _end: function (e) { + if (hasTouch && e.touches.length != 0) return; + + var that = this, + point = hasTouch ? e.changedTouches[0] : e, + target, ev, + momentumX = { dist:0, time:0 }, + momentumY = { dist:0, time:0 }, + duration = (e.timeStamp || Date.now()) - that.startTime, + newPosX = that.x, + newPosY = that.y, + distX, distY, + newDuration, + snap, + scale; + + that._unbind(MOVE_EV); + that._unbind(END_EV); + that._unbind(CANCEL_EV); + + if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e); + + if (that.zoomed) { + scale = that.scale * that.lastScale; + scale = Math.max(that.options.zoomMin, scale); + scale = Math.min(that.options.zoomMax, scale); + that.lastScale = scale / that.scale; + that.scale = scale; + + that.x = that.originX - that.originX * that.lastScale + that.x; + that.y = that.originY - that.originY * that.lastScale + that.y; + + that.scroller.style[vendor + 'TransitionDuration'] = '200ms'; + that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + that.scale + ')'; + + that.zoomed = false; + that.refresh(); + + if (that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); + return; + } + + if (!that.moved) { + if (hasTouch) { + if (that.doubleTapTimer && that.options.zoom) { + // Double tapped + clearTimeout(that.doubleTapTimer); + that.doubleTapTimer = null; + if (that.options.onZoomStart) that.options.onZoomStart.call(that, e); + that.zoom(that.pointX, that.pointY, that.scale == 1 ? that.options.doubleTapZoom : 1); + if (that.options.onZoomEnd) { + setTimeout(function() { + that.options.onZoomEnd.call(that, e); + }, 200); // 200 is default zoom duration + } + } else { + that.doubleTapTimer = setTimeout(function () { + that.doubleTapTimer = null; + + // Find the last touched element + target = point.target; + while (target.nodeType != 1) target = target.parentNode; + + if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { + ev = document.createEvent('MouseEvents'); + ev.initMouseEvent('click', true, true, e.view, 1, + point.screenX, point.screenY, point.clientX, point.clientY, + e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, + 0, null); + ev._fake = true; + target.dispatchEvent(ev); + } + }, that.options.zoom ? 250 : 0); + } + } + + that._resetPos(200); + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + if (duration < 300 && that.options.momentum) { + momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX; + momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y - that.minScrollY : 0), that.options.bounce ? that.wrapperH : 0) : momentumY; + + newPosX = that.x + momentumX.dist; + newPosY = that.y + momentumY.dist; + + if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 }; + if ((that.y > that.minScrollY && newPosY > that.minScrollY) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 }; + } + + if (momentumX.dist || momentumY.dist) { + newDuration = m.max(m.max(momentumX.time, momentumY.time), 10); + + // Do we need to snap? + if (that.options.snap) { + distX = newPosX - that.absStartX; + distY = newPosY - that.absStartY; + if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) { that.scrollTo(that.absStartX, that.absStartY, 200); } + else { + snap = that._snap(newPosX, newPosY); + newPosX = snap.x; + newPosY = snap.y; + newDuration = m.max(snap.time, newDuration); + } + } + + that.scrollTo(mround(newPosX), mround(newPosY), newDuration); + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + // Do we need to snap? + if (that.options.snap) { + distX = newPosX - that.absStartX; + distY = newPosY - that.absStartY; + if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) that.scrollTo(that.absStartX, that.absStartY, 200); + else { + snap = that._snap(that.x, that.y); + if (snap.x != that.x || snap.y != that.y) that.scrollTo(snap.x, snap.y, snap.time); + } + + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + return; + } + + that._resetPos(200); + if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); + }, + + _resetPos: function (time) { + var that = this, + resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x, + resetY = that.y >= that.minScrollY || that.maxScrollY > 0 ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; + + if (resetX == that.x && resetY == that.y) { + if (that.moved) { + that.moved = false; + if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); // Execute custom code on scroll end + } + + if (that.hScrollbar && that.options.hideScrollbar) { + if (vendor == 'webkit') that.hScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + that.hScrollbarWrapper.style.opacity = '0'; + } + if (that.vScrollbar && that.options.hideScrollbar) { + if (vendor == 'webkit') that.vScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + that.vScrollbarWrapper.style.opacity = '0'; + } + + return; + } + + that.scrollTo(resetX, resetY, time || 0); + }, + + _wheel: function (e) { + var that = this, + wheelDeltaX, wheelDeltaY, + deltaX, deltaY, + deltaScale; + + if ('wheelDeltaX' in e) { + wheelDeltaX = e.wheelDeltaX / 12; + wheelDeltaY = e.wheelDeltaY / 12; + } else if ('detail' in e) { + wheelDeltaX = wheelDeltaY = -e.detail * 3; + } else { + wheelDeltaX = wheelDeltaY = -e.wheelDelta; + } + + if (that.options.wheelAction == 'zoom') { + deltaScale = that.scale * Math.pow(2, 1/3 * (wheelDeltaY ? wheelDeltaY / Math.abs(wheelDeltaY) : 0)); + if (deltaScale < that.options.zoomMin) deltaScale = that.options.zoomMin; + if (deltaScale > that.options.zoomMax) deltaScale = that.options.zoomMax; + + if (deltaScale != that.scale) { + if (!that.wheelZoomCount && that.options.onZoomStart) that.options.onZoomStart.call(that, e); + that.wheelZoomCount++; + + that.zoom(e.pageX, e.pageY, deltaScale, 400); + + setTimeout(function() { + that.wheelZoomCount--; + if (!that.wheelZoomCount && that.options.onZoomEnd) that.options.onZoomEnd.call(that, e); + }, 400); + } + + return; + } + + deltaX = that.x + wheelDeltaX; + deltaY = that.y + wheelDeltaY; + + if (deltaX > 0) deltaX = 0; + else if (deltaX < that.maxScrollX) deltaX = that.maxScrollX; + + if (deltaY > that.minScrollY) deltaY = that.minScrollY; + else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY; + + that.scrollTo(deltaX, deltaY, 0); + }, + + _mouseout: function (e) { + var t = e.relatedTarget; + + if (!t) { + this._end(e); + return; + } + + while (t = t.parentNode) if (t == this.wrapper) return; + + this._end(e); + }, + + _transitionEnd: function (e) { + var that = this; + + if (e.target != that.scroller) return; + + that._unbind('webkitTransitionEnd'); + + that._startAni(); + }, + + + /** + * + * Utilities + * + */ + _startAni: function () { + var that = this, + startX = that.x, startY = that.y, + startTime = Date.now(), + step, easeOut, + animate; + + if (that.animating) return; + + if (!that.steps.length) { + that._resetPos(400); + return; + } + + step = that.steps.shift(); + + if (step.x == startX && step.y == startY) step.time = 0; + + that.animating = true; + that.moved = true; + + if (that.options.useTransition) { + that._transitionTime(step.time); + that._pos(step.x, step.y); + that.animating = false; + if (step.time) that._bind('webkitTransitionEnd'); + else that._resetPos(0); + return; + } + + animate = function () { + var now = Date.now(), + newX, newY; + + if (now >= startTime + step.time) { + that._pos(step.x, step.y); + that.animating = false; + if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that); // Execute custom code on animation end + that._startAni(); + return; + } + + now = (now - startTime) / step.time - 1; + easeOut = m.sqrt(1 - now * now); + newX = (step.x - startX) * easeOut + startX; + newY = (step.y - startY) * easeOut + startY; + that._pos(newX, newY); + if (that.animating) that.aniTime = nextFrame(animate); + }; + + animate(); + }, + + _transitionTime: function (time) { + time += 'ms'; + this.scroller.style[vendor + 'TransitionDuration'] = time; + if (this.hScrollbar) this.hScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; + if (this.vScrollbar) this.vScrollbarIndicator.style[vendor + 'TransitionDuration'] = time; + }, + + _momentum: function (dist, time, maxDistUpper, maxDistLower, size) { + var deceleration = 0.0006, + speed = m.abs(dist) / time, + newDist = (speed * speed) / (2 * deceleration), + newTime = 0, outsideDist = 0; + + // Proportinally reduce speed if we are outside of the boundaries + if (dist > 0 && newDist > maxDistUpper) { + outsideDist = size / (6 / (newDist / speed * deceleration)); + maxDistUpper = maxDistUpper + outsideDist; + speed = speed * maxDistUpper / newDist; + newDist = maxDistUpper; + } else if (dist < 0 && newDist > maxDistLower) { + outsideDist = size / (6 / (newDist / speed * deceleration)); + maxDistLower = maxDistLower + outsideDist; + speed = speed * maxDistLower / newDist; + newDist = maxDistLower; + } + + newDist = newDist * (dist < 0 ? -1 : 1); + newTime = speed / deceleration; + + return { dist: newDist, time: mround(newTime) }; + }, + + _offset: function (el) { + var left = -el.offsetLeft, + top = -el.offsetTop; + + while (el = el.offsetParent) { + left -= el.offsetLeft; + top -= el.offsetTop; + } + + if (el != this.wrapper) { + left *= this.scale; + top *= this.scale; + } + + return { left: left, top: top }; + }, + + _snap: function (x, y) { + var that = this, + i, l, + page, time, + sizeX, sizeY; + + // Check page X + page = that.pagesX.length - 1; + for (i=0, l=that.pagesX.length; i= that.pagesX[i]) { + page = i; + break; + } + } + if (page == that.currPageX && page > 0 && that.dirX < 0) page--; + x = that.pagesX[page]; + sizeX = m.abs(x - that.pagesX[that.currPageX]); + sizeX = sizeX ? m.abs(that.x - x) / sizeX * 500 : 0; + that.currPageX = page; + + // Check page Y + page = that.pagesY.length-1; + for (i=0; i= that.pagesY[i]) { + page = i; + break; + } + } + if (page == that.currPageY && page > 0 && that.dirY < 0) page--; + y = that.pagesY[page]; + sizeY = m.abs(y - that.pagesY[that.currPageY]); + sizeY = sizeY ? m.abs(that.y - y) / sizeY * 500 : 0; + that.currPageY = page; + + // Snap with constant speed (proportional duration) + time = mround(m.max(sizeX, sizeY)) || 200; + + return { x: x, y: y, time: time }; + }, + + _bind: function (type, el, bubble) { + (el || this.scroller).addEventListener(type, this, !!bubble); + }, + + _unbind: function (type, el, bubble) { + (el || this.scroller).removeEventListener(type, this, !!bubble); + }, + + + /** + * + * Public methods + * + */ + destroy: function () { + var that = this; + + that.scroller.style[vendor + 'Transform'] = ''; + + // Remove the scrollbars + that.hScrollbar = false; + that.vScrollbar = false; + that._scrollbar('h'); + that._scrollbar('v'); + + // Remove the event listeners + that._unbind(RESIZE_EV, window); + that._unbind(START_EV); + that._unbind(MOVE_EV); + that._unbind(END_EV); + that._unbind(CANCEL_EV); + + if (!that.options.hasTouch) { + that._unbind('mouseout', that.wrapper); + that._unbind(WHEEL_EV); + } + + if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + + if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime); + + if (that.options.onDestroy) that.options.onDestroy.call(that); + }, + + refresh: function () { + var that = this, + offset, + i, l, + els, + pos = 0, + page = 0; + + if (that.scale < that.options.zoomMin) that.scale = that.options.zoomMin; + that.wrapperW = that.wrapper.clientWidth || 1; + that.wrapperH = that.wrapper.clientHeight || 1; + + that.minScrollY = -that.options.topOffset || 0; + that.scrollerW = mround(that.scroller.offsetWidth * that.scale); + that.scrollerH = mround((that.scroller.offsetHeight + that.minScrollY) * that.scale); + that.maxScrollX = that.wrapperW - that.scrollerW; + that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY; + that.dirX = 0; + that.dirY = 0; + + if (that.options.onRefresh) that.options.onRefresh.call(that); + + that.hScroll = that.options.hScroll && that.maxScrollX < 0; + that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH); + + that.hScrollbar = that.hScroll && that.options.hScrollbar; + that.vScrollbar = that.vScroll && that.options.vScrollbar && that.scrollerH > that.wrapperH; + + offset = that._offset(that.wrapper); + that.wrapperOffsetLeft = -offset.left; + that.wrapperOffsetTop = -offset.top; + + // Prepare snap + if (typeof that.options.snap == 'string') { + that.pagesX = []; + that.pagesY = []; + els = that.scroller.querySelectorAll(that.options.snap); + for (i=0, l=els.length; i= that.maxScrollX) { + that.pagesX[page] = pos; + pos = pos - that.wrapperW; + page++; + } + if (that.maxScrollX%that.wrapperW) that.pagesX[that.pagesX.length] = that.maxScrollX - that.pagesX[that.pagesX.length-1] + that.pagesX[that.pagesX.length-1]; + + pos = 0; + page = 0; + that.pagesY = []; + while (pos >= that.maxScrollY) { + that.pagesY[page] = pos; + pos = pos - that.wrapperH; + page++; + } + if (that.maxScrollY%that.wrapperH) that.pagesY[that.pagesY.length] = that.maxScrollY - that.pagesY[that.pagesY.length-1] + that.pagesY[that.pagesY.length-1]; + } + + // Prepare the scrollbars + that._scrollbar('h'); + that._scrollbar('v'); + + if (!that.zoomed) { + that.scroller.style[vendor + 'TransitionDuration'] = '0'; + that._resetPos(200); + } + }, + + scrollTo: function (x, y, time, relative) { + var that = this, + step = x, + i, l; + + that.stop(); + + if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }]; + + for (i=0, l=step.length; i 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left; + pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top; + time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time; + + that.scrollTo(pos.left, pos.top, time); + }, + + scrollToPage: function (pageX, pageY, time) { + var that = this, x, y; + + time = time === undefined ? 400 : time; + + if (that.options.onScrollStart) that.options.onScrollStart.call(that); + + if (that.options.snap) { + pageX = pageX == 'next' ? that.currPageX+1 : pageX == 'prev' ? that.currPageX-1 : pageX; + pageY = pageY == 'next' ? that.currPageY+1 : pageY == 'prev' ? that.currPageY-1 : pageY; + + pageX = pageX < 0 ? 0 : pageX > that.pagesX.length-1 ? that.pagesX.length-1 : pageX; + pageY = pageY < 0 ? 0 : pageY > that.pagesY.length-1 ? that.pagesY.length-1 : pageY; + + that.currPageX = pageX; + that.currPageY = pageY; + x = that.pagesX[pageX]; + y = that.pagesY[pageY]; + } else { + x = -that.wrapperW * pageX; + y = -that.wrapperH * pageY; + if (x < that.maxScrollX) x = that.maxScrollX; + if (y < that.maxScrollY) y = that.maxScrollY; + } + + that.scrollTo(x, y, time); + }, + + disable: function () { + this.stop(); + this._resetPos(0); + this.enabled = false; + + // If disabled after touchstart we make sure that there are no left over events + this._unbind(MOVE_EV); + this._unbind(END_EV); + this._unbind(CANCEL_EV); + }, + + enable: function () { + this.enabled = true; + }, + + stop: function () { + if (this.options.useTransition) this._unbind('webkitTransitionEnd'); + else cancelFrame(this.aniTime); + this.steps = []; + this.moved = false; + this.animating = false; + }, + + zoom: function (x, y, scale, time) { + var that = this, + relScale = scale / that.scale; + + if (!that.options.useTransform) return; + + that.zoomed = true; + time = time === undefined ? 200 : time; + x = x - that.wrapperOffsetLeft - that.x; + y = y - that.wrapperOffsetTop - that.y; + that.x = x - x * relScale + that.x; + that.y = y - y * relScale + that.y; + + that.scale = scale; + that.refresh(); + + that.x = that.x > 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x; + that.y = that.y > that.minScrollY ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y; + + that.scroller.style[vendor + 'TransitionDuration'] = time + 'ms'; + that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + scale + ')'; + that.zoomed = false; + }, + + isReady: function () { + return !this.moved && !this.zoomed && !this.animating; + } +}; + +if (typeof exports !== 'undefined') exports.iScroll = iScroll; +else window.iScroll = iScroll; + +})(); diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/jquery-1.7.1.min.js b/iOS/ExternalScreen/samples/fleet manager/www/js/jquery-1.7.1.min.js new file mode 100644 index 0000000..198b3ff --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/jquery-1.7.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/layers.png b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/layers.png new file mode 100755 index 0000000..9be965f Binary files /dev/null and b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/layers.png differ diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/marker-icon.png b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/marker-icon.png new file mode 100755 index 0000000..bef032e Binary files /dev/null and b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/marker-icon.png differ diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/marker-shadow.png b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/marker-shadow.png new file mode 100755 index 0000000..a64f6a6 Binary files /dev/null and b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/marker-shadow.png differ diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/zoom-in.png b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/zoom-in.png new file mode 100755 index 0000000..9f473d6 Binary files /dev/null and b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/zoom-in.png differ diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/zoom-out.png b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/zoom-out.png new file mode 100755 index 0000000..f0a5b5d Binary files /dev/null and b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/images/zoom-out.png differ diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet-src.js b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet-src.js new file mode 100755 index 0000000..4caee62 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet-src.js @@ -0,0 +1,7638 @@ +/* + Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin + Leaflet is an open-source JavaScript library for mobile-friendly interactive maps. + http://leaflet.cloudmade.com +*/ +(function (window, undefined) { + +var L, originalL; + +if (typeof exports !== undefined + '') { + L = exports; +} else { + originalL = window.L; + L = {}; + + L.noConflict = function () { + window.L = originalL; + return this; + }; + + window.L = L; +} + +L.version = '0.4.4'; + + +/* + * L.Util is a namespace for various utility functions. + */ + +L.Util = { + extend: function (/*Object*/ dest) /*-> Object*/ { // merge src properties into dest + var sources = Array.prototype.slice.call(arguments, 1); + for (var j = 0, len = sources.length, src; j < len; j++) { + src = sources[j] || {}; + for (var i in src) { + if (src.hasOwnProperty(i)) { + dest[i] = src[i]; + } + } + } + return dest; + }, + + bind: function (fn, obj) { // (Function, Object) -> Function + var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null; + return function () { + return fn.apply(obj, args || arguments); + }; + }, + + stamp: (function () { + var lastId = 0, key = '_leaflet_id'; + return function (/*Object*/ obj) { + obj[key] = obj[key] || ++lastId; + return obj[key]; + }; + }()), + + limitExecByInterval: function (fn, time, context) { + var lock, execOnUnlock; + + return function wrapperFn() { + var args = arguments; + + if (lock) { + execOnUnlock = true; + return; + } + + lock = true; + + setTimeout(function () { + lock = false; + + if (execOnUnlock) { + wrapperFn.apply(context, args); + execOnUnlock = false; + } + }, time); + + fn.apply(context, args); + }; + }, + + falseFn: function () { + return false; + }, + + formatNum: function (num, digits) { + var pow = Math.pow(10, digits || 5); + return Math.round(num * pow) / pow; + }, + + splitWords: function (str) { + return str.replace(/^\s+|\s+$/g, '').split(/\s+/); + }, + + setOptions: function (obj, options) { + obj.options = L.Util.extend({}, obj.options, options); + return obj.options; + }, + + getParamString: function (obj) { + var params = []; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + params.push(i + '=' + obj[i]); + } + } + return '?' + params.join('&'); + }, + + template: function (str, data) { + return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) { + var value = data[key]; + if (!data.hasOwnProperty(key)) { + throw new Error('No value provided for variable ' + str); + } + return value; + }); + }, + + emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' +}; + +(function () { + + function getPrefixed(name) { + var i, fn, + prefixes = ['webkit', 'moz', 'o', 'ms']; + + for (i = 0; i < prefixes.length && !fn; i++) { + fn = window[prefixes[i] + name]; + } + + return fn; + } + + function timeoutDefer(fn) { + return window.setTimeout(fn, 1000 / 60); + } + + var requestFn = window.requestAnimationFrame || + getPrefixed('RequestAnimationFrame') || timeoutDefer; + + var cancelFn = window.cancelAnimationFrame || + getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || + function (id) { + window.clearTimeout(id); + }; + + + L.Util.requestAnimFrame = function (fn, context, immediate, element) { + fn = L.Util.bind(fn, context); + + if (immediate && requestFn === timeoutDefer) { + fn(); + } else { + return requestFn.call(window, fn, element); + } + }; + + L.Util.cancelAnimFrame = function (id) { + if (id) { + cancelFn.call(window, id); + } + }; + +}()); + + +/* + * Class powers the OOP facilities of the library. Thanks to John Resig and Dean Edwards for inspiration! + */ + +L.Class = function () {}; + +L.Class.extend = function (/*Object*/ props) /*-> Class*/ { + + // extended class with the new prototype + var NewClass = function () { + if (this.initialize) { + this.initialize.apply(this, arguments); + } + }; + + // instantiate class without calling constructor + var F = function () {}; + F.prototype = this.prototype; + + var proto = new F(); + proto.constructor = NewClass; + + NewClass.prototype = proto; + + //inherit parent's statics + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'prototype') { + NewClass[i] = this[i]; + } + } + + // mix static properties into the class + if (props.statics) { + L.Util.extend(NewClass, props.statics); + delete props.statics; + } + + // mix includes into the prototype + if (props.includes) { + L.Util.extend.apply(null, [proto].concat(props.includes)); + delete props.includes; + } + + // merge options + if (props.options && proto.options) { + props.options = L.Util.extend({}, proto.options, props.options); + } + + // mix given properties into the prototype + L.Util.extend(proto, props); + + return NewClass; +}; + + +// method for adding properties to prototype +L.Class.include = function (props) { + L.Util.extend(this.prototype, props); +}; + +L.Class.mergeOptions = function (options) { + L.Util.extend(this.prototype.options, options); +}; + +/* + * L.Mixin.Events adds custom events functionality to Leaflet classes + */ + +var key = '_leaflet_events'; + +L.Mixin = {}; + +L.Mixin.Events = { + + addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object]) + var events = this[key] = this[key] || {}, + type, i, len; + + // Types can be a map of types/handlers + if (typeof types === 'object') { + for (type in types) { + if (types.hasOwnProperty(type)) { + this.addEventListener(type, types[type], fn); + } + } + + return this; + } + + types = L.Util.splitWords(types); + + for (i = 0, len = types.length; i < len; i++) { + events[types[i]] = events[types[i]] || []; + events[types[i]].push({ + action: fn, + context: context || this + }); + } + + return this; + }, + + hasEventListeners: function (type) { // (String) -> Boolean + return (key in this) && (type in this[key]) && (this[key][type].length > 0); + }, + + removeEventListener: function (types, fn, context) { // (String[, Function, Object]) or (Object[, Object]) + var events = this[key], + type, i, len, listeners, j; + + if (typeof types === 'object') { + for (type in types) { + if (types.hasOwnProperty(type)) { + this.removeEventListener(type, types[type], fn); + } + } + + return this; + } + + types = L.Util.splitWords(types); + + for (i = 0, len = types.length; i < len; i++) { + + if (this.hasEventListeners(types[i])) { + listeners = events[types[i]]; + + for (j = listeners.length - 1; j >= 0; j--) { + if ( + (!fn || listeners[j].action === fn) && + (!context || (listeners[j].context === context)) + ) { + listeners.splice(j, 1); + } + } + } + } + + return this; + }, + + fireEvent: function (type, data) { // (String[, Object]) + if (!this.hasEventListeners(type)) { + return this; + } + + var event = L.Util.extend({ + type: type, + target: this + }, data); + + var listeners = this[key][type].slice(); + + for (var i = 0, len = listeners.length; i < len; i++) { + listeners[i].action.call(listeners[i].context || this, event); + } + + return this; + } +}; + +L.Mixin.Events.on = L.Mixin.Events.addEventListener; +L.Mixin.Events.off = L.Mixin.Events.removeEventListener; +L.Mixin.Events.fire = L.Mixin.Events.fireEvent; + + +(function () { + var ua = navigator.userAgent.toLowerCase(), + ie = !!window.ActiveXObject, + ie6 = ie && !window.XMLHttpRequest, + webkit = ua.indexOf("webkit") !== -1, + gecko = ua.indexOf("gecko") !== -1, + //Terrible browser detection to work around a safari / iOS / android browser bug. See TileLayer._addTile and debug/hacks/jitter.html + chrome = ua.indexOf("chrome") !== -1, + opera = window.opera, + android = ua.indexOf("android") !== -1, + android23 = ua.search("android [23]") !== -1, + mobile = typeof orientation !== undefined + '' ? true : false, + doc = document.documentElement, + ie3d = ie && ('transition' in doc.style), + webkit3d = webkit && ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()), + gecko3d = gecko && ('MozPerspective' in doc.style), + opera3d = opera && ('OTransition' in doc.style); + + var touch = !window.L_NO_TOUCH && (function () { + var startName = 'ontouchstart'; + + // WebKit, etc + if (startName in doc) { + return true; + } + + // Firefox/Gecko + var div = document.createElement('div'), + supported = false; + + if (!div.setAttribute) { + return false; + } + div.setAttribute(startName, 'return;'); + + if (typeof div[startName] === 'function') { + supported = true; + } + + div.removeAttribute(startName); + div = null; + + return supported; + }()); + + var retina = (('devicePixelRatio' in window && window.devicePixelRatio > 1) || ('matchMedia' in window && window.matchMedia("(min-resolution:144dpi)").matches)); + + L.Browser = { + ua: ua, + ie: ie, + ie6: ie6, + webkit: webkit, + gecko: gecko, + opera: opera, + android: android, + android23: android23, + + chrome: chrome, + + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + opera3d: opera3d, + any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d), + + mobile: mobile, + mobileWebkit: mobile && webkit, + mobileWebkit3d: mobile && webkit3d, + mobileOpera: mobile && opera, + + touch: touch, + + retina: retina + }; +}()); + + +/* + * L.Point represents a point with x and y coordinates. + */ + +L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) { + this.x = (round ? Math.round(x) : x); + this.y = (round ? Math.round(y) : y); +}; + +L.Point.prototype = { + add: function (point) { + return this.clone()._add(L.point(point)); + }, + + _add: function (point) { + this.x += point.x; + this.y += point.y; + return this; + }, + + subtract: function (point) { + return this.clone()._subtract(L.point(point)); + }, + + // destructive subtract (faster) + _subtract: function (point) { + this.x -= point.x; + this.y -= point.y; + return this; + }, + + divideBy: function (num, round) { + return new L.Point(this.x / num, this.y / num, round); + }, + + multiplyBy: function (num, round) { + return new L.Point(this.x * num, this.y * num, round); + }, + + distanceTo: function (point) { + point = L.point(point); + + var x = point.x - this.x, + y = point.y - this.y; + + return Math.sqrt(x * x + y * y); + }, + + round: function () { + return this.clone()._round(); + }, + + // destructive round + _round: function () { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }, + + floor: function () { + return this.clone()._floor(); + }, + + _floor: function () { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }, + + clone: function () { + return new L.Point(this.x, this.y); + }, + + toString: function () { + return 'Point(' + + L.Util.formatNum(this.x) + ', ' + + L.Util.formatNum(this.y) + ')'; + } +}; + +L.point = function (x, y, round) { + if (x instanceof L.Point) { + return x; + } + if (x instanceof Array) { + return new L.Point(x[0], x[1]); + } + if (isNaN(x)) { + return x; + } + return new L.Point(x, y, round); +}; + + +/* + * L.Bounds represents a rectangular area on the screen in pixel coordinates. + */ + +L.Bounds = L.Class.extend({ + + initialize: function (a, b) { //(Point, Point) or Point[] + if (!a) { return; } + + var points = b ? [a, b] : a; + + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } + }, + + // extend the bounds to contain the given point + extend: function (point) { // (Point) + point = L.point(point); + + if (!this.min && !this.max) { + this.min = point.clone(); + this.max = point.clone(); + } else { + this.min.x = Math.min(point.x, this.min.x); + this.max.x = Math.max(point.x, this.max.x); + this.min.y = Math.min(point.y, this.min.y); + this.max.y = Math.max(point.y, this.max.y); + } + return this; + }, + + getCenter: function (round) { // (Boolean) -> Point + return new L.Point( + (this.min.x + this.max.x) / 2, + (this.min.y + this.max.y) / 2, round); + }, + + getBottomLeft: function () { // -> Point + return new L.Point(this.min.x, this.max.y); + }, + + getTopRight: function () { // -> Point + return new L.Point(this.max.x, this.min.y); + }, + + contains: function (obj) { // (Bounds) or (Point) -> Boolean + var min, max; + + if (typeof obj[0] === 'number' || obj instanceof L.Point) { + obj = L.point(obj); + } else { + obj = L.bounds(obj); + } + + if (obj instanceof L.Bounds) { + min = obj.min; + max = obj.max; + } else { + min = max = obj; + } + + return (min.x >= this.min.x) && + (max.x <= this.max.x) && + (min.y >= this.min.y) && + (max.y <= this.max.y); + }, + + intersects: function (bounds) { // (Bounds) -> Boolean + bounds = L.bounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max; + + var xIntersects = (max2.x >= min.x) && (min2.x <= max.x), + yIntersects = (max2.y >= min.y) && (min2.y <= max.y); + + return xIntersects && yIntersects; + } + +}); + +L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) + if (!a || a instanceof L.Bounds) { + return a; + } + return new L.Bounds(a, b); +}; + + +/* + * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix. + */ + +L.Transformation = L.Class.extend({ + initialize: function (/*Number*/ a, /*Number*/ b, /*Number*/ c, /*Number*/ d) { + this._a = a; + this._b = b; + this._c = c; + this._d = d; + }, + + transform: function (point, scale) { + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + untransform: function (/*Point*/ point, /*Number*/ scale) /*-> Point*/ { + scale = scale || 1; + return new L.Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } +}); + + +/* + * L.DomUtil contains various utility functions for working with DOM + */ + +L.DomUtil = { + get: function (id) { + return (typeof id === 'string' ? document.getElementById(id) : id); + }, + + getStyle: function (el, style) { + var value = el.style[style]; + if (!value && el.currentStyle) { + value = el.currentStyle[style]; + } + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(el, null); + value = css ? css[style] : null; + } + return (value === 'auto' ? null : value); + }, + + getViewportOffset: function (element) { + var top = 0, + left = 0, + el = element, + docBody = document.body; + + do { + top += el.offsetTop || 0; + left += el.offsetLeft || 0; + + if (el.offsetParent === docBody && + L.DomUtil.getStyle(el, 'position') === 'absolute') { + break; + } + if (L.DomUtil.getStyle(el, 'position') === 'fixed') { + top += docBody.scrollTop || 0; + left += docBody.scrollLeft || 0; + break; + } + + el = el.offsetParent; + } while (el); + + el = element; + + do { + if (el === docBody) { + break; + } + + top -= el.scrollTop || 0; + left -= el.scrollLeft || 0; + + el = el.parentNode; + } while (el); + + return new L.Point(left, top); + }, + + create: function (tagName, className, container) { + var el = document.createElement(tagName); + el.className = className; + if (container) { + container.appendChild(el); + } + return el; + }, + + disableTextSelection: function () { + if (document.selection && document.selection.empty) { + document.selection.empty(); + } + if (!this._onselectstart) { + this._onselectstart = document.onselectstart; + document.onselectstart = L.Util.falseFn; + } + }, + + enableTextSelection: function () { + document.onselectstart = this._onselectstart; + this._onselectstart = null; + }, + + hasClass: function (el, name) { + return (el.className.length > 0) && + new RegExp("(^|\\s)" + name + "(\\s|$)").test(el.className); + }, + + addClass: function (el, name) { + if (!L.DomUtil.hasClass(el, name)) { + el.className += (el.className ? ' ' : '') + name; + } + }, + + removeClass: function (el, name) { + function replaceFn(w, match) { + if (match === name) { + return ''; + } + return w; + } + el.className = el.className + .replace(/(\S+)\s*/g, replaceFn) + .replace(/(^\s+|\s+$)/, ''); + }, + + setOpacity: function (el, value) { + + if ('opacity' in el.style) { + el.style.opacity = value; + + } else if (L.Browser.ie) { + + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; + + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { filter = el.filters.item(filterName); } catch (e) {} + + value = Math.round(value * 100); + + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + } + } + }, + + testProp: function (props) { + var style = document.documentElement.style; + + for (var i = 0; i < props.length; i++) { + if (props[i] in style) { + return props[i]; + } + } + return false; + }, + + getTranslateString: function (point) { + // On webkit browsers (Chrome/Safari/MobileSafari/Android) using translate3d instead of translate + // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care + // (same speed either way), Opera 12 doesn't support translate3d + + var is3d = L.Browser.webkit3d, + open = 'translate' + (is3d ? '3d' : '') + '(', + close = (is3d ? ',0' : '') + ')'; + + return open + point.x + 'px,' + point.y + 'px' + close; + }, + + getScaleString: function (scale, origin) { + var preTranslateStr = L.DomUtil.getTranslateString(origin), + scaleStr = ' scale(' + scale + ') ', + postTranslateStr = L.DomUtil.getTranslateString(origin.multiplyBy(-1)); + + return preTranslateStr + scaleStr + postTranslateStr; + }, + + setPosition: function (el, point, disable3D) { + el._leaflet_pos = point; + if (!disable3D && L.Browser.any3d) { + el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point); + + // workaround for Android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69) + if (L.Browser.mobileWebkit3d) { + el.style.WebkitBackfaceVisibility = 'hidden'; + } + } else { + el.style.left = point.x + 'px'; + el.style.top = point.y + 'px'; + } + }, + + getPosition: function (el) { + return el._leaflet_pos; + } +}; + +L.Util.extend(L.DomUtil, { + TRANSITION: L.DomUtil.testProp(['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']), + TRANSFORM: L.DomUtil.testProp(['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']) +}); + + +/* + CM.LatLng represents a geographical point with latitude and longtitude coordinates. +*/ + +L.LatLng = function (rawLat, rawLng, noWrap) { // (Number, Number[, Boolean]) + var lat = parseFloat(rawLat), + lng = parseFloat(rawLng); + + if (isNaN(lat) || isNaN(lng)) { + throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')'); + } + + if (noWrap !== true) { + lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90 + lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longtitude into -180..180 + } + + this.lat = lat; + this.lng = lng; +}; + +L.Util.extend(L.LatLng, { + DEG_TO_RAD: Math.PI / 180, + RAD_TO_DEG: 180 / Math.PI, + MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check +}); + +L.LatLng.prototype = { + equals: function (obj) { // (LatLng) -> Boolean + if (!obj) { return false; } + + obj = L.latLng(obj); + + var margin = Math.max(Math.abs(this.lat - obj.lat), Math.abs(this.lng - obj.lng)); + return margin <= L.LatLng.MAX_MARGIN; + }, + + toString: function () { // -> String + return 'LatLng(' + + L.Util.formatNum(this.lat) + ', ' + + L.Util.formatNum(this.lng) + ')'; + }, + + // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula + distanceTo: function (other) { // (LatLng) -> Number + other = L.latLng(other); + + var R = 6378137, // earth radius in meters + d2r = L.LatLng.DEG_TO_RAD, + dLat = (other.lat - this.lat) * d2r, + dLon = (other.lng - this.lng) * d2r, + lat1 = this.lat * d2r, + lat2 = other.lat * d2r, + sin1 = Math.sin(dLat / 2), + sin2 = Math.sin(dLon / 2); + + var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2); + + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + } +}; + +L.latLng = function (a, b, c) { // (LatLng) or ([Number, Number]) or (Number, Number, Boolean) + if (a instanceof L.LatLng) { + return a; + } + if (a instanceof Array) { + return new L.LatLng(a[0], a[1]); + } + if (isNaN(a)) { + return a; + } + return new L.LatLng(a, b, c); +}; + + +/* + * L.LatLngBounds represents a rectangular area on the map in geographical coordinates. + */ + +L.LatLngBounds = L.Class.extend({ + initialize: function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) + if (!southWest) { return; } + + var latlngs = northEast ? [southWest, northEast] : southWest; + + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } + }, + + // extend the bounds to contain the given point or bounds + extend: function (obj) { // (LatLng) or (LatLngBounds) + if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { + obj = L.latLng(obj); + } else { + obj = L.latLngBounds(obj); + } + + if (obj instanceof L.LatLng) { + if (!this._southWest && !this._northEast) { + this._southWest = new L.LatLng(obj.lat, obj.lng, true); + this._northEast = new L.LatLng(obj.lat, obj.lng, true); + } else { + this._southWest.lat = Math.min(obj.lat, this._southWest.lat); + this._southWest.lng = Math.min(obj.lng, this._southWest.lng); + + this._northEast.lat = Math.max(obj.lat, this._northEast.lat); + this._northEast.lng = Math.max(obj.lng, this._northEast.lng); + } + } else if (obj instanceof L.LatLngBounds) { + this.extend(obj._southWest); + this.extend(obj._northEast); + } + return this; + }, + + // extend the bounds by a percentage + pad: function (bufferRatio) { // (Number) -> LatLngBounds + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; + + return new L.LatLngBounds( + new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); + }, + + getCenter: function () { // -> LatLng + return new L.LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); + }, + + getSouthWest: function () { + return this._southWest; + }, + + getNorthEast: function () { + return this._northEast; + }, + + getNorthWest: function () { + return new L.LatLng(this._northEast.lat, this._southWest.lng, true); + }, + + getSouthEast: function () { + return new L.LatLng(this._southWest.lat, this._northEast.lng, true); + }, + + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { + obj = L.latLng(obj); + } else { + obj = L.latLngBounds(obj); + } + + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof L.LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); + } else { + sw2 = ne2 = obj; + } + + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + }, + + intersects: function (bounds) { // (LatLngBounds) + bounds = L.latLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(); + + var latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + + return latIntersects && lngIntersects; + }, + + toBBoxString: function () { + var sw = this._southWest, + ne = this._northEast; + return [sw.lng, sw.lat, ne.lng, ne.lat].join(','); + }, + + equals: function (bounds) { // (LatLngBounds) + if (!bounds) { return false; } + + bounds = L.latLngBounds(bounds); + + return this._southWest.equals(bounds.getSouthWest()) && + this._northEast.equals(bounds.getNorthEast()); + } +}); + +//TODO International date line? + +L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng) + if (!a || a instanceof L.LatLngBounds) { + return a; + } + return new L.LatLngBounds(a, b); +}; + + +/* + * L.Projection contains various geographical projections used by CRS classes. + */ + +L.Projection = {}; + + + +L.Projection.SphericalMercator = { + MAX_LATITUDE: 85.0511287798, + + project: function (latlng) { // (LatLng) -> Point + var d = L.LatLng.DEG_TO_RAD, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + x = latlng.lng * d, + y = lat * d; + y = Math.log(Math.tan((Math.PI / 4) + (y / 2))); + + return new L.Point(x, y); + }, + + unproject: function (point) { // (Point, Boolean) -> LatLng + var d = L.LatLng.RAD_TO_DEG, + lng = point.x * d, + lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; + + // TODO refactor LatLng wrapping + return new L.LatLng(lat, lng, true); + } +}; + + + +L.Projection.LonLat = { + project: function (latlng) { + return new L.Point(latlng.lng, latlng.lat); + }, + + unproject: function (point) { + return new L.LatLng(point.y, point.x, true); + } +}; + + + +L.CRS = { + latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + + return this.transformation._transform(projectedPoint, scale); + }, + + pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + + return this.projection.unproject(untransformedPoint); + }, + + project: function (latlng) { + return this.projection.project(latlng); + }, + + scale: function (zoom) { + return 256 * Math.pow(2, zoom); + } +}; + + + +L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, { + code: 'EPSG:3857', + + projection: L.Projection.SphericalMercator, + transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5), + + project: function (latlng) { // (LatLng) -> Point + var projectedPoint = this.projection.project(latlng), + earthRadius = 6378137; + return projectedPoint.multiplyBy(earthRadius); + } +}); + +L.CRS.EPSG900913 = L.Util.extend({}, L.CRS.EPSG3857, { + code: 'EPSG:900913' +}); + + + +L.CRS.EPSG4326 = L.Util.extend({}, L.CRS, { + code: 'EPSG:4326', + + projection: L.Projection.LonLat, + transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5) +}); + + +/* + * L.Map is the central class of the API - it is used to create a map. + */ + +L.Map = L.Class.extend({ + + includes: L.Mixin.Events, + + options: { + crs: L.CRS.EPSG3857, + + /* + center: LatLng, + zoom: Number, + layers: Array, + */ + + fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23, + trackResize: true, + markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d + }, + + initialize: function (id, options) { // (HTMLElement or String, Object) + options = L.Util.setOptions(this, options); + + this._initContainer(id); + this._initLayout(); + this._initHooks(); + this._initEvents(); + + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); + } + + if (options.center && options.zoom !== undefined) { + this.setView(L.latLng(options.center), options.zoom, true); + } + + this._initLayers(options.layers); + }, + + + // public methods that modify map state + + // replaced by animation-powered implementation in Map.PanAnimation.js + setView: function (center, zoom) { + this._resetView(L.latLng(center), this._limitZoom(zoom)); + return this; + }, + + setZoom: function (zoom) { // (Number) + return this.setView(this.getCenter(), zoom); + }, + + zoomIn: function () { + return this.setZoom(this._zoom + 1); + }, + + zoomOut: function () { + return this.setZoom(this._zoom - 1); + }, + + fitBounds: function (bounds) { // (LatLngBounds) + var zoom = this.getBoundsZoom(bounds); + return this.setView(L.latLngBounds(bounds).getCenter(), zoom); + }, + + fitWorld: function () { + var sw = new L.LatLng(-60, -170), + ne = new L.LatLng(85, 179); + + return this.fitBounds(new L.LatLngBounds(sw, ne)); + }, + + panTo: function (center) { // (LatLng) + return this.setView(center, this._zoom); + }, + + panBy: function (offset) { // (Point) + // replaced with animated panBy in Map.Animation.js + this.fire('movestart'); + + this._rawPanBy(L.point(offset)); + + this.fire('move'); + return this.fire('moveend'); + }, + + setMaxBounds: function (bounds) { + bounds = L.latLngBounds(bounds); + + this.options.maxBounds = bounds; + + if (!bounds) { + this._boundsMinZoom = null; + return this; + } + + var minZoom = this.getBoundsZoom(bounds, true); + + this._boundsMinZoom = minZoom; + + if (this._loaded) { + if (this._zoom < minZoom) { + this.setView(bounds.getCenter(), minZoom); + } else { + this.panInsideBounds(bounds); + } + } + + return this; + }, + + panInsideBounds: function (bounds) { + bounds = L.latLngBounds(bounds); + + var viewBounds = this.getBounds(), + viewSw = this.project(viewBounds.getSouthWest()), + viewNe = this.project(viewBounds.getNorthEast()), + sw = this.project(bounds.getSouthWest()), + ne = this.project(bounds.getNorthEast()), + dx = 0, + dy = 0; + + if (viewNe.y < ne.y) { // north + dy = ne.y - viewNe.y; + } + if (viewNe.x > ne.x) { // east + dx = ne.x - viewNe.x; + } + if (viewSw.y > sw.y) { // south + dy = sw.y - viewSw.y; + } + if (viewSw.x < sw.x) { // west + dx = sw.x - viewSw.x; + } + + return this.panBy(new L.Point(dx, dy, true)); + }, + + addLayer: function (layer) { + // TODO method is too big, refactor + + var id = L.Util.stamp(layer); + + if (this._layers[id]) { return this; } + + this._layers[id] = layer; + + // TODO getMaxZoom, getMinZoom in ILayer (instead of options) + if (layer.options && !isNaN(layer.options.maxZoom)) { + this._layersMaxZoom = Math.max(this._layersMaxZoom || 0, layer.options.maxZoom); + } + if (layer.options && !isNaN(layer.options.minZoom)) { + this._layersMinZoom = Math.min(this._layersMinZoom || Infinity, layer.options.minZoom); + } + + // TODO looks ugly, refactor!!! + if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { + this._tileLayersNum++; + this._tileLayersToLoad++; + layer.on('load', this._onTileLayerLoad, this); + } + + var onMapLoad = function () { + layer.onAdd(this); + this.fire('layeradd', {layer: layer}); + }; + + if (this._loaded) { + onMapLoad.call(this); + } else { + this.on('load', onMapLoad, this); + } + + return this; + }, + + removeLayer: function (layer) { + var id = L.Util.stamp(layer); + + if (!this._layers[id]) { return; } + + layer.onRemove(this); + + delete this._layers[id]; + + // TODO looks ugly, refactor + if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { + this._tileLayersNum--; + this._tileLayersToLoad--; + layer.off('load', this._onTileLayerLoad, this); + } + + return this.fire('layerremove', {layer: layer}); + }, + + hasLayer: function (layer) { + var id = L.Util.stamp(layer); + return this._layers.hasOwnProperty(id); + }, + + invalidateSize: function (animate) { + var oldSize = this.getSize(); + + this._sizeChanged = true; + + if (this.options.maxBounds) { + this.setMaxBounds(this.options.maxBounds); + } + + if (!this._loaded) { return this; } + + var offset = oldSize.subtract(this.getSize()).divideBy(2, true); + + if (animate === true) { + this.panBy(offset); + } else { + this._rawPanBy(offset); + + this.fire('move'); + + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(L.Util.bind(this.fire, this, 'moveend'), 200); + } + return this; + }, + + // TODO handler.addTo + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return; } + + this[name] = new HandlerClass(this); + + if (this.options[name]) { + this[name].enable(); + } + + return this; + }, + + + // public methods for getting map state + + getCenter: function () { // (Boolean) -> LatLng + return this.layerPointToLatLng(this._getCenterLayerPoint()); + }, + + getZoom: function () { + return this._zoom; + }, + + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); + + return new L.LatLngBounds(sw, ne); + }, + + getMinZoom: function () { + var z1 = this.options.minZoom || 0, + z2 = this._layersMinZoom || 0, + z3 = this._boundsMinZoom || 0; + + return Math.max(z1, z2, z3); + }, + + getMaxZoom: function () { + var z1 = this.options.maxZoom === undefined ? Infinity : this.options.maxZoom, + z2 = this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom; + + return Math.min(z1, z2); + }, + + getBoundsZoom: function (bounds, inside) { // (LatLngBounds, Boolean) -> Number + bounds = L.latLngBounds(bounds); + + var size = this.getSize(), + zoom = this.options.minZoom || 0, + maxZoom = this.getMaxZoom(), + ne = bounds.getNorthEast(), + sw = bounds.getSouthWest(), + boundsSize, + nePoint, + swPoint, + zoomNotFound = true; + + if (inside) { + zoom--; + } + + do { + zoom++; + nePoint = this.project(ne, zoom); + swPoint = this.project(sw, zoom); + boundsSize = new L.Point(Math.abs(nePoint.x - swPoint.x), Math.abs(swPoint.y - nePoint.y)); + + if (!inside) { + zoomNotFound = boundsSize.x <= size.x && boundsSize.y <= size.y; + } else { + zoomNotFound = boundsSize.x < size.x || boundsSize.y < size.y; + } + } while (zoomNotFound && zoom <= maxZoom); + + if (zoomNotFound && inside) { + return null; + } + + return inside ? zoom : zoom - 1; + }, + + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new L.Point( + this._container.clientWidth, + this._container.clientHeight); + + this._sizeChanged = false; + } + return this._size; + }, + + getPixelBounds: function () { + var topLeftPoint = this._getTopLeftPoint(); + return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + }, + + getPixelOrigin: function () { + return this._initialTopLeftPoint; + }, + + getPanes: function () { + return this._panes; + }, + + getContainer: function () { + return this._container; + }, + + + // TODO replace with universal implementation after refactoring projections + + getZoomScale: function (toZoom) { + var crs = this.options.crs; + return crs.scale(toZoom) / crs.scale(this._zoom); + }, + + getScaleZoom: function (scale) { + return this._zoom + (Math.log(scale) / Math.LN2); + }, + + + // conversion methods + + project: function (latlng, zoom) { // (LatLng[, Number]) -> Point + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(L.latLng(latlng), zoom); + }, + + unproject: function (point, zoom) { // (Point[, Number]) -> LatLng + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(L.point(point), zoom); + }, + + layerPointToLatLng: function (point) { // (Point) + var projectedPoint = L.point(point).add(this._initialTopLeftPoint); + return this.unproject(projectedPoint); + }, + + latLngToLayerPoint: function (latlng) { // (LatLng) + var projectedPoint = this.project(L.latLng(latlng))._round(); + return projectedPoint._subtract(this._initialTopLeftPoint); + }, + + containerPointToLayerPoint: function (point) { // (Point) + return L.point(point).subtract(this._getMapPanePos()); + }, + + layerPointToContainerPoint: function (point) { // (Point) + return L.point(point).add(this._getMapPanePos()); + }, + + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(L.point(point)); + return this.layerPointToLatLng(layerPoint); + }, + + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng))); + }, + + mouseEventToContainerPoint: function (e) { // (MouseEvent) + return L.DomEvent.getMousePosition(e, this._container); + }, + + mouseEventToLayerPoint: function (e) { // (MouseEvent) + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, + + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, + + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = L.DomUtil.get(id); + + if (container._leaflet) { + throw new Error("Map container is already initialized."); + } + + container._leaflet = true; + }, + + _initLayout: function () { + var container = this._container; + + container.innerHTML = ''; + L.DomUtil.addClass(container, 'leaflet-container'); + + if (L.Browser.touch) { + L.DomUtil.addClass(container, 'leaflet-touch'); + } + + if (this.options.fadeAnimation) { + L.DomUtil.addClass(container, 'leaflet-fade-anim'); + } + + var position = L.DomUtil.getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { + container.style.position = 'relative'; + } + + this._initPanes(); + + if (this._initControlPos) { + this._initControlPos(); + } + }, + + _initPanes: function () { + var panes = this._panes = {}; + + this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container); + + this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane); + this._objectsPane = panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane); + + panes.shadowPane = this._createPane('leaflet-shadow-pane'); + panes.overlayPane = this._createPane('leaflet-overlay-pane'); + panes.markerPane = this._createPane('leaflet-marker-pane'); + panes.popupPane = this._createPane('leaflet-popup-pane'); + + var zoomHide = ' leaflet-zoom-hide'; + + if (!this.options.markerZoomAnimation) { + L.DomUtil.addClass(panes.markerPane, zoomHide); + L.DomUtil.addClass(panes.shadowPane, zoomHide); + L.DomUtil.addClass(panes.popupPane, zoomHide); + } + }, + + _createPane: function (className, container) { + return L.DomUtil.create('div', className, container || this._objectsPane); + }, + + _initializers: [], + + _initHooks: function () { + var i, len; + for (i = 0, len = this._initializers.length; i < len; i++) { + this._initializers[i].call(this); + } + }, + + _initLayers: function (layers) { + layers = layers ? (layers instanceof Array ? layers : [layers]) : []; + + this._layers = {}; + this._tileLayersNum = 0; + + var i, len; + + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + }, + + + // private methods that modify map state + + _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) { + + var zoomChanged = (this._zoom !== zoom); + + if (!afterZoomAnim) { + this.fire('movestart'); + + if (zoomChanged) { + this.fire('zoomstart'); + } + } + + this._zoom = zoom; + + this._initialTopLeftPoint = this._getNewTopLeftPoint(center); + + if (!preserveMapOffset) { + L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0)); + } else { + this._initialTopLeftPoint._add(this._getMapPanePos()); + } + + this._tileLayersToLoad = this._tileLayersNum; + + this.fire('viewreset', {hard: !preserveMapOffset}); + + this.fire('move'); + + if (zoomChanged || afterZoomAnim) { + this.fire('zoomend'); + } + + this.fire('moveend', {hard: !preserveMapOffset}); + + if (!this._loaded) { + this._loaded = true; + this.fire('load'); + } + }, + + _rawPanBy: function (offset) { + L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, + + + // map events + + _initEvents: function () { + if (!L.DomEvent) { return; } + + L.DomEvent.on(this._container, 'click', this._onMouseClick, this); + + var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu'], + i, len; + + for (i = 0, len = events.length; i < len; i++) { + L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this); + } + + if (this.options.trackResize) { + L.DomEvent.on(window, 'resize', this._onResize, this); + } + }, + + _onResize: function () { + L.Util.cancelAnimFrame(this._resizeRequest); + this._resizeRequest = L.Util.requestAnimFrame(this.invalidateSize, this, false, this._container); + }, + + _onMouseClick: function (e) { + if (!this._loaded || (this.dragging && this.dragging.moved())) { return; } + + this.fire('preclick'); + this._fireMouseEvent(e); + }, + + _fireMouseEvent: function (e) { + if (!this._loaded) { return; } + + var type = e.type; + + type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type)); + + if (!this.hasEventListeners(type)) { return; } + + if (type === 'contextmenu') { + L.DomEvent.preventDefault(e); + } + + var containerPoint = this.mouseEventToContainerPoint(e), + layerPoint = this.containerPointToLayerPoint(containerPoint), + latlng = this.layerPointToLatLng(layerPoint); + + this.fire(type, { + latlng: latlng, + layerPoint: layerPoint, + containerPoint: containerPoint, + originalEvent: e + }); + }, + + _onTileLayerLoad: function () { + // TODO super-ugly, refactor!!! + // clear scaled tiles after all new tiles are loaded (for performance) + this._tileLayersToLoad--; + if (this._tileLayersNum && !this._tileLayersToLoad && this._tileBg) { + clearTimeout(this._clearTileBgTimer); + this._clearTileBgTimer = setTimeout(L.Util.bind(this._clearTileBg, this), 500); + } + }, + + + // private methods for getting map state + + _getMapPanePos: function () { + return L.DomUtil.getPosition(this._mapPane); + }, + + _getTopLeftPoint: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + + return this._initialTopLeftPoint.subtract(this._getMapPanePos()); + }, + + _getNewTopLeftPoint: function (center, zoom) { + var viewHalf = this.getSize().divideBy(2); + // TODO round on display, not calculation to increase precision? + return this.project(center, zoom)._subtract(viewHalf)._round(); + }, + + _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) { + var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos()); + return this.project(latlng, newZoom)._subtract(topLeft); + }, + + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize().divideBy(2)); + }, + + _getCenterOffset: function (center) { + return this.latLngToLayerPoint(center).subtract(this._getCenterLayerPoint()); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(); + + return Math.max(min, Math.min(max, zoom)); + } +}); + +L.Map.addInitHook = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initializers.push(init); +}; + +L.map = function (id, options) { + return new L.Map(id, options); +}; + + + +L.Projection.Mercator = { + MAX_LATITUDE: 85.0840591556, + + R_MINOR: 6356752.3142, + R_MAJOR: 6378137, + + project: function (latlng) { // (LatLng) -> Point + var d = L.LatLng.DEG_TO_RAD, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + r = this.R_MAJOR, + r2 = this.R_MINOR, + x = latlng.lng * d * r, + y = lat * d, + tmp = r2 / r, + eccent = Math.sqrt(1.0 - tmp * tmp), + con = eccent * Math.sin(y); + + con = Math.pow((1 - con) / (1 + con), eccent * 0.5); + + var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con; + y = -r2 * Math.log(ts); + + return new L.Point(x, y); + }, + + unproject: function (point) { // (Point, Boolean) -> LatLng + var d = L.LatLng.RAD_TO_DEG, + r = this.R_MAJOR, + r2 = this.R_MINOR, + lng = point.x * d / r, + tmp = r2 / r, + eccent = Math.sqrt(1 - (tmp * tmp)), + ts = Math.exp(- point.y / r2), + phi = (Math.PI / 2) - 2 * Math.atan(ts), + numIter = 15, + tol = 1e-7, + i = numIter, + dphi = 0.1, + con; + + while ((Math.abs(dphi) > tol) && (--i > 0)) { + con = eccent * Math.sin(phi); + dphi = (Math.PI / 2) - 2 * Math.atan(ts * Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi; + phi += dphi; + } + + return new L.LatLng(phi * d, lng, true); + } +}; + + + +L.CRS.EPSG3395 = L.Util.extend({}, L.CRS, { + code: 'EPSG:3395', + + projection: L.Projection.Mercator, + + transformation: (function () { + var m = L.Projection.Mercator, + r = m.R_MAJOR, + r2 = m.R_MINOR; + + return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5); + }()) +}); + + +/* + * L.TileLayer is used for standard xyz-numbered tile layers. + */ + +L.TileLayer = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + minZoom: 0, + maxZoom: 18, + tileSize: 256, + subdomains: 'abc', + errorTileUrl: '', + attribution: '', + zoomOffset: 0, + opacity: 1, + /* (undefined works too) + zIndex: null, + tms: false, + continuousWorld: false, + noWrap: false, + zoomReverse: false, + detectRetina: false, + reuseTiles: false, + */ + unloadInvisibleTiles: L.Browser.mobile, + updateWhenIdle: L.Browser.mobile + }, + + initialize: function (url, options) { + options = L.Util.setOptions(this, options); + + // detecting retina displays, adjusting tileSize and zoom levels + if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) { + + options.tileSize = Math.floor(options.tileSize / 2); + options.zoomOffset++; + + if (options.minZoom > 0) { + options.minZoom--; + } + this.options.maxZoom--; + } + + this._url = url; + + var subdomains = this.options.subdomains; + + if (typeof subdomains === 'string') { + this.options.subdomains = subdomains.split(''); + } + }, + + onAdd: function (map) { + this._map = map; + + // create a container div for tiles + this._initContainer(); + + // create an image to clone for tiles + this._createTileProto(); + + // set up events + map.on({ + 'viewreset': this._resetCallback, + 'moveend': this._update + }, this); + + if (!this.options.updateWhenIdle) { + this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this); + map.on('move', this._limitedUpdate, this); + } + + this._reset(); + this._update(); + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + onRemove: function (map) { + map._panes.tilePane.removeChild(this._container); + + map.off({ + 'viewreset': this._resetCallback, + 'moveend': this._update + }, this); + + if (!this.options.updateWhenIdle) { + map.off('move', this._limitedUpdate, this); + } + + this._container = null; + this._map = null; + }, + + bringToFront: function () { + var pane = this._map._panes.tilePane; + + if (this._container) { + pane.appendChild(this._container); + this._setAutoZIndex(pane, Math.max); + } + + return this; + }, + + bringToBack: function () { + var pane = this._map._panes.tilePane; + + if (this._container) { + pane.insertBefore(this._container, pane.firstChild); + this._setAutoZIndex(pane, Math.min); + } + + return this; + }, + + getAttribution: function () { + return this.options.attribution; + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + setZIndex: function (zIndex) { + this.options.zIndex = zIndex; + this._updateZIndex(); + + return this; + }, + + setUrl: function (url, noRedraw) { + this._url = url; + + if (!noRedraw) { + this.redraw(); + } + + return this; + }, + + redraw: function () { + if (this._map) { + this._map._panes.tilePane.empty = false; + this._reset(true); + this._update(); + } + return this; + }, + + _updateZIndex: function () { + if (this._container && this.options.zIndex !== undefined) { + this._container.style.zIndex = this.options.zIndex; + } + }, + + _setAutoZIndex: function (pane, compare) { + + var layers = pane.getElementsByClassName('leaflet-layer'), + edgeZIndex = -compare(Infinity, -Infinity), // -Ifinity for max, Infinity for min + zIndex; + + for (var i = 0, len = layers.length; i < len; i++) { + + if (layers[i] !== this._container) { + zIndex = parseInt(layers[i].style.zIndex, 10); + + if (!isNaN(zIndex)) { + edgeZIndex = compare(edgeZIndex, zIndex); + } + } + } + + this._container.style.zIndex = isFinite(edgeZIndex) ? edgeZIndex + compare(1, -1) : ''; + }, + + _updateOpacity: function () { + L.DomUtil.setOpacity(this._container, this.options.opacity); + + // stupid webkit hack to force redrawing of tiles + var i, + tiles = this._tiles; + + if (L.Browser.webkit) { + for (i in tiles) { + if (tiles.hasOwnProperty(i)) { + tiles[i].style.webkitTransform += ' translate(0,0)'; + } + } + } + }, + + _initContainer: function () { + var tilePane = this._map._panes.tilePane; + + if (!this._container || tilePane.empty) { + this._container = L.DomUtil.create('div', 'leaflet-layer'); + + this._updateZIndex(); + + tilePane.appendChild(this._container); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + }, + + _resetCallback: function (e) { + this._reset(e.hard); + }, + + _reset: function (clearOldContainer) { + var key, + tiles = this._tiles; + + for (key in tiles) { + if (tiles.hasOwnProperty(key)) { + this.fire('tileunload', {tile: tiles[key]}); + } + } + + this._tiles = {}; + this._tilesToLoad = 0; + + if (this.options.reuseTiles) { + this._unusedTiles = []; + } + + if (clearOldContainer && this._container) { + this._container.innerHTML = ""; + } + + this._initContainer(); + }, + + _update: function (e) { + if (this._map._panTransition && this._map._panTransition._inProgress) { return; } + + var bounds = this._map.getPixelBounds(), + zoom = this._map.getZoom(), + tileSize = this.options.tileSize; + + if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { + return; + } + + var nwTilePoint = new L.Point( + Math.floor(bounds.min.x / tileSize), + Math.floor(bounds.min.y / tileSize)), + seTilePoint = new L.Point( + Math.floor(bounds.max.x / tileSize), + Math.floor(bounds.max.y / tileSize)), + tileBounds = new L.Bounds(nwTilePoint, seTilePoint); + + this._addTilesFromCenterOut(tileBounds); + + if (this.options.unloadInvisibleTiles || this.options.reuseTiles) { + this._removeOtherTiles(tileBounds); + } + }, + + _addTilesFromCenterOut: function (bounds) { + var queue = [], + center = bounds.getCenter(); + + var j, i, point; + + for (j = bounds.min.y; j <= bounds.max.y; j++) { + for (i = bounds.min.x; i <= bounds.max.x; i++) { + point = new L.Point(i, j); + + if (this._tileShouldBeLoaded(point)) { + queue.push(point); + } + } + } + + var tilesToLoad = queue.length; + + if (tilesToLoad === 0) { return; } + + // load tiles in order of their distance to center + queue.sort(function (a, b) { + return a.distanceTo(center) - b.distanceTo(center); + }); + + var fragment = document.createDocumentFragment(); + + // if its the first batch of tiles to load + if (!this._tilesToLoad) { + this.fire('loading'); + } + + this._tilesToLoad += tilesToLoad; + + for (i = 0; i < tilesToLoad; i++) { + this._addTile(queue[i], fragment); + } + + this._container.appendChild(fragment); + }, + + _tileShouldBeLoaded: function (tilePoint) { + if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) { + return false; // already loaded + } + + if (!this.options.continuousWorld) { + var limit = this._getWrapTileNum(); + + if (this.options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit) || + tilePoint.y < 0 || tilePoint.y >= limit) { + return false; // exceeds world bounds + } + } + + return true; + }, + + _removeOtherTiles: function (bounds) { + var kArr, x, y, key; + + for (key in this._tiles) { + if (this._tiles.hasOwnProperty(key)) { + kArr = key.split(':'); + x = parseInt(kArr[0], 10); + y = parseInt(kArr[1], 10); + + // remove tile if it's out of bounds + if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) { + this._removeTile(key); + } + } + } + }, + + _removeTile: function (key) { + var tile = this._tiles[key]; + + this.fire("tileunload", {tile: tile, url: tile.src}); + + if (this.options.reuseTiles) { + L.DomUtil.removeClass(tile, 'leaflet-tile-loaded'); + this._unusedTiles.push(tile); + } else if (tile.parentNode === this._container) { + this._container.removeChild(tile); + } + + if (!L.Browser.android) { //For https://github.com/CloudMade/Leaflet/issues/137 + tile.src = L.Util.emptyImageUrl; + } + + delete this._tiles[key]; + }, + + _addTile: function (tilePoint, container) { + var tilePos = this._getTilePos(tilePoint); + + // get unused tile - or create a new tile + var tile = this._getTile(); + + // Chrome 20 layouts much faster with top/left (Verify with timeline, frames) + // android 4 browser has display issues with top/left and requires transform instead + // android 3 browser not tested + // android 2 browser requires top/left or tiles disappear on load or first drag (reappear after zoom) https://github.com/CloudMade/Leaflet/issues/866 + // (other browsers don't currently care) - see debug/hacks/jitter.html for an example + L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome || L.Browser.android23); + + this._tiles[tilePoint.x + ':' + tilePoint.y] = tile; + + this._loadTile(tile, tilePoint); + + if (tile.parentNode !== this._container) { + container.appendChild(tile); + } + }, + + _getZoomForUrl: function () { + + var options = this.options, + zoom = this._map.getZoom(); + + if (options.zoomReverse) { + zoom = options.maxZoom - zoom; + } + + return zoom + options.zoomOffset; + }, + + _getTilePos: function (tilePoint) { + var origin = this._map.getPixelOrigin(), + tileSize = this.options.tileSize; + + return tilePoint.multiplyBy(tileSize).subtract(origin); + }, + + // image-specific code (override to implement e.g. Canvas or SVG tile layer) + + getTileUrl: function (tilePoint) { + this._adjustTilePoint(tilePoint); + + return L.Util.template(this._url, L.Util.extend({ + s: this._getSubdomain(tilePoint), + z: this._getZoomForUrl(), + x: tilePoint.x, + y: tilePoint.y + }, this.options)); + }, + + _getWrapTileNum: function () { + // TODO refactor, limit is not valid for non-standard projections + return Math.pow(2, this._getZoomForUrl()); + }, + + _adjustTilePoint: function (tilePoint) { + + var limit = this._getWrapTileNum(); + + // wrap tile coordinates + if (!this.options.continuousWorld && !this.options.noWrap) { + tilePoint.x = ((tilePoint.x % limit) + limit) % limit; + } + + if (this.options.tms) { + tilePoint.y = limit - tilePoint.y - 1; + } + }, + + _getSubdomain: function (tilePoint) { + var index = (tilePoint.x + tilePoint.y) % this.options.subdomains.length; + return this.options.subdomains[index]; + }, + + _createTileProto: function () { + var img = this._tileImg = L.DomUtil.create('img', 'leaflet-tile'); + img.galleryimg = 'no'; + + var tileSize = this.options.tileSize; + img.style.width = tileSize + 'px'; + img.style.height = tileSize + 'px'; + }, + + _getTile: function () { + if (this.options.reuseTiles && this._unusedTiles.length > 0) { + var tile = this._unusedTiles.pop(); + this._resetTile(tile); + return tile; + } + return this._createTile(); + }, + + _resetTile: function (tile) { + // Override if data stored on a tile needs to be cleaned up before reuse + }, + + _createTile: function () { + var tile = this._tileImg.cloneNode(false); + tile.onselectstart = tile.onmousemove = L.Util.falseFn; + return tile; + }, + + _loadTile: function (tile, tilePoint) { + tile._layer = this; + tile.onload = this._tileOnLoad; + tile.onerror = this._tileOnError; + + tile.src = this.getTileUrl(tilePoint); + }, + + _tileLoaded: function () { + this._tilesToLoad--; + if (!this._tilesToLoad) { + this.fire('load'); + } + }, + + _tileOnLoad: function (e) { + var layer = this._layer; + + //Only if we are loading an actual image + if (this.src !== L.Util.emptyImageUrl) { + L.DomUtil.addClass(this, 'leaflet-tile-loaded'); + + layer.fire('tileload', { + tile: this, + url: this.src + }); + } + + layer._tileLoaded(); + }, + + _tileOnError: function (e) { + var layer = this._layer; + + layer.fire('tileerror', { + tile: this, + url: this.src + }); + + var newUrl = layer.options.errorTileUrl; + if (newUrl) { + this.src = newUrl; + } + + layer._tileLoaded(); + } +}); + +L.tileLayer = function (url, options) { + return new L.TileLayer(url, options); +}; + + +L.TileLayer.WMS = L.TileLayer.extend({ + + defaultWmsParams: { + service: 'WMS', + request: 'GetMap', + version: '1.1.1', + layers: '', + styles: '', + format: 'image/jpeg', + transparent: false + }, + + initialize: function (url, options) { // (String, Object) + + this._url = url; + + var wmsParams = L.Util.extend({}, this.defaultWmsParams); + + if (options.detectRetina && L.Browser.retina) { + wmsParams.width = wmsParams.height = this.options.tileSize * 2; + } else { + wmsParams.width = wmsParams.height = this.options.tileSize; + } + + for (var i in options) { + // all keys that are not TileLayer options go to WMS params + if (!this.options.hasOwnProperty(i)) { + wmsParams[i] = options[i]; + } + } + + this.wmsParams = wmsParams; + + L.Util.setOptions(this, options); + }, + + onAdd: function (map) { + + var projectionKey = parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs'; + this.wmsParams[projectionKey] = map.options.crs.code; + + L.TileLayer.prototype.onAdd.call(this, map); + }, + + getTileUrl: function (tilePoint, zoom) { // (Point, Number) -> String + + var map = this._map, + crs = map.options.crs, + tileSize = this.options.tileSize, + + nwPoint = tilePoint.multiplyBy(tileSize), + sePoint = nwPoint.add(new L.Point(tileSize, tileSize)), + + nw = crs.project(map.unproject(nwPoint, zoom)), + se = crs.project(map.unproject(sePoint, zoom)), + + bbox = [nw.x, se.y, se.x, nw.y].join(','), + + url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)}); + + return url + L.Util.getParamString(this.wmsParams) + "&bbox=" + bbox; + }, + + setParams: function (params, noRedraw) { + + L.Util.extend(this.wmsParams, params); + + if (!noRedraw) { + this.redraw(); + } + + return this; + } +}); + +L.tileLayer.wms = function (url, options) { + return new L.TileLayer.WMS(url, options); +}; + + +L.TileLayer.Canvas = L.TileLayer.extend({ + options: { + async: false + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + }, + + redraw: function () { + var i, + tiles = this._tiles; + + for (i in tiles) { + if (tiles.hasOwnProperty(i)) { + this._redrawTile(tiles[i]); + } + } + }, + + _redrawTile: function (tile) { + this.drawTile(tile, tile._tilePoint, tile._zoom); + }, + + _createTileProto: function () { + var proto = this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile'); + + var tileSize = this.options.tileSize; + proto.width = tileSize; + proto.height = tileSize; + }, + + _createTile: function () { + var tile = this._canvasProto.cloneNode(false); + tile.onselectstart = tile.onmousemove = L.Util.falseFn; + return tile; + }, + + _loadTile: function (tile, tilePoint, zoom) { + tile._layer = this; + tile._tilePoint = tilePoint; + tile._zoom = zoom; + + this.drawTile(tile, tilePoint, zoom); + + if (!this.options.async) { + this.tileDrawn(tile); + } + }, + + drawTile: function (tile, tilePoint, zoom) { + // override with rendering code + }, + + tileDrawn: function (tile) { + this._tileOnLoad.call(tile); + } +}); + + +L.tileLayer.canvas = function (options) { + return new L.TileLayer.Canvas(options); +}; + +L.ImageOverlay = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + opacity: 1 + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = L.latLngBounds(bounds); + + L.Util.setOptions(this, options); + }, + + onAdd: function (map) { + this._map = map; + + if (!this._image) { + this._initImage(); + } + + map._panes.overlayPane.appendChild(this._image); + + map.on('viewreset', this._reset, this); + + if (map.options.zoomAnimation && L.Browser.any3d) { + map.on('zoomanim', this._animateZoom, this); + } + + this._reset(); + }, + + onRemove: function (map) { + map.getPanes().overlayPane.removeChild(this._image); + + map.off('viewreset', this._reset, this); + + if (map.options.zoomAnimation) { + map.off('zoomanim', this._animateZoom, this); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + this._updateOpacity(); + return this; + }, + + // TODO remove bringToFront/bringToBack duplication from TileLayer/Path + bringToFront: function () { + if (this._image) { + this._map._panes.overlayPane.appendChild(this._image); + } + return this; + }, + + bringToBack: function () { + var pane = this._map._panes.overlayPane; + if (this._image) { + pane.insertBefore(this._image, pane.firstChild); + } + return this; + }, + + _initImage: function () { + this._image = L.DomUtil.create('img', 'leaflet-image-layer'); + + if (this._map.options.zoomAnimation && L.Browser.any3d) { + L.DomUtil.addClass(this._image, 'leaflet-zoom-animated'); + } else { + L.DomUtil.addClass(this._image, 'leaflet-zoom-hide'); + } + + this._updateOpacity(); + + //TODO createImage util method to remove duplication + L.Util.extend(this._image, { + galleryimg: 'no', + onselectstart: L.Util.falseFn, + onmousemove: L.Util.falseFn, + onload: L.Util.bind(this._onImageLoad, this), + src: this._url + }); + }, + + _animateZoom: function (e) { + var map = this._map, + image = this._image, + scale = map.getZoomScale(e.zoom), + nw = this._bounds.getNorthWest(), + se = this._bounds.getSouthEast(), + topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center), + size = map._latLngToNewLayerPoint(se, e.zoom, e.center).subtract(topLeft), + currentSize = map.latLngToLayerPoint(se).subtract(map.latLngToLayerPoint(nw)), + origin = topLeft.add(size.subtract(currentSize).divideBy(2)); + + image.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') '; + }, + + _reset: function () { + var image = this._image, + topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + size = this._map.latLngToLayerPoint(this._bounds.getSouthEast()).subtract(topLeft); + + L.DomUtil.setPosition(image, topLeft); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _onImageLoad: function () { + this.fire('load'); + }, + + _updateOpacity: function () { + L.DomUtil.setOpacity(this._image, this.options.opacity); + } +}); + +L.imageOverlay = function (url, bounds, options) { + return new L.ImageOverlay(url, bounds, options); +}; + + +L.Icon = L.Class.extend({ + options: { + /* + iconUrl: (String) (required) + iconSize: (Point) (can be set through CSS) + iconAnchor: (Point) (centered by default if size is specified, can be set in CSS with negative margins) + popupAnchor: (Point) (if not specified, popup opens in the anchor point) + shadowUrl: (Point) (no shadow by default) + shadowSize: (Point) + shadowAnchor: (Point) + */ + className: '' + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + }, + + createIcon: function () { + return this._createIcon('icon'); + }, + + createShadow: function () { + return this._createIcon('shadow'); + }, + + _createIcon: function (name) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error("iconUrl not set in Icon options (see the docs)."); + } + return null; + } + + var img = this._createImg(src); + this._setIconStyles(img, name); + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options, + size = L.point(options[name + 'Size']), + anchor; + + if (name === 'shadow') { + anchor = L.point(options.shadowAnchor || options.iconAnchor); + } else { + anchor = L.point(options.iconAnchor); + } + + if (!anchor && size) { + anchor = size.divideBy(2, true); + } + + img.className = 'leaflet-marker-' + name + ' ' + options.className; + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + _createImg: function (src) { + var el; + + if (!L.Browser.ie6) { + el = document.createElement('img'); + el.src = src; + } else { + el = document.createElement('div'); + el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")'; + } + return el; + }, + + _getIconUrl: function (name) { + return this.options[name + 'Url']; + } +}); + +L.icon = function (options) { + return new L.Icon(options); +}; + + + +L.Icon.Default = L.Icon.extend({ + + options: { + iconSize: new L.Point(25, 41), + iconAnchor: new L.Point(13, 41), + popupAnchor: new L.Point(1, -34), + + shadowSize: new L.Point(41, 41) + }, + + _getIconUrl: function (name) { + var key = name + 'Url'; + + if (this.options[key]) { + return this.options[key]; + } + + var path = L.Icon.Default.imagePath; + + if (!path) { + throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually."); + } + + return path + '/marker-' + name + '.png'; + } +}); + +L.Icon.Default.imagePath = (function () { + var scripts = document.getElementsByTagName('script'), + leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/; + + var i, len, src, matches; + + for (i = 0, len = scripts.length; i < len; i++) { + src = scripts[i].src; + matches = src.match(leafletRe); + + if (matches) { + return src.split(leafletRe)[0] + '/images'; + } + } +}()); + + +/* + * L.Marker is used to display clickable/draggable icons on the map. + */ + +L.Marker = L.Class.extend({ + + includes: L.Mixin.Events, + + options: { + icon: new L.Icon.Default(), + title: '', + clickable: true, + draggable: false, + zIndexOffset: 0, + opacity: 1 + }, + + initialize: function (latlng, options) { + L.Util.setOptions(this, options); + this._latlng = L.latLng(latlng); + }, + + onAdd: function (map) { + this._map = map; + + map.on('viewreset', this.update, this); + + this._initIcon(); + this.update(); + + if (map.options.zoomAnimation && map.options.markerZoomAnimation) { + map.on('zoomanim', this._animateZoom, this); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + onRemove: function (map) { + this._removeIcon(); + + // TODO move to Marker.Popup.js + if (this.closePopup) { + this.closePopup(); + } + + map.off({ + 'viewreset': this.update, + 'zoomanim': this._animateZoom + }, this); + + this._map = null; + }, + + getLatLng: function () { + return this._latlng; + }, + + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + + this.update(); + + if (this._popup) { + this._popup.setLatLng(latlng); + } + }, + + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + this.update(); + }, + + setIcon: function (icon) { + if (this._map) { + this._removeIcon(); + } + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + }, + + update: function () { + if (!this._icon) { return; } + + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + }, + + _initIcon: function () { + var options = this.options, + map = this._map, + animation = (map.options.zoomAnimation && map.options.markerZoomAnimation), + classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide', + needOpacityUpdate = false; + + if (!this._icon) { + this._icon = options.icon.createIcon(); + + if (options.title) { + this._icon.title = options.title; + } + + this._initInteraction(); + needOpacityUpdate = (this.options.opacity < 1); + + L.DomUtil.addClass(this._icon, classToAdd); + } + if (!this._shadow) { + this._shadow = options.icon.createShadow(); + + if (this._shadow) { + L.DomUtil.addClass(this._shadow, classToAdd); + needOpacityUpdate = (this.options.opacity < 1); + } + } + + if (needOpacityUpdate) { + this._updateOpacity(); + } + + var panes = this._map._panes; + + panes.markerPane.appendChild(this._icon); + + if (this._shadow) { + panes.shadowPane.appendChild(this._shadow); + } + }, + + _removeIcon: function () { + var panes = this._map._panes; + + panes.markerPane.removeChild(this._icon); + + if (this._shadow) { + panes.shadowPane.removeChild(this._shadow); + } + + this._icon = this._shadow = null; + }, + + _setPos: function (pos) { + L.DomUtil.setPosition(this._icon, pos); + + if (this._shadow) { + L.DomUtil.setPosition(this._shadow, pos); + } + + this._icon.style.zIndex = pos.y + this.options.zIndexOffset; + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); + + this._setPos(pos); + }, + + _initInteraction: function () { + if (!this.options.clickable) { + return; + } + + var icon = this._icon, + events = ['dblclick', 'mousedown', 'mouseover', 'mouseout']; + + L.DomUtil.addClass(icon, 'leaflet-clickable'); + L.DomEvent.on(icon, 'click', this._onMouseClick, this); + + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(icon, events[i], this._fireMouseEvent, this); + } + + if (L.Handler.MarkerDrag) { + this.dragging = new L.Handler.MarkerDrag(this); + + if (this.options.draggable) { + this.dragging.enable(); + } + } + }, + + _onMouseClick: function (e) { + L.DomEvent.stopPropagation(e); + if (this.dragging && this.dragging.moved()) { return; } + if (this._map.dragging && this._map.dragging.moved()) { return; } + this.fire(e.type, { + originalEvent: e + }); + }, + + _fireMouseEvent: function (e) { + this.fire(e.type, { + originalEvent: e + }); + if (e.type !== 'mousedown') { + L.DomEvent.stopPropagation(e); + } + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + }, + + _updateOpacity: function () { + L.DomUtil.setOpacity(this._icon, this.options.opacity); + if (this._shadow) { + L.DomUtil.setOpacity(this._shadow, this.options.opacity); + } + } +}); + +L.marker = function (latlng, options) { + return new L.Marker(latlng, options); +}; + + +L.DivIcon = L.Icon.extend({ + options: { + iconSize: new L.Point(12, 12), // also can be set through CSS + /* + iconAnchor: (Point) + popupAnchor: (Point) + html: (String) + bgPos: (Point) + */ + className: 'leaflet-div-icon' + }, + + createIcon: function () { + var div = document.createElement('div'), + options = this.options; + + if (options.html) { + div.innerHTML = options.html; + } + + if (options.bgPos) { + div.style.backgroundPosition = + (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px'; + } + + this._setIconStyles(div, 'icon'); + return div; + }, + + createShadow: function () { + return null; + } +}); + +L.divIcon = function (options) { + return new L.DivIcon(options); +}; + + + +L.Map.mergeOptions({ + closePopupOnClick: true +}); + +L.Popup = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + minWidth: 50, + maxWidth: 300, + maxHeight: null, + autoPan: true, + closeButton: true, + offset: new L.Point(0, 6), + autoPanPadding: new L.Point(5, 5), + className: '' + }, + + initialize: function (options, source) { + L.Util.setOptions(this, options); + + this._source = source; + }, + + onAdd: function (map) { + this._map = map; + + if (!this._container) { + this._initLayout(); + } + this._updateContent(); + + var animFade = map.options.fadeAnimation; + + if (animFade) { + L.DomUtil.setOpacity(this._container, 0); + } + map._panes.popupPane.appendChild(this._container); + + map.on('viewreset', this._updatePosition, this); + + if (L.Browser.any3d) { + map.on('zoomanim', this._zoomAnimation, this); + } + + if (map.options.closePopupOnClick) { + map.on('preclick', this._close, this); + } + + this._update(); + + if (animFade) { + L.DomUtil.setOpacity(this._container, 1); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + openOn: function (map) { + map.openPopup(this); + return this; + }, + + onRemove: function (map) { + map._panes.popupPane.removeChild(this._container); + + L.Util.falseFn(this._container.offsetWidth); // force reflow + + map.off({ + viewreset: this._updatePosition, + preclick: this._close, + zoomanim: this._zoomAnimation + }, this); + + if (map.options.fadeAnimation) { + L.DomUtil.setOpacity(this._container, 0); + } + + this._map = null; + }, + + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + this._update(); + return this; + }, + + setContent: function (content) { + this._content = content; + this._update(); + return this; + }, + + _close: function () { + var map = this._map; + + if (map) { + map._popup = null; + + map + .removeLayer(this) + .fire('popupclose', {popup: this}); + } + }, + + _initLayout: function () { + var prefix = 'leaflet-popup', + container = this._container = L.DomUtil.create('div', prefix + ' ' + this.options.className + ' leaflet-zoom-animated'), + closeButton; + + if (this.options.closeButton) { + closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container); + closeButton.href = '#close'; + closeButton.innerHTML = '×'; + + L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this); + } + + var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container); + L.DomEvent.disableClickPropagation(wrapper); + + this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper); + L.DomEvent.on(this._contentNode, 'mousewheel', L.DomEvent.stopPropagation); + + this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container); + this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer); + }, + + _update: function () { + if (!this._map) { return; } + + this._container.style.visibility = 'hidden'; + + this._updateContent(); + this._updateLayout(); + this._updatePosition(); + + this._container.style.visibility = ''; + + this._adjustPan(); + }, + + _updateContent: function () { + if (!this._content) { return; } + + if (typeof this._content === 'string') { + this._contentNode.innerHTML = this._content; + } else { + while (this._contentNode.hasChildNodes()) { + this._contentNode.removeChild(this._contentNode.firstChild); + } + this._contentNode.appendChild(this._content); + } + this.fire('contentupdate'); + }, + + _updateLayout: function () { + var container = this._contentNode, + style = container.style; + + style.width = ''; + style.whiteSpace = 'nowrap'; + + var width = container.offsetWidth; + width = Math.min(width, this.options.maxWidth); + width = Math.max(width, this.options.minWidth); + + style.width = (width + 1) + 'px'; + style.whiteSpace = ''; + + style.height = ''; + + var height = container.offsetHeight, + maxHeight = this.options.maxHeight, + scrolledClass = 'leaflet-popup-scrolled'; + + if (maxHeight && height > maxHeight) { + style.height = maxHeight + 'px'; + L.DomUtil.addClass(container, scrolledClass); + } else { + L.DomUtil.removeClass(container, scrolledClass); + } + + this._containerWidth = this._container.offsetWidth; + }, + + _updatePosition: function () { + var pos = this._map.latLngToLayerPoint(this._latlng), + is3d = L.Browser.any3d, + offset = this.options.offset; + + if (is3d) { + L.DomUtil.setPosition(this._container, pos); + } + + this._containerBottom = -offset.y - (is3d ? 0 : pos.y); + this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (is3d ? 0 : pos.x); + + //Bottom position the popup in case the height of the popup changes (images loading etc) + this._container.style.bottom = this._containerBottom + 'px'; + this._container.style.left = this._containerLeft + 'px'; + }, + + _zoomAnimation: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); + + L.DomUtil.setPosition(this._container, pos); + }, + + _adjustPan: function () { + if (!this.options.autoPan) { return; } + + var map = this._map, + containerHeight = this._container.offsetHeight, + containerWidth = this._containerWidth, + + layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); + + if (L.Browser.any3d) { + layerPos._add(L.DomUtil.getPosition(this._container)); + } + + var containerPos = map.layerPointToContainerPoint(layerPos), + padding = this.options.autoPanPadding, + size = map.getSize(), + dx = 0, + dy = 0; + + if (containerPos.x < 0) { + dx = containerPos.x - padding.x; + } + if (containerPos.x + containerWidth > size.x) { + dx = containerPos.x + containerWidth - size.x + padding.x; + } + if (containerPos.y < 0) { + dy = containerPos.y - padding.y; + } + if (containerPos.y + containerHeight > size.y) { + dy = containerPos.y + containerHeight - size.y + padding.y; + } + + if (dx || dy) { + map.panBy(new L.Point(dx, dy)); + } + }, + + _onCloseButtonClick: function (e) { + this._close(); + L.DomEvent.stop(e); + } +}); + +L.popup = function (options, source) { + return new L.Popup(options, source); +}; + + +/* + * Popup extension to L.Marker, adding openPopup & bindPopup methods. + */ + +L.Marker.include({ + openPopup: function () { + if (this._popup && this._map) { + this._popup.setLatLng(this._latlng); + this._map.openPopup(this._popup); + } + + return this; + }, + + closePopup: function () { + if (this._popup) { + this._popup._close(); + } + return this; + }, + + bindPopup: function (content, options) { + var anchor = L.point(this.options.icon.options.popupAnchor) || new L.Point(0, 0); + + anchor = anchor.add(L.Popup.prototype.options.offset); + + if (options && options.offset) { + anchor = anchor.add(options.offset); + } + + options = L.Util.extend({offset: anchor}, options); + + if (!this._popup) { + this.on('click', this.openPopup, this); + } + + this._popup = new L.Popup(options, this) + .setContent(content); + + return this; + }, + + unbindPopup: function () { + if (this._popup) { + this._popup = null; + this.off('click', this.openPopup); + } + return this; + } +}); + + + +L.Map.include({ + openPopup: function (popup) { + this.closePopup(); + + this._popup = popup; + + return this + .addLayer(popup) + .fire('popupopen', {popup: this._popup}); + }, + + closePopup: function () { + if (this._popup) { + this._popup._close(); + } + return this; + } +}); + +/* + * L.LayerGroup is a class to combine several layers so you can manipulate the group (e.g. add/remove it) as one layer. + */ + +L.LayerGroup = L.Class.extend({ + initialize: function (layers) { + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + addLayer: function (layer) { + var id = L.Util.stamp(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + removeLayer: function (layer) { + var id = L.Util.stamp(layer); + + delete this._layers[id]; + + if (this._map) { + this._map.removeLayer(layer); + } + + return this; + }, + + clearLayers: function () { + this.eachLayer(this.removeLayer, this); + return this; + }, + + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + if (this._layers.hasOwnProperty(i)) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + } + + return this; + }, + + onAdd: function (map) { + this._map = map; + this.eachLayer(map.addLayer, map); + }, + + onRemove: function (map) { + this.eachLayer(map.removeLayer, map); + this._map = null; + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + eachLayer: function (method, context) { + for (var i in this._layers) { + if (this._layers.hasOwnProperty(i)) { + method.call(context, this._layers[i]); + } + } + } +}); + +L.layerGroup = function (layers) { + return new L.LayerGroup(layers); +}; + + +/* + * L.FeatureGroup extends L.LayerGroup by introducing mouse events and bindPopup method shared between a group of layers. + */ + +L.FeatureGroup = L.LayerGroup.extend({ + includes: L.Mixin.Events, + + addLayer: function (layer) { + if (this._layers[L.Util.stamp(layer)]) { + return this; + } + + layer.on('click dblclick mouseover mouseout mousemove contextmenu', this._propagateEvent, this); + + L.LayerGroup.prototype.addLayer.call(this, layer); + + if (this._popupContent && layer.bindPopup) { + layer.bindPopup(this._popupContent); + } + + return this; + }, + + removeLayer: function (layer) { + layer.off('click dblclick mouseover mouseout mousemove contextmenu', this._propagateEvent, this); + + L.LayerGroup.prototype.removeLayer.call(this, layer); + + if (this._popupContent) { + return this.invoke('unbindPopup'); + } else { + return this; + } + }, + + bindPopup: function (content) { + this._popupContent = content; + return this.invoke('bindPopup', content); + }, + + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + getBounds: function () { + var bounds = new L.LatLngBounds(); + this.eachLayer(function (layer) { + bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds()); + }, this); + return bounds; + }, + + _propagateEvent: function (e) { + e.layer = e.target; + e.target = this; + + this.fire(e.type, e); + } +}); + +L.featureGroup = function (layers) { + return new L.FeatureGroup(layers); +}; + + +/* + * L.Path is a base class for rendering vector paths on a map. It's inherited by Polyline, Circle, etc. + */ + +L.Path = L.Class.extend({ + includes: [L.Mixin.Events], + + statics: { + // how much to extend the clip area around the map view + // (relative to its size, e.g. 0.5 is half the screen in each direction) + // set in such way that SVG element doesn't exceed 1280px (vector layers flicker on dragend if it is) + CLIP_PADDING: L.Browser.mobile ? + Math.max(0, Math.min(0.5, + (1280 / Math.max(window.innerWidth, window.innerHeight) - 1) / 2)) + : 0.5 + }, + + options: { + stroke: true, + color: '#0033ff', + dashArray: null, + weight: 5, + opacity: 0.5, + + fill: false, + fillColor: null, //same as color by default + fillOpacity: 0.2, + + clickable: true + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + }, + + onAdd: function (map) { + this._map = map; + + if (!this._container) { + this._initElements(); + this._initEvents(); + } + + this.projectLatlngs(); + this._updatePath(); + + if (this._container) { + this._map._pathRoot.appendChild(this._container); + } + + map.on({ + 'viewreset': this.projectLatlngs, + 'moveend': this._updatePath + }, this); + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + onRemove: function (map) { + map._pathRoot.removeChild(this._container); + + this._map = null; + + if (L.Browser.vml) { + this._container = null; + this._stroke = null; + this._fill = null; + } + + map.off({ + 'viewreset': this.projectLatlngs, + 'moveend': this._updatePath + }, this); + }, + + projectLatlngs: function () { + // do all projection stuff here + }, + + setStyle: function (style) { + L.Util.setOptions(this, style); + + if (this._container) { + this._updateStyle(); + } + + return this; + }, + + redraw: function () { + if (this._map) { + this.projectLatlngs(); + this._updatePath(); + } + return this; + } +}); + +L.Map.include({ + _updatePathViewport: function () { + var p = L.Path.CLIP_PADDING, + size = this.getSize(), + panePos = L.DomUtil.getPosition(this._mapPane), + min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)), + max = min.add(size.multiplyBy(1 + p * 2)); + + this._pathViewport = new L.Bounds(min, max); + } +}); + + +L.Path.SVG_NS = 'http://www.w3.org/2000/svg'; + +L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect); + +L.Path = L.Path.extend({ + statics: { + SVG: L.Browser.svg + }, + + bringToFront: function () { + if (this._container) { + this._map._pathRoot.appendChild(this._container); + } + return this; + }, + + bringToBack: function () { + if (this._container) { + var root = this._map._pathRoot; + root.insertBefore(this._container, root.firstChild); + } + return this; + }, + + getPathString: function () { + // form path string here + }, + + _createElement: function (name) { + return document.createElementNS(L.Path.SVG_NS, name); + }, + + _initElements: function () { + this._map._initPathRoot(); + this._initPath(); + this._initStyle(); + }, + + _initPath: function () { + this._container = this._createElement('g'); + + this._path = this._createElement('path'); + this._container.appendChild(this._path); + }, + + _initStyle: function () { + if (this.options.stroke) { + this._path.setAttribute('stroke-linejoin', 'round'); + this._path.setAttribute('stroke-linecap', 'round'); + } + if (this.options.fill) { + this._path.setAttribute('fill-rule', 'evenodd'); + } + this._updateStyle(); + }, + + _updateStyle: function () { + if (this.options.stroke) { + this._path.setAttribute('stroke', this.options.color); + this._path.setAttribute('stroke-opacity', this.options.opacity); + this._path.setAttribute('stroke-width', this.options.weight); + if (this.options.dashArray) { + this._path.setAttribute('stroke-dasharray', this.options.dashArray); + } else { + this._path.removeAttribute('stroke-dasharray'); + } + } else { + this._path.setAttribute('stroke', 'none'); + } + if (this.options.fill) { + this._path.setAttribute('fill', this.options.fillColor || this.options.color); + this._path.setAttribute('fill-opacity', this.options.fillOpacity); + } else { + this._path.setAttribute('fill', 'none'); + } + }, + + _updatePath: function () { + var str = this.getPathString(); + if (!str) { + // fix webkit empty string parsing bug + str = 'M0 0'; + } + this._path.setAttribute('d', str); + }, + + // TODO remove duplication with L.Map + _initEvents: function () { + if (this.options.clickable) { + if (L.Browser.svg || !L.Browser.vml) { + this._path.setAttribute('class', 'leaflet-clickable'); + } + + L.DomEvent.on(this._container, 'click', this._onMouseClick, this); + + var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'mousemove', 'contextmenu']; + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this); + } + } + }, + + _onMouseClick: function (e) { + if (this._map.dragging && this._map.dragging.moved()) { + return; + } + + this._fireMouseEvent(e); + + L.DomEvent.stopPropagation(e); + }, + + _fireMouseEvent: function (e) { + if (!this.hasEventListeners(e.type)) { + return; + } + + if (e.type === 'contextmenu') { + L.DomEvent.preventDefault(e); + } + + var map = this._map, + containerPoint = map.mouseEventToContainerPoint(e), + layerPoint = map.containerPointToLayerPoint(containerPoint), + latlng = map.layerPointToLatLng(layerPoint); + + this.fire(e.type, { + latlng: latlng, + layerPoint: layerPoint, + containerPoint: containerPoint, + originalEvent: e + }); + } +}); + +L.Map.include({ + _initPathRoot: function () { + if (!this._pathRoot) { + this._pathRoot = L.Path.prototype._createElement('svg'); + this._panes.overlayPane.appendChild(this._pathRoot); + + if (this.options.zoomAnimation && L.Browser.any3d) { + this._pathRoot.setAttribute('class', ' leaflet-zoom-animated'); + + this.on({ + 'zoomanim': this._animatePathZoom, + 'zoomend': this._endPathZoom + }); + } else { + this._pathRoot.setAttribute('class', ' leaflet-zoom-hide'); + } + + this.on('moveend', this._updateSvgViewport); + this._updateSvgViewport(); + } + }, + + _animatePathZoom: function (opt) { + var scale = this.getZoomScale(opt.zoom), + offset = this._getCenterOffset(opt.center).divideBy(1 - 1 / scale), + viewportPos = this.containerPointToLayerPoint(this.getSize().multiplyBy(-L.Path.CLIP_PADDING)), + origin = viewportPos.add(offset).round(); + + this._pathRoot.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString((origin.multiplyBy(-1).add(L.DomUtil.getPosition(this._pathRoot)).multiplyBy(scale).add(origin))) + ' scale(' + scale + ') '; + + this._pathZooming = true; + }, + + _endPathZoom: function () { + this._pathZooming = false; + }, + + _updateSvgViewport: function () { + if (this._pathZooming) { + // Do not update SVGs while a zoom animation is going on otherwise the animation will break. + // When the zoom animation ends we will be updated again anyway + // This fixes the case where you do a momentum move and zoom while the move is still ongoing. + return; + } + + this._updatePathViewport(); + + var vp = this._pathViewport, + min = vp.min, + max = vp.max, + width = max.x - min.x, + height = max.y - min.y, + root = this._pathRoot, + pane = this._panes.overlayPane; + + // Hack to make flicker on drag end on mobile webkit less irritating + if (L.Browser.mobileWebkit) { + pane.removeChild(root); + } + + L.DomUtil.setPosition(root, min); + root.setAttribute('width', width); + root.setAttribute('height', height); + root.setAttribute('viewBox', [min.x, min.y, width, height].join(' ')); + + if (L.Browser.mobileWebkit) { + pane.appendChild(root); + } + } +}); + + +/* + * Popup extension to L.Path (polylines, polygons, circles), adding bindPopup method. + */ + +L.Path.include({ + + bindPopup: function (content, options) { + + if (!this._popup || this._popup.options !== options) { + this._popup = new L.Popup(options, this); + } + + this._popup.setContent(content); + + if (!this._openPopupAdded) { + this.on('click', this._openPopup, this); + this._openPopupAdded = true; + } + + return this; + }, + + openPopup: function (latlng) { + + if (this._popup) { + latlng = latlng || this._latlng || + this._latlngs[Math.floor(this._latlngs.length / 2)]; + + this._openPopup({latlng: latlng}); + } + + return this; + }, + + _openPopup: function (e) { + this._popup.setLatLng(e.latlng); + this._map.openPopup(this._popup); + } +}); + + +/* + * Vector rendering for IE6-8 through VML. + * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! + */ + +L.Browser.vml = (function () { + try { + var div = document.createElement('div'); + div.innerHTML = ''; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + } catch (e) { + return false; + } +}()); + +L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ + statics: { + VML: true, + CLIP_PADDING: 0.02 + }, + + _createElement: (function () { + try { + document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); + return function (name) { + return document.createElement(''); + }; + } catch (e) { + return function (name) { + return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); + }; + } + }()), + + _initPath: function () { + var container = this._container = this._createElement('shape'); + L.DomUtil.addClass(container, 'leaflet-vml-shape'); + if (this.options.clickable) { + L.DomUtil.addClass(container, 'leaflet-clickable'); + } + container.coordsize = '1 1'; + + this._path = this._createElement('path'); + container.appendChild(this._path); + + this._map._pathRoot.appendChild(container); + }, + + _initStyle: function () { + this._updateStyle(); + }, + + _updateStyle: function () { + var stroke = this._stroke, + fill = this._fill, + options = this.options, + container = this._container; + + container.stroked = options.stroke; + container.filled = options.fill; + + if (options.stroke) { + if (!stroke) { + stroke = this._stroke = this._createElement('stroke'); + stroke.endcap = 'round'; + container.appendChild(stroke); + } + stroke.weight = options.weight + 'px'; + stroke.color = options.color; + stroke.opacity = options.opacity; + if (options.dashArray) { + stroke.dashStyle = options.dashArray.replace(/ *, */g, ' '); + } else { + stroke.dashStyle = ''; + } + } else if (stroke) { + container.removeChild(stroke); + this._stroke = null; + } + + if (options.fill) { + if (!fill) { + fill = this._fill = this._createElement('fill'); + container.appendChild(fill); + } + fill.color = options.fillColor || options.color; + fill.opacity = options.fillOpacity; + } else if (fill) { + container.removeChild(fill); + this._fill = null; + } + }, + + _updatePath: function () { + var style = this._container.style; + + style.display = 'none'; + this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug + style.display = ''; + } +}); + +L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : { + _initPathRoot: function () { + if (this._pathRoot) { return; } + + var root = this._pathRoot = document.createElement('div'); + root.className = 'leaflet-vml-container'; + this._panes.overlayPane.appendChild(root); + + this.on('moveend', this._updatePathViewport); + this._updatePathViewport(); + } +}); + + +/* + * Vector rendering for all browsers that support canvas. + */ + +L.Browser.canvas = (function () { + return !!document.createElement('canvas').getContext; +}()); + +L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({ + statics: { + //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value + CANVAS: true, + SVG: false + }, + + redraw: function () { + if (this._map) { + this.projectLatlngs(); + this._requestUpdate(); + } + return this; + }, + + setStyle: function (style) { + L.Util.setOptions(this, style); + + if (this._map) { + this._updateStyle(); + this._requestUpdate(); + } + return this; + }, + + onRemove: function (map) { + map + .off('viewreset', this.projectLatlngs, this) + .off('moveend', this._updatePath, this); + + this._requestUpdate(); + + this._map = null; + }, + + _requestUpdate: function () { + if (this._map) { + L.Util.cancelAnimFrame(this._fireMapMoveEnd); + this._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map); + } + }, + + _fireMapMoveEnd: function () { + this.fire('moveend'); + }, + + _initElements: function () { + this._map._initPathRoot(); + this._ctx = this._map._canvasCtx; + }, + + _updateStyle: function () { + var options = this.options; + + if (options.stroke) { + this._ctx.lineWidth = options.weight; + this._ctx.strokeStyle = options.color; + } + if (options.fill) { + this._ctx.fillStyle = options.fillColor || options.color; + } + }, + + _drawPath: function () { + var i, j, len, len2, point, drawMethod; + + this._ctx.beginPath(); + + for (i = 0, len = this._parts.length; i < len; i++) { + for (j = 0, len2 = this._parts[i].length; j < len2; j++) { + point = this._parts[i][j]; + drawMethod = (j === 0 ? 'move' : 'line') + 'To'; + + this._ctx[drawMethod](point.x, point.y); + } + // TODO refactor ugly hack + if (this instanceof L.Polygon) { + this._ctx.closePath(); + } + } + }, + + _checkIfEmpty: function () { + return !this._parts.length; + }, + + _updatePath: function () { + if (this._checkIfEmpty()) { return; } + + var ctx = this._ctx, + options = this.options; + + this._drawPath(); + ctx.save(); + this._updateStyle(); + + if (options.fill) { + if (options.fillOpacity < 1) { + ctx.globalAlpha = options.fillOpacity; + } + ctx.fill(); + } + + if (options.stroke) { + if (options.opacity < 1) { + ctx.globalAlpha = options.opacity; + } + ctx.stroke(); + } + + ctx.restore(); + + // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature + }, + + _initEvents: function () { + if (this.options.clickable) { + // TODO hand cursor + // TODO mouseover, mouseout, dblclick + this._map.on('click', this._onClick, this); + } + }, + + _onClick: function (e) { + if (this._containsPoint(e.layerPoint)) { + this.fire('click', e); + } + } +}); + +L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : { + _initPathRoot: function () { + var root = this._pathRoot, + ctx; + + if (!root) { + root = this._pathRoot = document.createElement("canvas"); + root.style.position = 'absolute'; + ctx = this._canvasCtx = root.getContext('2d'); + + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + + this._panes.overlayPane.appendChild(root); + + if (this.options.zoomAnimation) { + this._pathRoot.className = 'leaflet-zoom-animated'; + this.on('zoomanim', this._animatePathZoom); + this.on('zoomend', this._endPathZoom); + } + this.on('moveend', this._updateCanvasViewport); + this._updateCanvasViewport(); + } + }, + + _updateCanvasViewport: function () { + if (this._pathZooming) { + //Don't redraw while zooming. See _updateSvgViewport for more details + return; + } + this._updatePathViewport(); + + var vp = this._pathViewport, + min = vp.min, + size = vp.max.subtract(min), + root = this._pathRoot; + + //TODO check if this works properly on mobile webkit + L.DomUtil.setPosition(root, min); + root.width = size.x; + root.height = size.y; + root.getContext('2d').translate(-min.x, -min.y); + } +}); + + +/* + * L.LineUtil contains different utility functions for line segments + * and polylines (clipping, simplification, distances, etc.) + */ + +L.LineUtil = { + + // Simplify polyline with vertex reduction and Douglas-Peucker simplification. + // Improves rendering performance dramatically by lessening the number of points to draw. + + simplify: function (/*Point[]*/ points, /*Number*/ tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } + + var sqTolerance = tolerance * tolerance; + + // stage 1: vertex reduction + points = this._reducePoints(points, sqTolerance); + + // stage 2: Douglas-Peucker simplification + points = this._simplifyDP(points, sqTolerance); + + return points; + }, + + // distance from a point to a segment between two points + pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true)); + }, + + closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + return this._sqClosestPointOnSegment(p, p1, p2); + }, + + // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm + _simplifyDP: function (points, sqTolerance) { + + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); + + markers[0] = markers[len - 1] = 1; + + this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + + var i, + newPoints = []; + + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); + } + } + + return newPoints; + }, + + _simplifyDPStep: function (points, markers, sqTolerance, first, last) { + + var maxSqDist = 0, + index, i, sqDist; + + for (i = first + 1; i <= last - 1; i++) { + sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + this._simplifyDPStep(points, markers, sqTolerance, first, index); + this._simplifyDPStep(points, markers, sqTolerance, index, last); + } + }, + + // reduce points that are too close to each other to a single point + _reducePoints: function (points, sqTolerance) { + var reducedPoints = [points[0]]; + + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (this._sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; + } + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; + }, + + /*jshint bitwise:false */ // temporarily allow bitwise oprations + + // Cohen-Sutherland line clipping algorithm. + // Used to avoid rendering parts of a polyline that are not currently visible. + + clipSegment: function (a, b, bounds, useLastCode) { + var min = bounds.min, + max = bounds.max; + + var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), + codeB = this._getBitCode(b, bounds); + + // save 2nd code to avoid calculating it on the next segment + this._lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; + // if a,b is outside the clip window (trivial reject) + } else if (codeA & codeB) { + return false; + // other cases + } else { + var codeOut = codeA || codeB, + p = this._getEdgeIntersection(a, b, codeOut, bounds), + newCode = this._getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } + } + }, + + _getEdgeIntersection: function (a, b, code, bounds) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max; + + if (code & 8) { // top + return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y); + } else if (code & 4) { // bottom + return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y); + } else if (code & 2) { // right + return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx); + } else if (code & 1) { // left + return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx); + } + }, + + _getBitCode: function (/*Point*/ p, bounds) { + var code = 0; + + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } + + return code; + }, + + /*jshint bitwise:true */ + + // square distance (to avoid unnecessary Math.sqrt calls) + _sqDist: function (p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; + }, + + // return closest point on segment or distance to that point + _sqClosestPointOnSegment: function (p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return sqDist ? dx * dx + dy * dy : new L.Point(x, y); + } +}; + + +L.Polyline = L.Path.extend({ + initialize: function (latlngs, options) { + L.Path.prototype.initialize.call(this, options); + + this._latlngs = this._convertLatLngs(latlngs); + + // TODO refactor: move to Polyline.Edit.js + if (L.Handler.PolyEdit) { + this.editing = new L.Handler.PolyEdit(this); + + if (this.options.editable) { + this.editing.enable(); + } + } + }, + + options: { + // how much to simplify the polyline on each zoom level + // more = better performance and smoother look, less = more accurate + smoothFactor: 1.0, + noClip: false + }, + + projectLatlngs: function () { + this._originalPoints = []; + + for (var i = 0, len = this._latlngs.length; i < len; i++) { + this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]); + } + }, + + getPathString: function () { + for (var i = 0, len = this._parts.length, str = ''; i < len; i++) { + str += this._getPathPartStr(this._parts[i]); + } + return str; + }, + + getLatLngs: function () { + return this._latlngs; + }, + + setLatLngs: function (latlngs) { + this._latlngs = this._convertLatLngs(latlngs); + return this.redraw(); + }, + + addLatLng: function (latlng) { + this._latlngs.push(L.latLng(latlng)); + return this.redraw(); + }, + + spliceLatLngs: function (index, howMany) { + var removed = [].splice.apply(this._latlngs, arguments); + this._convertLatLngs(this._latlngs); + this.redraw(); + return removed; + }, + + closestLayerPoint: function (p) { + var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null; + + for (var j = 0, jLen = parts.length; j < jLen; j++) { + var points = parts[j]; + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; + var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true); + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2); + } + } + } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; + }, + + getBounds: function () { + var b = new L.LatLngBounds(); + var latLngs = this.getLatLngs(); + for (var i = 0, len = latLngs.length; i < len; i++) { + b.extend(latLngs[i]); + } + return b; + }, + + // TODO refactor: move to Polyline.Edit.js + onAdd: function (map) { + L.Path.prototype.onAdd.call(this, map); + + if (this.editing && this.editing.enabled()) { + this.editing.addHooks(); + } + }, + + onRemove: function (map) { + if (this.editing && this.editing.enabled()) { + this.editing.removeHooks(); + } + + L.Path.prototype.onRemove.call(this, map); + }, + + _convertLatLngs: function (latlngs) { + var i, len; + for (i = 0, len = latlngs.length; i < len; i++) { + if (latlngs[i] instanceof Array && typeof latlngs[i][0] !== 'number') { + return; + } + latlngs[i] = L.latLng(latlngs[i]); + } + return latlngs; + }, + + _initEvents: function () { + L.Path.prototype._initEvents.call(this); + }, + + _getPathPartStr: function (points) { + var round = L.Path.VML; + + for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) { + p = points[j]; + if (round) { + p._round(); + } + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + return str; + }, + + _clipPoints: function () { + var points = this._originalPoints, + len = points.length, + i, k, segment; + + if (this.options.noClip) { + this._parts = [points]; + return; + } + + this._parts = []; + + var parts = this._parts, + vp = this._map._pathViewport, + lu = L.LineUtil; + + for (i = 0, k = 0; i < len - 1; i++) { + segment = lu.clipSegment(points[i], points[i + 1], vp, i); + if (!segment) { + continue; + } + + parts[k] = parts[k] || []; + parts[k].push(segment[0]); + + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[i + 1]) || (i === len - 2)) { + parts[k].push(segment[1]); + k++; + } + } + }, + + // simplify each clipped part of the polyline + _simplifyPoints: function () { + var parts = this._parts, + lu = L.LineUtil; + + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = lu.simplify(parts[i], this.options.smoothFactor); + } + }, + + _updatePath: function () { + if (!this._map) { return; } + + this._clipPoints(); + this._simplifyPoints(); + + L.Path.prototype._updatePath.call(this); + } +}); + +L.polyline = function (latlngs, options) { + return new L.Polyline(latlngs, options); +}; + + +/* + * L.PolyUtil contains utilify functions for polygons (clipping, etc.). + */ + +/*jshint bitwise:false */ // allow bitwise oprations here + +L.PolyUtil = {}; + +/* + * Sutherland-Hodgeman polygon clipping algorithm. + * Used to avoid rendering parts of a polygon that are not currently visible. + */ +L.PolyUtil.clipPolygon = function (points, bounds) { + var min = bounds.min, + max = bounds.max, + clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p, + lu = L.LineUtil; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = lu._getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = lu._getEdgeIntersection(b, a, edge, bounds); + p._code = lu._getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = lu._getEdgeIntersection(b, a, edge, bounds); + p._code = lu._getBitCode(p, bounds); + clippedPoints.push(p); + } + } + points = clippedPoints; + } + + return points; +}; + +/*jshint bitwise:true */ + + +/* + * L.Polygon is used to display polygons on a map. + */ + +L.Polygon = L.Polyline.extend({ + options: { + fill: true + }, + + initialize: function (latlngs, options) { + L.Polyline.prototype.initialize.call(this, latlngs, options); + + if (latlngs && (latlngs[0] instanceof Array) && (typeof latlngs[0][0] !== 'number')) { + this._latlngs = this._convertLatLngs(latlngs[0]); + this._holes = latlngs.slice(1); + } + }, + + projectLatlngs: function () { + L.Polyline.prototype.projectLatlngs.call(this); + + // project polygon holes points + // TODO move this logic to Polyline to get rid of duplication + this._holePoints = []; + + if (!this._holes) { + return; + } + + for (var i = 0, len = this._holes.length, hole; i < len; i++) { + this._holePoints[i] = []; + + for (var j = 0, len2 = this._holes[i].length; j < len2; j++) { + this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]); + } + } + }, + + _clipPoints: function () { + var points = this._originalPoints, + newParts = []; + + this._parts = [points].concat(this._holePoints); + + if (this.options.noClip) { + return; + } + + for (var i = 0, len = this._parts.length; i < len; i++) { + var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport); + if (!clipped.length) { + continue; + } + newParts.push(clipped); + } + + this._parts = newParts; + }, + + _getPathPartStr: function (points) { + var str = L.Polyline.prototype._getPathPartStr.call(this, points); + return str + (L.Browser.svg ? 'z' : 'x'); + } +}); + +L.polygon = function (latlngs, options) { + return new L.Polygon(latlngs, options); +}; + + +/* + * Contains L.MultiPolyline and L.MultiPolygon layers. + */ + +(function () { + function createMulti(Klass) { + return L.FeatureGroup.extend({ + initialize: function (latlngs, options) { + this._layers = {}; + this._options = options; + this.setLatLngs(latlngs); + }, + + setLatLngs: function (latlngs) { + var i = 0, len = latlngs.length; + + this.eachLayer(function (layer) { + if (i < len) { + layer.setLatLngs(latlngs[i++]); + } else { + this.removeLayer(layer); + } + }, this); + + while (i < len) { + this.addLayer(new Klass(latlngs[i++], this._options)); + } + + return this; + } + }); + } + + L.MultiPolyline = createMulti(L.Polyline); + L.MultiPolygon = createMulti(L.Polygon); + + L.multiPolyline = function (latlngs, options) { + return new L.MultiPolyline(latlngs, options); + }; + + L.multiPolygon = function (latlngs, options) { + return new L.MultiPolygon(latlngs, options); + }; +}()); + + +/* + * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds + */ + +L.Rectangle = L.Polygon.extend({ + initialize: function (latLngBounds, options) { + L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); + }, + + setBounds: function (latLngBounds) { + this.setLatLngs(this._boundsToLatLngs(latLngBounds)); + }, + + _boundsToLatLngs: function (latLngBounds) { + latLngBounds = L.latLngBounds(latLngBounds); + return [ + latLngBounds.getSouthWest(), + latLngBounds.getNorthWest(), + latLngBounds.getNorthEast(), + latLngBounds.getSouthEast(), + latLngBounds.getSouthWest() + ]; + } +}); + +L.rectangle = function (latLngBounds, options) { + return new L.Rectangle(latLngBounds, options); +}; + + +/* + * L.Circle is a circle overlay (with a certain radius in meters). + */ + +L.Circle = L.Path.extend({ + initialize: function (latlng, radius, options) { + L.Path.prototype.initialize.call(this, options); + + this._latlng = L.latLng(latlng); + this._mRadius = radius; + }, + + options: { + fill: true + }, + + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + return this.redraw(); + }, + + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, + + projectLatlngs: function () { + var lngRadius = this._getLngRadius(), + latlng2 = new L.LatLng(this._latlng.lat, this._latlng.lng - lngRadius, true), + point2 = this._map.latLngToLayerPoint(latlng2); + + this._point = this._map.latLngToLayerPoint(this._latlng); + this._radius = Math.max(Math.round(this._point.x - point2.x), 1); + }, + + getBounds: function () { + var map = this._map, + delta = this._radius * Math.cos(Math.PI / 4), + point = map.project(this._latlng), + swPoint = new L.Point(point.x - delta, point.y + delta), + nePoint = new L.Point(point.x + delta, point.y - delta), + sw = map.unproject(swPoint), + ne = map.unproject(nePoint); + + return new L.LatLngBounds(sw, ne); + }, + + getLatLng: function () { + return this._latlng; + }, + + getPathString: function () { + var p = this._point, + r = this._radius; + + if (this._checkIfEmpty()) { + return ''; + } + + if (L.Browser.svg) { + return "M" + p.x + "," + (p.y - r) + + "A" + r + "," + r + ",0,1,1," + + (p.x - 0.1) + "," + (p.y - r) + " z"; + } else { + p._round(); + r = Math.round(r); + return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360); + } + }, + + getRadius: function () { + return this._mRadius; + }, + + _getLngRadius: function () { + var equatorLength = 40075017, + hLength = equatorLength * Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat); + + return (this._mRadius / hLength) * 360; + }, + + _checkIfEmpty: function () { + if (!this._map) { + return false; + } + var vp = this._map._pathViewport, + r = this._radius, + p = this._point; + + return p.x - r > vp.max.x || p.y - r > vp.max.y || + p.x + r < vp.min.x || p.y + r < vp.min.y; + } +}); + +L.circle = function (latlng, radius, options) { + return new L.Circle(latlng, radius, options); +}; + + +/* + * L.CircleMarker is a circle overlay with a permanent pixel radius. + */ + +L.CircleMarker = L.Circle.extend({ + options: { + radius: 10, + weight: 2 + }, + + initialize: function (latlng, options) { + L.Circle.prototype.initialize.call(this, latlng, null, options); + this._radius = this.options.radius; + }, + + projectLatlngs: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + }, + + setRadius: function (radius) { + this._radius = radius; + return this.redraw(); + } +}); + +L.circleMarker = function (latlng, options) { + return new L.CircleMarker(latlng, options); +}; + + + +L.Polyline.include(!L.Path.CANVAS ? {} : { + _containsPoint: function (p, closed) { + var i, j, k, len, len2, dist, part, + w = this.options.weight / 2; + + if (L.Browser.touch) { + w += 10; // polyline click tolerance on touch devices + } + + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { + continue; + } + + dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]); + + if (dist <= w) { + return true; + } + } + } + return false; + } +}); + + + +L.Polygon.include(!L.Path.CANVAS ? {} : { + _containsPoint: function (p) { + var inside = false, + part, p1, p2, + i, j, k, + len, len2; + + // TODO optimization: check if within bounds first + + if (L.Polyline.prototype._containsPoint.call(this, p, true)) { + // click on polygon border + return true; + } + + // ray casting algorithm for detecting if point is in polygon + + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; + + if (((p1.y > p.y) !== (p2.y > p.y)) && + (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } + } + } + + return inside; + } +}); + + +/* + * Circle canvas specific drawing parts. + */ + +L.Circle.include(!L.Path.CANVAS ? {} : { + _drawPath: function () { + var p = this._point; + this._ctx.beginPath(); + this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false); + }, + + _containsPoint: function (p) { + var center = this._point, + w2 = this.options.stroke ? this.options.weight / 2 : 0; + + return (p.distanceTo(center) <= this._radius + w2); + } +}); + + +L.GeoJSON = L.FeatureGroup.extend({ + initialize: function (geojson, options) { + L.Util.setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + addData: function (geojson) { + var features = geojson instanceof Array ? geojson : geojson.features, + i, len; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + this.addData(features[i]); + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return; } + + var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer); + layer.feature = geojson; + + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + resetStyle: function (layer) { + var style = this.options.style; + if (style) { + this._setLayerStyle(layer, style); + } + }, + + setStyle: function (style) { + this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (typeof style === 'function') { + style = style(layer.feature); + } + if (layer.setStyle) { + layer.setStyle(style); + } + } +}); + +L.Util.extend(L.GeoJSON, { + geometryToLayer: function (geojson, pointToLayer) { + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry.coordinates, + layers = [], + latlng, latlngs, i, len, layer; + + switch (geometry.type) { + case 'Point': + latlng = this.coordsToLatLng(coords); + return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = this.coordsToLatLng(coords[i]); + layer = pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng); + layers.push(layer); + } + return new L.FeatureGroup(layers); + + case 'LineString': + latlngs = this.coordsToLatLngs(coords); + return new L.Polyline(latlngs); + + case 'Polygon': + latlngs = this.coordsToLatLngs(coords, 1); + return new L.Polygon(latlngs); + + case 'MultiLineString': + latlngs = this.coordsToLatLngs(coords, 1); + return new L.MultiPolyline(latlngs); + + case "MultiPolygon": + latlngs = this.coordsToLatLngs(coords, 2); + return new L.MultiPolygon(latlngs); + + case "GeometryCollection": + for (i = 0, len = geometry.geometries.length; i < len; i++) { + layer = this.geometryToLayer(geometry.geometries[i], pointToLayer); + layers.push(layer); + } + return new L.FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } + }, + + coordsToLatLng: function (coords, reverse) { // (Array, Boolean) -> LatLng + var lat = parseFloat(coords[reverse ? 0 : 1]), + lng = parseFloat(coords[reverse ? 1 : 0]); + + return new L.LatLng(lat, lng, true); + }, + + coordsToLatLngs: function (coords, levelsDeep, reverse) { // (Array, Number, Boolean) -> Array + var latlng, + latlngs = [], + i, len; + + for (i = 0, len = coords.length; i < len; i++) { + latlng = levelsDeep ? + this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) : + this.coordsToLatLng(coords[i], reverse); + + latlngs.push(latlng); + } + + return latlngs; + } +}); + +L.geoJson = function (geojson, options) { + return new L.GeoJSON(geojson, options); +}; + + +/* + * L.DomEvent contains functions for working with DOM events. + */ + +L.DomEvent = { + /* inpired by John Resig, Dean Edwards and YUI addEvent implementations */ + addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object]) + + var id = L.Util.stamp(fn), + key = '_leaflet_' + type + id, + handler, originalHandler, newType; + + if (obj[key]) { return this; } + + handler = function (e) { + return fn.call(context || obj, e || L.DomEvent._getEvent()); + }; + + if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { + return this.addDoubleTapListener(obj, handler, id); + + } else if ('addEventListener' in obj) { + + if (type === 'mousewheel') { + obj.addEventListener('DOMMouseScroll', handler, false); + obj.addEventListener(type, handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + + originalHandler = handler; + newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout'); + + handler = function (e) { + if (!L.DomEvent._checkMouse(obj, e)) { return; } + return originalHandler(e); + }; + + obj.addEventListener(newType, handler, false); + + } else { + obj.addEventListener(type, handler, false); + } + + } else if ('attachEvent' in obj) { + obj.attachEvent("on" + type, handler); + } + + obj[key] = handler; + + return this; + }, + + removeListener: function (obj, type, fn) { // (HTMLElement, String, Function) + + var id = L.Util.stamp(fn), + key = '_leaflet_' + type + id, + handler = obj[key]; + + if (!handler) { return; } + + if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) { + this.removeDoubleTapListener(obj, id); + + } else if ('removeEventListener' in obj) { + + if (type === 'mousewheel') { + obj.removeEventListener('DOMMouseScroll', handler, false); + obj.removeEventListener(type, handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false); + } else { + obj.removeEventListener(type, handler, false); + } + } else if ('detachEvent' in obj) { + obj.detachEvent("on" + type, handler); + } + + obj[key] = null; + + return this; + }, + + stopPropagation: function (e) { + + if (e.stopPropagation) { + e.stopPropagation(); + } else { + e.cancelBubble = true; + } + return this; + }, + + disableClickPropagation: function (el) { + + var stop = L.DomEvent.stopPropagation; + + return L.DomEvent + .addListener(el, L.Draggable.START, stop) + .addListener(el, 'click', stop) + .addListener(el, 'dblclick', stop); + }, + + preventDefault: function (e) { + + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; + }, + + stop: function (e) { + return L.DomEvent.preventDefault(e).stopPropagation(e); + }, + + getMousePosition: function (e, container) { + + var body = document.body, + docEl = document.documentElement, + x = e.pageX ? e.pageX : e.clientX + body.scrollLeft + docEl.scrollLeft, + y = e.pageY ? e.pageY : e.clientY + body.scrollTop + docEl.scrollTop, + pos = new L.Point(x, y); + + return (container ? pos._subtract(L.DomUtil.getViewportOffset(container)) : pos); + }, + + getWheelDelta: function (e) { + + var delta = 0; + + if (e.wheelDelta) { + delta = e.wheelDelta / 120; + } + if (e.detail) { + delta = -e.detail / 3; + } + return delta; + }, + + // check if element really left/entered the event target (for mouseenter/mouseleave) + _checkMouse: function (el, e) { + + var related = e.relatedTarget; + + if (!related) { return true; } + + try { + while (related && (related !== el)) { + related = related.parentNode; + } + } catch (err) { + return false; + } + return (related !== el); + }, + + /*jshint noarg:false */ + _getEvent: function () { // evil magic for IE + + var e = window.event; + if (!e) { + var caller = arguments.callee.caller; + while (caller) { + e = caller['arguments'][0]; + if (e && window.Event === e.constructor) { + break; + } + caller = caller.caller; + } + } + return e; + } + /*jshint noarg:false */ +}; + +L.DomEvent.on = L.DomEvent.addListener; +L.DomEvent.off = L.DomEvent.removeListener; + +/* + * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too. + */ + +L.Draggable = L.Class.extend({ + includes: L.Mixin.Events, + + statics: { + START: L.Browser.touch ? 'touchstart' : 'mousedown', + END: L.Browser.touch ? 'touchend' : 'mouseup', + MOVE: L.Browser.touch ? 'touchmove' : 'mousemove', + TAP_TOLERANCE: 15 + }, + + initialize: function (element, dragStartTarget) { + this._element = element; + this._dragStartTarget = dragStartTarget || element; + }, + + enable: function () { + if (this._enabled) { + return; + } + L.DomEvent.on(this._dragStartTarget, L.Draggable.START, this._onDown, this); + this._enabled = true; + }, + + disable: function () { + if (!this._enabled) { + return; + } + L.DomEvent.off(this._dragStartTarget, L.Draggable.START, this._onDown); + this._enabled = false; + this._moved = false; + }, + + _onDown: function (e) { + if ((!L.Browser.touch && e.shiftKey) || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { + return; + } + + this._simulateClick = true; + + if (e.touches && e.touches.length > 1) { + this._simulateClick = false; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + el = first.target; + + L.DomEvent.preventDefault(e); + + if (L.Browser.touch && el.tagName.toLowerCase() === 'a') { + L.DomUtil.addClass(el, 'leaflet-active'); + } + + this._moved = false; + if (this._moving) { + return; + } + + this._startPos = this._newPos = L.DomUtil.getPosition(this._element); + this._startPoint = new L.Point(first.clientX, first.clientY); + + L.DomEvent.on(document, L.Draggable.MOVE, this._onMove, this); + L.DomEvent.on(document, L.Draggable.END, this._onUp, this); + }, + + _onMove: function (e) { + if (e.touches && e.touches.length > 1) { return; } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + newPoint = new L.Point(first.clientX, first.clientY), + diffVec = newPoint.subtract(this._startPoint); + + if (!diffVec.x && !diffVec.y) { return; } + + L.DomEvent.preventDefault(e); + + if (!this._moved) { + this.fire('dragstart'); + this._moved = true; + + if (!L.Browser.touch) { + L.DomUtil.disableTextSelection(); + this._setMovingCursor(); + } + } + + this._newPos = this._startPos.add(diffVec); + this._moving = true; + + L.Util.cancelAnimFrame(this._animRequest); + this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget); + }, + + _updatePosition: function () { + this.fire('predrag'); + L.DomUtil.setPosition(this._element, this._newPos); + this.fire('drag'); + }, + + _onUp: function (e) { + if (this._simulateClick && e.changedTouches) { + var first = e.changedTouches[0], + el = first.target, + dist = (this._newPos && this._newPos.distanceTo(this._startPos)) || 0; + + if (el.tagName.toLowerCase() === 'a') { + L.DomUtil.removeClass(el, 'leaflet-active'); + } + + if (dist < L.Draggable.TAP_TOLERANCE) { + this._simulateEvent('click', first); + } + } + + if (!L.Browser.touch) { + L.DomUtil.enableTextSelection(); + this._restoreCursor(); + } + + L.DomEvent.off(document, L.Draggable.MOVE, this._onMove); + L.DomEvent.off(document, L.Draggable.END, this._onUp); + + if (this._moved) { + // ensure drag is not fired after dragend + L.Util.cancelAnimFrame(this._animRequest); + + this.fire('dragend'); + } + this._moving = false; + }, + + _setMovingCursor: function () { + L.DomUtil.addClass(document.body, 'leaflet-dragging'); + }, + + _restoreCursor: function () { + L.DomUtil.removeClass(document.body, 'leaflet-dragging'); + }, + + _simulateEvent: function (type, e) { + var simulatedEvent = document.createEvent('MouseEvents'); + + simulatedEvent.initMouseEvent( + type, true, true, window, 1, + e.screenX, e.screenY, + e.clientX, e.clientY, + false, false, false, false, 0, null); + + e.target.dispatchEvent(simulatedEvent); + } +}); + + +/* + * L.Handler classes are used internally to inject interaction features to classes like Map and Marker. + */ + +L.Handler = L.Class.extend({ + initialize: function (map) { + this._map = map; + }, + + enable: function () { + if (this._enabled) { return; } + + this._enabled = true; + this.addHooks(); + }, + + disable: function () { + if (!this._enabled) { return; } + + this._enabled = false; + this.removeHooks(); + }, + + enabled: function () { + return !!this._enabled; + } +}); + + +/* + * L.Handler.MapDrag is used internally by L.Map to make the map draggable. + */ + +L.Map.mergeOptions({ + dragging: true, + + inertia: !L.Browser.android23, + inertiaDeceleration: 3000, // px/s^2 + inertiaMaxSpeed: 1500, // px/s + inertiaThreshold: L.Browser.touch ? 32 : 14, // ms + + // TODO refactor, move to CRS + worldCopyJump: true +}); + +L.Map.Drag = L.Handler.extend({ + addHooks: function () { + if (!this._draggable) { + this._draggable = new L.Draggable(this._map._mapPane, this._map._container); + + this._draggable.on({ + 'dragstart': this._onDragStart, + 'drag': this._onDrag, + 'dragend': this._onDragEnd + }, this); + + var options = this._map.options; + + if (options.worldCopyJump) { + this._draggable.on('predrag', this._onPreDrag, this); + this._map.on('viewreset', this._onViewReset, this); + } + } + this._draggable.enable(); + }, + + removeHooks: function () { + this._draggable.disable(); + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _onDragStart: function () { + var map = this._map; + + map + .fire('movestart') + .fire('dragstart'); + + if (map._panTransition) { + map._panTransition._onTransitionEnd(true); + } + + if (map.options.inertia) { + this._positions = []; + this._times = []; + } + }, + + _onDrag: function () { + if (this._map.options.inertia) { + var time = this._lastTime = +new Date(), + pos = this._lastPos = this._draggable._newPos; + + this._positions.push(pos); + this._times.push(time); + + if (time - this._times[0] > 200) { + this._positions.shift(); + this._times.shift(); + } + } + + this._map + .fire('move') + .fire('drag'); + }, + + _onViewReset: function () { + var pxCenter = this._map.getSize().divideBy(2), + pxWorldCenter = this._map.latLngToLayerPoint(new L.LatLng(0, 0)); + + this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; + this._worldWidth = this._map.project(new L.LatLng(0, 180)).x; + }, + + _onPreDrag: function () { + // TODO refactor to be able to adjust map pane position after zoom + var map = this._map, + worldWidth = this._worldWidth, + halfWidth = Math.round(worldWidth / 2), + dx = this._initialWorldOffset, + x = this._draggable._newPos.x, + newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, + newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, + newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; + + this._draggable._newPos.x = newX; + }, + + _onDragEnd: function () { + var map = this._map, + options = map.options, + delay = +new Date() - this._lastTime, + + noInertia = !options.inertia || + delay > options.inertiaThreshold || + this._positions[0] === undefined; + + if (noInertia) { + map.fire('moveend'); + + } else { + + var direction = this._lastPos.subtract(this._positions[0]), + duration = (this._lastTime + delay - this._times[0]) / 1000, + + speedVector = direction.multiplyBy(0.58 / duration), + speed = speedVector.distanceTo(new L.Point(0, 0)), + + limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), + limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), + + decelerationDuration = limitedSpeed / options.inertiaDeceleration, + offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); + + var panOptions = { + duration: decelerationDuration, + easing: 'ease-out' + }; + + L.Util.requestAnimFrame(L.Util.bind(function () { + this._map.panBy(offset, panOptions); + }, this)); + } + + map.fire('dragend'); + + if (options.maxBounds) { + // TODO predrag validation instead of animation + L.Util.requestAnimFrame(this._panInsideMaxBounds, map, true, map._container); + } + }, + + _panInsideMaxBounds: function () { + this.panInsideBounds(this.options.maxBounds); + } +}); + +L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag); + + +/* + * L.Handler.DoubleClickZoom is used internally by L.Map to add double-click zooming. + */ + +L.Map.mergeOptions({ + doubleClickZoom: true +}); + +L.Map.DoubleClickZoom = L.Handler.extend({ + addHooks: function () { + this._map.on('dblclick', this._onDoubleClick); + }, + + removeHooks: function () { + this._map.off('dblclick', this._onDoubleClick); + }, + + _onDoubleClick: function (e) { + this.setView(e.latlng, this._zoom + 1); + } +}); + +L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); + +/* + * L.Handler.ScrollWheelZoom is used internally by L.Map to enable mouse scroll wheel zooming on the map. + */ + +L.Map.mergeOptions({ + scrollWheelZoom: !L.Browser.touch +}); + +L.Map.ScrollWheelZoom = L.Handler.extend({ + addHooks: function () { + L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this); + this._delta = 0; + }, + + removeHooks: function () { + L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll); + }, + + _onWheelScroll: function (e) { + var delta = L.DomEvent.getWheelDelta(e); + + this._delta += delta; + this._lastMousePos = this._map.mouseEventToContainerPoint(e); + + clearTimeout(this._timer); + this._timer = setTimeout(L.Util.bind(this._performZoom, this), 40); + + L.DomEvent.preventDefault(e); + }, + + _performZoom: function () { + var map = this._map, + delta = Math.round(this._delta), + zoom = map.getZoom(); + + delta = Math.max(Math.min(delta, 4), -4); + delta = map._limitZoom(zoom + delta) - zoom; + + this._delta = 0; + + if (!delta) { return; } + + var newZoom = zoom + delta, + newCenter = this._getCenterForScrollWheelZoom(this._lastMousePos, newZoom); + + map.setView(newCenter, newZoom); + }, + + _getCenterForScrollWheelZoom: function (mousePos, newZoom) { + var map = this._map, + scale = map.getZoomScale(newZoom), + viewHalf = map.getSize().divideBy(2), + centerOffset = mousePos.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenterPoint = map._getTopLeftPoint().add(viewHalf).add(centerOffset); + + return map.unproject(newCenterPoint); + } +}); + +L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); + + +L.Util.extend(L.DomEvent, { + // inspired by Zepto touch code by Thomas Fuchs + addDoubleTapListener: function (obj, handler, id) { + var last, + doubleTap = false, + delay = 250, + touch, + pre = '_leaflet_', + touchstart = 'touchstart', + touchend = 'touchend'; + + function onTouchStart(e) { + if (e.touches.length !== 1) { + return; + } + + var now = Date.now(), + delta = now - (last || now); + + touch = e.touches[0]; + doubleTap = (delta > 0 && delta <= delay); + last = now; + } + function onTouchEnd(e) { + if (doubleTap) { + touch.type = 'dblclick'; + handler(touch); + last = null; + } + } + obj[pre + touchstart + id] = onTouchStart; + obj[pre + touchend + id] = onTouchEnd; + + obj.addEventListener(touchstart, onTouchStart, false); + obj.addEventListener(touchend, onTouchEnd, false); + return this; + }, + + removeDoubleTapListener: function (obj, id) { + var pre = '_leaflet_'; + obj.removeEventListener(obj, obj[pre + 'touchstart' + id], false); + obj.removeEventListener(obj, obj[pre + 'touchend' + id], false); + return this; + } +}); + + +/* + * L.Handler.TouchZoom is used internally by L.Map to add touch-zooming on Webkit-powered mobile browsers. + */ + +L.Map.mergeOptions({ + touchZoom: L.Browser.touch && !L.Browser.android23 +}); + +L.Map.TouchZoom = L.Handler.extend({ + addHooks: function () { + L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + removeHooks: function () { + L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + _onTouchStart: function (e) { + var map = this._map; + + if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } + + var p1 = map.mouseEventToLayerPoint(e.touches[0]), + p2 = map.mouseEventToLayerPoint(e.touches[1]), + viewCenter = map._getCenterLayerPoint(); + + this._startCenter = p1.add(p2).divideBy(2, true); + this._startDist = p1.distanceTo(p2); + + this._moved = false; + this._zooming = true; + + this._centerOffset = viewCenter.subtract(this._startCenter); + + L.DomEvent + .on(document, 'touchmove', this._onTouchMove, this) + .on(document, 'touchend', this._onTouchEnd, this); + + L.DomEvent.preventDefault(e); + }, + + _onTouchMove: function (e) { + if (!e.touches || e.touches.length !== 2) { return; } + + var map = this._map; + + var p1 = map.mouseEventToLayerPoint(e.touches[0]), + p2 = map.mouseEventToLayerPoint(e.touches[1]); + + this._scale = p1.distanceTo(p2) / this._startDist; + this._delta = p1.add(p2).divideBy(2, true).subtract(this._startCenter); + + if (this._scale === 1) { return; } + + if (!this._moved) { + L.DomUtil.addClass(map._mapPane, 'leaflet-zoom-anim leaflet-touching'); + + map + .fire('movestart') + .fire('zoomstart') + ._prepareTileBg(); + + this._moved = true; + } + + L.Util.cancelAnimFrame(this._animRequest); + this._animRequest = L.Util.requestAnimFrame(this._updateOnMove, this, true, this._map._container); + + L.DomEvent.preventDefault(e); + }, + + _updateOnMove: function () { + var map = this._map, + origin = this._getScaleOrigin(), + center = map.layerPointToLatLng(origin); + + map.fire('zoomanim', { + center: center, + zoom: map.getScaleZoom(this._scale) + }); + + // Used 2 translates instead of transform-origin because of a very strange bug - + // it didn't count the origin on the first touch-zoom but worked correctly afterwards + + map._tileBg.style[L.DomUtil.TRANSFORM] = + L.DomUtil.getTranslateString(this._delta) + ' ' + + L.DomUtil.getScaleString(this._scale, this._startCenter); + }, + + _onTouchEnd: function (e) { + if (!this._moved || !this._zooming) { return; } + + var map = this._map; + + this._zooming = false; + L.DomUtil.removeClass(map._mapPane, 'leaflet-touching'); + + L.DomEvent + .off(document, 'touchmove', this._onTouchMove) + .off(document, 'touchend', this._onTouchEnd); + + var origin = this._getScaleOrigin(), + center = map.layerPointToLatLng(origin), + + oldZoom = map.getZoom(), + floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom, + roundZoomDelta = (floatZoomDelta > 0 ? Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)), + zoom = map._limitZoom(oldZoom + roundZoomDelta); + + map.fire('zoomanim', { + center: center, + zoom: zoom + }); + + map._runAnimation(center, zoom, map.getZoomScale(zoom) / this._scale, origin, true); + }, + + _getScaleOrigin: function () { + var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale); + return this._startCenter.add(centerOffset); + } +}); + +L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom); + + +/* + * L.Handler.ShiftDragZoom is used internally by L.Map to add shift-drag zoom (zoom to a selected bounding box). + */ + +L.Map.mergeOptions({ + boxZoom: true +}); + +L.Map.BoxZoom = L.Handler.extend({ + initialize: function (map) { + this._map = map; + this._container = map._container; + this._pane = map._panes.overlayPane; + }, + + addHooks: function () { + L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this); + }, + + removeHooks: function () { + L.DomEvent.off(this._container, 'mousedown', this._onMouseDown); + }, + + _onMouseDown: function (e) { + if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } + + L.DomUtil.disableTextSelection(); + + this._startLayerPoint = this._map.mouseEventToLayerPoint(e); + + this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane); + L.DomUtil.setPosition(this._box, this._startLayerPoint); + + //TODO refactor: move cursor to styles + this._container.style.cursor = 'crosshair'; + + L.DomEvent + .on(document, 'mousemove', this._onMouseMove, this) + .on(document, 'mouseup', this._onMouseUp, this) + .preventDefault(e); + + this._map.fire("boxzoomstart"); + }, + + _onMouseMove: function (e) { + var startPoint = this._startLayerPoint, + box = this._box, + + layerPoint = this._map.mouseEventToLayerPoint(e), + offset = layerPoint.subtract(startPoint), + + newPos = new L.Point( + Math.min(layerPoint.x, startPoint.x), + Math.min(layerPoint.y, startPoint.y)); + + L.DomUtil.setPosition(box, newPos); + + // TODO refactor: remove hardcoded 4 pixels + box.style.width = (Math.abs(offset.x) - 4) + 'px'; + box.style.height = (Math.abs(offset.y) - 4) + 'px'; + }, + + _onMouseUp: function (e) { + this._pane.removeChild(this._box); + this._container.style.cursor = ''; + + L.DomUtil.enableTextSelection(); + + L.DomEvent + .off(document, 'mousemove', this._onMouseMove) + .off(document, 'mouseup', this._onMouseUp); + + var map = this._map, + layerPoint = map.mouseEventToLayerPoint(e); + + var bounds = new L.LatLngBounds( + map.layerPointToLatLng(this._startLayerPoint), + map.layerPointToLatLng(layerPoint)); + + map.fitBounds(bounds); + + map.fire("boxzoomend", { + boxZoomBounds: bounds + }); + } +}); + +L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom); + + +L.Map.mergeOptions({ + keyboard: true, + keyboardPanOffset: 80, + keyboardZoomOffset: 1 +}); + +L.Map.Keyboard = L.Handler.extend({ + + // list of e.keyCode values for particular actions + keyCodes: { + left: [37], + right: [39], + down: [40], + up: [38], + zoomIn: [187, 107, 61], + zoomOut: [189, 109] + }, + + initialize: function (map) { + this._map = map; + + this._setPanOffset(map.options.keyboardPanOffset); + this._setZoomOffset(map.options.keyboardZoomOffset); + }, + + addHooks: function () { + var container = this._map._container; + + // make the container focusable by tabbing + if (container.tabIndex === -1) { + container.tabIndex = "0"; + } + + L.DomEvent + .addListener(container, 'focus', this._onFocus, this) + .addListener(container, 'blur', this._onBlur, this) + .addListener(container, 'mousedown', this._onMouseDown, this); + + this._map + .on('focus', this._addHooks, this) + .on('blur', this._removeHooks, this); + }, + + removeHooks: function () { + this._removeHooks(); + + var container = this._map._container; + L.DomEvent + .removeListener(container, 'focus', this._onFocus, this) + .removeListener(container, 'blur', this._onBlur, this) + .removeListener(container, 'mousedown', this._onMouseDown, this); + + this._map + .off('focus', this._addHooks, this) + .off('blur', this._removeHooks, this); + }, + + _onMouseDown: function () { + if (!this._focused) { + this._map._container.focus(); + } + }, + + _onFocus: function () { + this._focused = true; + this._map.fire('focus'); + }, + + _onBlur: function () { + this._focused = false; + this._map.fire('blur'); + }, + + _setPanOffset: function (pan) { + var keys = this._panKeys = {}, + codes = this.keyCodes, + i, len; + + for (i = 0, len = codes.left.length; i < len; i++) { + keys[codes.left[i]] = [-1 * pan, 0]; + } + for (i = 0, len = codes.right.length; i < len; i++) { + keys[codes.right[i]] = [pan, 0]; + } + for (i = 0, len = codes.down.length; i < len; i++) { + keys[codes.down[i]] = [0, pan]; + } + for (i = 0, len = codes.up.length; i < len; i++) { + keys[codes.up[i]] = [0, -1 * pan]; + } + }, + + _setZoomOffset: function (zoom) { + var keys = this._zoomKeys = {}, + codes = this.keyCodes, + i, len; + + for (i = 0, len = codes.zoomIn.length; i < len; i++) { + keys[codes.zoomIn[i]] = zoom; + } + for (i = 0, len = codes.zoomOut.length; i < len; i++) { + keys[codes.zoomOut[i]] = -zoom; + } + }, + + _addHooks: function () { + L.DomEvent.addListener(document, 'keydown', this._onKeyDown, this); + }, + + _removeHooks: function () { + L.DomEvent.removeListener(document, 'keydown', this._onKeyDown, this); + }, + + _onKeyDown: function (e) { + var key = e.keyCode; + + if (this._panKeys.hasOwnProperty(key)) { + this._map.panBy(this._panKeys[key]); + + } else if (this._zoomKeys.hasOwnProperty(key)) { + this._map.setZoom(this._map.getZoom() + this._zoomKeys[key]); + + } else { + return; + } + + L.DomEvent.stop(e); + } +}); + +L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard); + + +/* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ + +L.Handler.MarkerDrag = L.Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, + + addHooks: function () { + var icon = this._marker._icon; + if (!this._draggable) { + this._draggable = new L.Draggable(icon, icon) + .on('dragstart', this._onDragStart, this) + .on('drag', this._onDrag, this) + .on('dragend', this._onDragEnd, this); + } + this._draggable.enable(); + }, + + removeHooks: function () { + this._draggable.disable(); + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _onDragStart: function (e) { + this._marker + .closePopup() + .fire('movestart') + .fire('dragstart'); + }, + + _onDrag: function (e) { + // update shadow position + var iconPos = L.DomUtil.getPosition(this._marker._icon); + if (this._marker._shadow) { + L.DomUtil.setPosition(this._marker._shadow, iconPos); + } + + this._marker._latlng = this._marker._map.layerPointToLatLng(iconPos); + + this._marker + .fire('move') + .fire('drag'); + }, + + _onDragEnd: function () { + this._marker + .fire('moveend') + .fire('dragend'); + } +}); + + +L.Handler.PolyEdit = L.Handler.extend({ + options: { + icon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: 'leaflet-div-icon leaflet-editing-icon' + }) + }, + + initialize: function (poly, options) { + this._poly = poly; + L.Util.setOptions(this, options); + }, + + addHooks: function () { + if (this._poly._map) { + if (!this._markerGroup) { + this._initMarkers(); + } + this._poly._map.addLayer(this._markerGroup); + } + }, + + removeHooks: function () { + if (this._poly._map) { + this._poly._map.removeLayer(this._markerGroup); + delete this._markerGroup; + delete this._markers; + } + }, + + updateMarkers: function () { + this._markerGroup.clearLayers(); + this._initMarkers(); + }, + + _initMarkers: function () { + if (!this._markerGroup) { + this._markerGroup = new L.LayerGroup(); + } + this._markers = []; + + var latlngs = this._poly._latlngs, + i, j, len, marker; + + // TODO refactor holes implementation in Polygon to support it here + + for (i = 0, len = latlngs.length; i < len; i++) { + + marker = this._createMarker(latlngs[i], i); + marker.on('click', this._onMarkerClick, this); + this._markers.push(marker); + } + + var markerLeft, markerRight; + + for (i = 0, j = len - 1; i < len; j = i++) { + if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) { + continue; + } + + markerLeft = this._markers[j]; + markerRight = this._markers[i]; + + this._createMiddleMarker(markerLeft, markerRight); + this._updatePrevNext(markerLeft, markerRight); + } + }, + + _createMarker: function (latlng, index) { + var marker = new L.Marker(latlng, { + draggable: true, + icon: this.options.icon + }); + + marker._origLatLng = latlng; + marker._index = index; + + marker.on('drag', this._onMarkerDrag, this); + marker.on('dragend', this._fireEdit, this); + + this._markerGroup.addLayer(marker); + + return marker; + }, + + _fireEdit: function () { + this._poly.fire('edit'); + }, + + _onMarkerDrag: function (e) { + var marker = e.target; + + L.Util.extend(marker._origLatLng, marker._latlng); + + if (marker._middleLeft) { + marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker)); + } + if (marker._middleRight) { + marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next)); + } + + this._poly.redraw(); + }, + + _onMarkerClick: function (e) { + // Default action on marker click is to remove that marker, but if we remove the marker when latlng count < 3, we don't have a valid polyline anymore + if (this._poly._latlngs.length < 3) { + return; + } + + var marker = e.target, + i = marker._index; + + // Check existence of previous and next markers since they wouldn't exist for edge points on the polyline + if (marker._prev && marker._next) { + this._createMiddleMarker(marker._prev, marker._next); + this._updatePrevNext(marker._prev, marker._next); + } + + // The marker itself is guaranteed to exist and present in the layer, since we managed to click on it + this._markerGroup.removeLayer(marker); + // Check for the existence of middle left or middle right + if (marker._middleLeft) { + this._markerGroup.removeLayer(marker._middleLeft); + } + if (marker._middleRight) { + this._markerGroup.removeLayer(marker._middleRight); + } + this._markers.splice(i, 1); + this._poly.spliceLatLngs(i, 1); + this._updateIndexes(i, -1); + this._poly.fire('edit'); + }, + + _updateIndexes: function (index, delta) { + this._markerGroup.eachLayer(function (marker) { + if (marker._index > index) { + marker._index += delta; + } + }); + }, + + _createMiddleMarker: function (marker1, marker2) { + var latlng = this._getMiddleLatLng(marker1, marker2), + marker = this._createMarker(latlng), + onClick, + onDragStart, + onDragEnd; + + marker.setOpacity(0.6); + + marker1._middleRight = marker2._middleLeft = marker; + + onDragStart = function () { + var i = marker2._index; + + marker._index = i; + + marker + .off('click', onClick) + .on('click', this._onMarkerClick, this); + + latlng.lat = marker.getLatLng().lat; + latlng.lng = marker.getLatLng().lng; + this._poly.spliceLatLngs(i, 0, latlng); + this._markers.splice(i, 0, marker); + + marker.setOpacity(1); + + this._updateIndexes(i, 1); + marker2._index++; + this._updatePrevNext(marker1, marker); + this._updatePrevNext(marker, marker2); + }; + + onDragEnd = function () { + marker.off('dragstart', onDragStart, this); + marker.off('dragend', onDragEnd, this); + + this._createMiddleMarker(marker1, marker); + this._createMiddleMarker(marker, marker2); + }; + + onClick = function () { + onDragStart.call(this); + onDragEnd.call(this); + this._poly.fire('edit'); + }; + + marker + .on('click', onClick, this) + .on('dragstart', onDragStart, this) + .on('dragend', onDragEnd, this); + + this._markerGroup.addLayer(marker); + }, + + _updatePrevNext: function (marker1, marker2) { + marker1._next = marker2; + marker2._prev = marker1; + }, + + _getMiddleLatLng: function (marker1, marker2) { + var map = this._poly._map, + p1 = map.latLngToLayerPoint(marker1.getLatLng()), + p2 = map.latLngToLayerPoint(marker2.getLatLng()); + + return map.layerPointToLatLng(p1._add(p2).divideBy(2)); + } +}); + + + +L.Control = L.Class.extend({ + options: { + position: 'topright' + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + }, + + getPosition: function () { + return this.options.position; + }, + + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + addTo: function (map) { + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + L.DomUtil.addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + return this; + }, + + removeFrom: function (map) { + var pos = this.getPosition(), + corner = map._controlCorners[pos]; + + corner.removeChild(this._container); + this._map = null; + + if (this.onRemove) { + this.onRemove(map); + } + + return this; + } +}); + +L.control = function (options) { + return new L.Control(options); +}; + + +L.Map.include({ + addControl: function (control) { + control.addTo(this); + return this; + }, + + removeControl: function (control) { + control.removeFrom(this); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + L.DomUtil.create('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = + L.DomUtil.create('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + } +}); + + +L.Control.Zoom = L.Control.extend({ + options: { + position: 'topleft' + }, + + onAdd: function (map) { + var className = 'leaflet-control-zoom', + container = L.DomUtil.create('div', className); + + this._createButton('Zoom in', className + '-in', container, map.zoomIn, map); + this._createButton('Zoom out', className + '-out', container, map.zoomOut, map); + + return container; + }, + + _createButton: function (title, className, container, fn, context) { + var link = L.DomUtil.create('a', className, container); + link.href = '#'; + link.title = title; + + L.DomEvent + .on(link, 'click', L.DomEvent.stopPropagation) + .on(link, 'click', L.DomEvent.preventDefault) + .on(link, 'click', fn, context) + .on(link, 'dblclick', L.DomEvent.stopPropagation); + + return link; + } +}); + +L.Map.mergeOptions({ + zoomControl: true +}); + +L.Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new L.Control.Zoom(); + this.addControl(this.zoomControl); + } +}); + +L.control.zoom = function (options) { + return new L.Control.Zoom(options); +}; + +L.Control.Attribution = L.Control.extend({ + options: { + position: 'bottomright', + prefix: 'Powered by Leaflet' + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); + L.DomEvent.disableClickPropagation(this._container); + + map + .on('layeradd', this._onLayerAdd, this) + .on('layerremove', this._onLayerRemove, this); + + this._update(); + + return this._container; + }, + + onRemove: function (map) { + map + .off('layeradd', this._onLayerAdd) + .off('layerremove', this._onLayerRemove); + + }, + + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + addAttribution: function (text) { + if (!text) { return; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + removeAttribution: function (text) { + if (!text) { return; } + + this._attributions[text]--; + this._update(); + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions.hasOwnProperty(i) && this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' — '); + }, + + _onLayerAdd: function (e) { + if (e.layer.getAttribution) { + this.addAttribution(e.layer.getAttribution()); + } + }, + + _onLayerRemove: function (e) { + if (e.layer.getAttribution) { + this.removeAttribution(e.layer.getAttribution()); + } + } +}); + +L.Map.mergeOptions({ + attributionControl: true +}); + +L.Map.addInitHook(function () { + if (this.options.attributionControl) { + this.attributionControl = (new L.Control.Attribution()).addTo(this); + } +}); + +L.control.attribution = function (options) { + return new L.Control.Attribution(options); +}; + + +L.Control.Scale = L.Control.extend({ + options: { + position: 'bottomleft', + maxWidth: 100, + metric: true, + imperial: true, + updateWhenIdle: false + }, + + onAdd: function (map) { + this._map = map; + + var className = 'leaflet-control-scale', + container = L.DomUtil.create('div', className), + options = this.options; + + this._addScales(options, className, container); + + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + this._update(); + + return container; + }, + + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + }, + + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = L.DomUtil.create('div', className + '-line', container); + } + if (options.imperial) { + this._iScale = L.DomUtil.create('div', className + '-line', container); + } + }, + + _update: function () { + var bounds = this._map.getBounds(), + centerLat = bounds.getCenter().lat, + halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180), + dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180, + + size = this._map.getSize(), + options = this.options, + maxMeters = 0; + + if (size.x > 0) { + maxMeters = dist * (options.maxWidth / size.x); + } + + this._updateScales(options, maxMeters); + }, + + _updateScales: function (options, maxMeters) { + if (options.metric && maxMeters) { + this._updateMetric(maxMeters); + } + + if (options.imperial && maxMeters) { + this._updateImperial(maxMeters); + } + }, + + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters); + + this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px'; + this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; + }, + + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + scale = this._iScale, + maxMiles, miles, feet; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + + scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px'; + scale.innerHTML = miles + ' mi'; + + } else { + feet = this._getRoundNum(maxFeet); + + scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px'; + scale.innerHTML = feet + ' ft'; + } + }, + + _getScaleWidth: function (ratio) { + return Math.round(this.options.maxWidth * ratio) - 10; + }, + + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; + + d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; + + return pow10 * d; + } +}); + +L.control.scale = function (options) { + return new L.Control.Scale(options); +}; + + + +L.Control.Layers = L.Control.extend({ + options: { + collapsed: true, + position: 'topright', + autoZIndex: true + }, + + initialize: function (baseLayers, overlays, options) { + L.Util.setOptions(this, options); + + this._layers = {}; + this._lastZIndex = 0; + + for (var i in baseLayers) { + if (baseLayers.hasOwnProperty(i)) { + this._addLayer(baseLayers[i], i); + } + } + + for (i in overlays) { + if (overlays.hasOwnProperty(i)) { + this._addLayer(overlays[i], i, true); + } + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + return this._container; + }, + + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + this._update(); + return this; + }, + + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + this._update(); + return this; + }, + + removeLayer: function (layer) { + var id = L.Util.stamp(layer); + delete this._layers[id]; + this._update(); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = L.DomUtil.create('div', className); + + if (!L.Browser.touch) { + L.DomEvent.disableClickPropagation(container); + } else { + L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); + } + + var form = this._form = L.DomUtil.create('form', className + '-list'); + + if (this.options.collapsed) { + L.DomEvent + .on(container, 'mouseover', this._expand, this) + .on(container, 'mouseout', this._collapse, this); + + var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (L.Browser.touch) { + L.DomEvent + .on(link, 'click', L.DomEvent.stopPropagation) + .on(link, 'click', L.DomEvent.preventDefault) + .on(link, 'click', this._expand, this); + } + else { + L.DomEvent.on(link, 'focus', this._expand, this); + } + + this._map.on('movestart', this._collapse, this); + // TODO keyboard accessibility + } else { + this._expand(); + } + + this._baseLayersList = L.DomUtil.create('div', className + '-base', form); + this._separator = L.DomUtil.create('div', className + '-separator', form); + this._overlaysList = L.DomUtil.create('div', className + '-overlays', form); + + container.appendChild(form); + }, + + _addLayer: function (layer, name, overlay) { + var id = L.Util.stamp(layer); + + this._layers[id] = { + layer: layer, + name: name, + overlay: overlay + }; + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + }, + + _update: function () { + if (!this._container) { + return; + } + + this._baseLayersList.innerHTML = ''; + this._overlaysList.innerHTML = ''; + + var baseLayersPresent = false, + overlaysPresent = false; + + for (var i in this._layers) { + if (this._layers.hasOwnProperty(i)) { + var obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + } + } + + this._separator.style.display = (overlaysPresent && baseLayersPresent ? '' : 'none'); + }, + + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) + _createRadioElement: function (name, checked) { + + var radioHtml = 'px) + UNIT_RE: /^[\d\.]+(\D*)$/ + }, + + options: { + fps: 50 + }, + + initialize: function (el, options) { + this._el = el; + L.Util.extend(this.options, options); + + this._easing = L.Transition.EASINGS[this.options.easing] || L.Transition.EASINGS['ease-out']; + + this._step = L.Util.bind(this._step, this); + this._interval = Math.round(1000 / this.options.fps); + }, + + run: function (props) { + this._props = {}; + + var getters = L.Transition.CUSTOM_PROPS_GETTERS, + re = L.Transition.UNIT_RE; + + this.fire('start'); + + for (var prop in props) { + if (props.hasOwnProperty(prop)) { + var p = {}; + if (prop in getters) { + p.from = getters[prop](this._el); + } else { + var matches = this._el.style[prop].match(re); + p.from = parseFloat(matches[0]); + p.unit = matches[1]; + } + p.to = props[prop]; + this._props[prop] = p; + } + } + + clearInterval(this._timer); + this._timer = setInterval(this._step, this._interval); + this._startTime = L.Transition.getTime(); + }, + + _step: function () { + var time = L.Transition.getTime(), + elapsed = time - this._startTime, + duration = this.options.duration * 1000; + + if (elapsed < duration) { + this._runFrame(this._easing(elapsed / duration)); + } else { + this._runFrame(1); + this._complete(); + } + }, + + _runFrame: function (percentComplete) { + var setters = L.Transition.CUSTOM_PROPS_SETTERS, + prop, p, value; + + for (prop in this._props) { + if (this._props.hasOwnProperty(prop)) { + p = this._props[prop]; + if (prop in setters) { + value = p.to.subtract(p.from).multiplyBy(percentComplete).add(p.from); + setters[prop](this._el, value); + } else { + this._el.style[prop] = + ((p.to - p.from) * percentComplete + p.from) + p.unit; + } + } + } + this.fire('step'); + }, + + _complete: function () { + clearInterval(this._timer); + this.fire('end'); + } +}); + + + +L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : { + + setView: function (center, zoom, forceReset) { + zoom = this._limitZoom(zoom); + + var zoomChanged = (this._zoom !== zoom); + + if (this._loaded && !forceReset && this._layers) { + var done = (zoomChanged ? + this._zoomToIfClose && this._zoomToIfClose(center, zoom) : + this._panByIfClose(center)); + + // exit if animated pan or zoom started + if (done) { + clearTimeout(this._sizeTimer); + return this; + } + } + + // reset the map view + this._resetView(center, zoom); + + return this; + }, + + panBy: function (offset, options) { + offset = L.point(offset); + + if (!(offset.x || offset.y)) { + return this; + } + + if (!this._panTransition) { + this._panTransition = new L.Transition(this._mapPane); + + this._panTransition.on({ + 'step': this._onPanTransitionStep, + 'end': this._onPanTransitionEnd + }, this); + } + + L.Util.setOptions(this._panTransition, L.Util.extend({duration: 0.25}, options)); + + this.fire('movestart'); + + L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim'); + + this._panTransition.run({ + position: L.DomUtil.getPosition(this._mapPane).subtract(offset) + }); + + return this; + }, + + _onPanTransitionStep: function () { + this.fire('move'); + }, + + _onPanTransitionEnd: function () { + L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim'); + this.fire('moveend'); + }, + + _panByIfClose: function (center) { + // difference between the new and current centers in pixels + var offset = this._getCenterOffset(center)._floor(); + + if (this._offsetIsWithinView(offset)) { + this.panBy(offset); + return true; + } + return false; + }, + + _offsetIsWithinView: function (offset, multiplyFactor) { + var m = multiplyFactor || 1, + size = this.getSize(); + + return (Math.abs(offset.x) <= size.x * m) && + (Math.abs(offset.y) <= size.y * m); + } +}); + + +L.Map.mergeOptions({ + zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android23 && !L.Browser.mobileOpera +}); + +if (L.DomUtil.TRANSITION) { + L.Map.addInitHook(function () { + L.DomEvent.on(this._mapPane, L.Transition.END, this._catchTransitionEnd, this); + }); +} + +L.Map.include(!L.DomUtil.TRANSITION ? {} : { + + _zoomToIfClose: function (center, zoom) { + + if (this._animatingZoom) { return true; } + + if (!this.options.zoomAnimation) { return false; } + + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center).divideBy(1 - 1 / scale); + + // if offset does not exceed half of the view + if (!this._offsetIsWithinView(offset, 1)) { return false; } + + L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim'); + + this + .fire('movestart') + .fire('zoomstart'); + + this.fire('zoomanim', { + center: center, + zoom: zoom + }); + + var origin = this._getCenterLayerPoint().add(offset); + + this._prepareTileBg(); + this._runAnimation(center, zoom, scale, origin); + + return true; + }, + + _catchTransitionEnd: function (e) { + if (this._animatingZoom) { + this._onZoomTransitionEnd(); + } + }, + + _runAnimation: function (center, zoom, scale, origin, backwardsTransform) { + this._animateToCenter = center; + this._animateToZoom = zoom; + this._animatingZoom = true; + + var transform = L.DomUtil.TRANSFORM, + tileBg = this._tileBg; + + clearTimeout(this._clearTileBgTimer); + + //dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem + if (L.Browser.gecko || window.opera) { + tileBg.style[transform] += ' translate(0,0)'; + } + + L.Util.falseFn(tileBg.offsetWidth); //hack to make sure transform is updated before running animation + + var scaleStr = L.DomUtil.getScaleString(scale, origin), + oldTransform = tileBg.style[transform]; + + tileBg.style[transform] = backwardsTransform ? + oldTransform + ' ' + scaleStr : + scaleStr + ' ' + oldTransform; + }, + + _prepareTileBg: function () { + var tilePane = this._tilePane, + tileBg = this._tileBg; + + // If foreground layer doesn't have many tiles but bg layer does, keep the existing bg layer and just zoom it some more + if (tileBg && + this._getLoadedTilesPercentage(tileBg) > 0.5 && + this._getLoadedTilesPercentage(tilePane) < 0.5) { + + tilePane.style.visibility = 'hidden'; + tilePane.empty = true; + this._stopLoadingImages(tilePane); + return; + } + + if (!tileBg) { + tileBg = this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane); + tileBg.style.zIndex = 1; + } + + // prepare the background pane to become the main tile pane + tileBg.style[L.DomUtil.TRANSFORM] = ''; + tileBg.style.visibility = 'hidden'; + + // tells tile layers to reinitialize their containers + tileBg.empty = true; //new FG + tilePane.empty = false; //new BG + + //Switch out the current layer to be the new bg layer (And vice-versa) + this._tilePane = this._panes.tilePane = tileBg; + var newTileBg = this._tileBg = tilePane; + + L.DomUtil.addClass(newTileBg, 'leaflet-zoom-animated'); + + this._stopLoadingImages(newTileBg); + }, + + _getLoadedTilesPercentage: function (container) { + var tiles = container.getElementsByTagName('img'), + i, len, count = 0; + + for (i = 0, len = tiles.length; i < len; i++) { + if (tiles[i].complete) { + count++; + } + } + return count / len; + }, + + // stops loading all tiles in the background layer + _stopLoadingImages: function (container) { + var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')), + i, len, tile; + + for (i = 0, len = tiles.length; i < len; i++) { + tile = tiles[i]; + + if (!tile.complete) { + tile.onload = L.Util.falseFn; + tile.onerror = L.Util.falseFn; + tile.src = L.Util.emptyImageUrl; + + tile.parentNode.removeChild(tile); + } + } + }, + + _onZoomTransitionEnd: function () { + this._restoreTileFront(); + L.Util.falseFn(this._tileBg.offsetWidth); // force reflow + this._resetView(this._animateToCenter, this._animateToZoom, true, true); + + L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim'); + this._animatingZoom = false; + }, + + _restoreTileFront: function () { + this._tilePane.innerHTML = ''; + this._tilePane.style.visibility = ''; + this._tilePane.style.zIndex = 2; + this._tileBg.style.zIndex = 1; + }, + + _clearTileBg: function () { + if (!this._animatingZoom && !this.touchZoom._zooming) { + this._tileBg.innerHTML = ''; + } + } +}); + + +/* + * Provides L.Map with convenient shortcuts for W3C geolocation. + */ + +L.Map.include({ + _defaultLocateOptions: { + watch: false, + setView: false, + maxZoom: Infinity, + timeout: 10000, + maximumAge: 0, + enableHighAccuracy: false + }, + + locate: function (/*Object*/ options) { + + options = this._locationOptions = L.Util.extend(this._defaultLocateOptions, options); + + if (!navigator.geolocation) { + this._handleGeolocationError({ + code: 0, + message: "Geolocation not supported." + }); + return this; + } + + var onResponse = L.Util.bind(this._handleGeolocationResponse, this), + onError = L.Util.bind(this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + return this; + }, + + stopLocate: function () { + if (navigator.geolocation) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + return this; + }, + + _handleGeolocationError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? "permission denied" : + (c === 2 ? "position unavailable" : "timeout")); + + if (this._locationOptions.setView && !this._loaded) { + this.fitWorld(); + } + + this.fire('locationerror', { + code: c, + message: "Geolocation error: " + message + "." + }); + }, + + _handleGeolocationResponse: function (pos) { + var latAccuracy = 180 * pos.coords.accuracy / 4e7, + lngAccuracy = latAccuracy * 2, + + lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new L.LatLng(lat, lng), + + sw = new L.LatLng(lat - latAccuracy, lng - lngAccuracy), + ne = new L.LatLng(lat + latAccuracy, lng + lngAccuracy), + bounds = new L.LatLngBounds(sw, ne), + + options = this._locationOptions; + + if (options.setView) { + var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom); + this.setView(latlng, zoom); + } + + this.fire('locationfound', { + latlng: latlng, + bounds: bounds, + accuracy: pos.coords.accuracy + }); + } +}); + + + + +}(this)); \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.css b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.css new file mode 100755 index 0000000..c76c54a --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.css @@ -0,0 +1,379 @@ +/* required styles */ + +.leaflet-map-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-pane, +.leaflet-overlay-pane, +.leaflet-shadow-pane, +.leaflet-marker-pane, +.leaflet-popup-pane, +.leaflet-overlay-pane svg, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { /* TODO optimize classes */ + position: absolute; + } +.leaflet-container { + overflow: hidden; + outline: 0; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +.leaflet-clickable { + cursor: pointer; + } +.leaflet-dragging, .leaflet-dragging .leaflet-clickable { + cursor: move; + } +.leaflet-container img { + /* map is broken in FF if you have max-width: 100% on tiles */ + max-width: none !important; + } +.leaflet-container img.leaflet-image-layer { + /* stupid Android 2 doesn't understand "max-width: none" properly */ + max-width: 15000px !important; + } + +.leaflet-tile-pane { z-index: 2; } +.leaflet-objects-pane { z-index: 3; } +.leaflet-overlay-pane { z-index: 4; } +.leaflet-shadow-pane { z-index: 5; } +.leaflet-marker-pane { z-index: 6; } +.leaflet-popup-pane { z-index: 7; } + +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } + +.leaflet-zoom-box { + width: 0; + height: 0; + } + +/* Leaflet controls */ + +.leaflet-control { + position: relative; + z-index: 7; + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + +.leaflet-control-zoom { + -moz-border-radius: 7px; + -webkit-border-radius: 7px; + border-radius: 7px; + } +.leaflet-control-zoom { + padding: 5px; + background: rgba(0, 0, 0, 0.25); + } +.leaflet-control-zoom a { + background-color: rgba(255, 255, 255, 0.75); + } +.leaflet-control-zoom a, .leaflet-control-layers a { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-control-zoom a { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + width: 19px; + height: 19px; + } +.leaflet-control-zoom a:hover { + background-color: #fff; + } +.leaflet-touch .leaflet-control-zoom a { + width: 27px; + height: 27px; + } +.leaflet-control-zoom-in { + background-image: url(images/zoom-in.png); + margin-bottom: 5px; + } +.leaflet-control-zoom-out { + background-image: url(images/zoom-out.png); + } + +.leaflet-control-layers { + box-shadow: 0 1px 7px #999; + background: #f8f8f9; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-radius: 8px; + } +.leaflet-control-layers a { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-touch .leaflet-control-layers a { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + color: #333; + background: #fff; + } +.leaflet-control-layers input { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + +.leaflet-container .leaflet-control-attribution { + background-color: rgba(255, 255, 255, 0.7); + box-shadow: 0 0 5px #bbb; + margin: 0; + } + +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + } + +.leaflet-container .leaflet-control-attribution, +.leaflet-container .leaflet-control-scale { + font: 11px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + } + +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } + +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + color: black; + line-height: 1; + font-size: 10px; + padding-bottom: 2px; + text-shadow: 1px 1px 1px #fff; + background-color: rgba(255, 255, 255, 0.5); + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + padding-top: 1px; + border-bottom: none; + margin-top: -2px; + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers { + border: 5px solid #bbb; + } + + +/* Zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile, .leaflet-fade-anim .leaflet-popup { + opacity: 0; + + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-tile-loaded, .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } + +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); + -moz-transition: -moz-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); + -o-transition: -o-transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); + transition: transform 0.25s cubic-bezier(0.25,0.1,0.25,0.75); + } + +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile, +.leaflet-touching .leaflet-zoom-animated { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* Popup layout */ + +.leaflet-popup { + position: absolute; + text-align: center; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + } +.leaflet-popup-content { + margin: 14px 20px; + } +.leaflet-popup-tip-container { + margin: 0 auto; + width: 40px; + height: 20px; + position: relative; + overflow: hidden; + } +.leaflet-popup-tip { + width: 15px; + height: 15px; + padding: 1px; + + margin: -8px auto 0; + + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + padding: 4px 5px 0 0; + text-align: center; + width: 18px; + height: 14px; + font: 16px/14px Tahoma, Verdana, sans-serif; + color: #c3c3c3; + text-decoration: none; + font-weight: bold; + } +.leaflet-container a.leaflet-popup-close-button:hover { + color: #999; + } +.leaflet-popup-content p { + margin: 18px 0; + } +.leaflet-popup-scrolled { + overflow: auto; + border-bottom: 1px solid #ddd; + border-top: 1px solid #ddd; + } + + +/* Visual appearance */ + +.leaflet-container { + background: #ddd; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-container a.leaflet-active { + outline: 2px solid orange; + } +.leaflet-zoom-box { + border: 2px dotted #05f; + background: white; + opacity: 0.5; + } +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } +.leaflet-editing-icon { + border-radius: 2px; + } +.leaflet-popup-content-wrapper, .leaflet-popup-tip { + background: white; + + box-shadow: 0 3px 10px #888; + -moz-box-shadow: 0 3px 10px #888; + -webkit-box-shadow: 0 3px 14px #999; + } +.leaflet-popup-content-wrapper { + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + border-radius: 20px; + } +.leaflet-popup-content { + font: 12px/1.4 "Helvetica Neue", Arial, Helvetica, sans-serif; + } diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.ie.css b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.ie.css new file mode 100755 index 0000000..9d2a52f --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.ie.css @@ -0,0 +1,44 @@ +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + +.leaflet-control { + display: inline; + } + +.leaflet-popup-tip { + width: 21px; + _width: 27px; + margin: 0 auto; + _margin-top: -3px; + + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + } +.leaflet-popup-tip-container { + margin-top: -1px; + } +.leaflet-popup-content-wrapper, .leaflet-popup-tip { + border: 1px solid #bbb; + } + +.leaflet-control-zoom { + filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#3F000000',EndColorStr='#3F000000'); + } +.leaflet-control-zoom a { + background-color: #eee; + } +.leaflet-control-zoom a:hover { + background-color: #fff; + } +.leaflet-control-layers-toggle { + } +.leaflet-control-attribution, .leaflet-control-layers { + background: white; + } \ No newline at end of file diff --git a/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.js b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.js new file mode 100755 index 0000000..d2ce4f8 --- /dev/null +++ b/iOS/ExternalScreen/samples/fleet manager/www/js/leaflet/leaflet.js @@ -0,0 +1,6 @@ +/* + Copyright (c) 2010-2012, CloudMade, Vladimir Agafonkin + Leaflet is an open-source JavaScript library for mobile-friendly interactive maps. + http://leaflet.cloudmade.com +*/ +(function(e,t){var n,r;typeof exports!=t+""?n=exports:(r=e.L,n={},n.noConflict=function(){return e.L=r,this},e.L=n),n.version="0.4.4",n.Util={extend:function(e){var t=Array.prototype.slice.call(arguments,1);for(var n=0,r=t.length,i;n2?Array.prototype.slice.call(arguments,2):null;return function(){return e.apply(t,n||arguments)}},stamp:function(){var e=0,t="_leaflet_id";return function(n){return n[t]=n[t]||++e,n[t]}}(),limitExecByInterval:function(e,t,n){var r,i;return function s(){var o=arguments;if(r){i=!0;return}r=!0,setTimeout(function(){r=!1,i&&(s.apply(n,o),i=!1)},t),e.apply(n,o)}},falseFn:function(){return!1},formatNum:function(e,t){var n=Math.pow(10,t||5);return Math.round(e*n)/n},splitWords:function(e){return e.replace(/^\s+|\s+$/g,"").split(/\s+/)},setOptions:function(e,t){return e.options=n.Util.extend({},e.options,t),e.options},getParamString:function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n+"="+e[n]);return"?"+t.join("&")},template:function(e,t){return e.replace(/\{ *([\w_]+) *\}/g,function(e,n){var r=t[n];if(!t.hasOwnProperty(n))throw Error("No value provided for variable "+e);return r})},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function t(t){var n,r,i=["webkit","moz","o","ms"];for(n=0;n0},removeEventListener:function(e,t,r){var s=this[i],o,u,a,f,l;if(typeof e=="object"){for(o in e)e.hasOwnProperty(o)&&this.removeEventListener(o,e[o],t);return this}e=n.Util.splitWords(e);for(u=0,a=e.length;u=0;l--)(!t||f[l].action===t)&&(!r||f[l].context===r)&&f.splice(l,1)}return this},fireEvent:function(e,t){if(!this.hasEventListeners(e))return this;var r=n.Util.extend({type:e,target:this},t),s=this[i][e].slice();for(var o=0,u=s.length;o1||"matchMedia"in e&&e.matchMedia("(min-resolution:144dpi)").matches;n.Browser={ua:r,ie:i,ie6:s,webkit:o,gecko:u,opera:f,android:l,android23:c,chrome:a,ie3d:d,webkit3d:v,gecko3d:m,opera3d:g,any3d:!e.L_DISABLE_3D&&(d||v||m||g),mobile:h,mobileWebkit:h&&o,mobileWebkit3d:h&&v,mobileOpera:h&&f,touch:y,retina:b}}(),n.Point=function(e,t,n){this.x=n?Math.round(e):e,this.y=n?Math.round(t):t},n.Point.prototype={add:function(e){return this.clone()._add(n.point(e))},_add:function(e){return this.x+=e.x,this.y+=e.y,this},subtract:function(e){return this.clone()._subtract(n.point(e))},_subtract:function(e){return this.x-=e.x,this.y-=e.y,this},divideBy:function(e,t){return new n.Point(this.x/e,this.y/e,t)},multiplyBy:function(e,t){return new n.Point(this.x*e,this.y*e,t)},distanceTo:function(e){e=n.point(e);var t=e.x-this.x,r=e.y-this.y;return Math.sqrt(t*t+r*r)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},clone:function(){return new n.Point(this.x,this.y)},toString:function(){return"Point("+n.Util.formatNum(this.x)+", "+n.Util.formatNum(this.y)+")"}},n.point=function(e,t,r){return e instanceof n.Point?e:e instanceof Array?new n.Point(e[0],e[1]):isNaN(e)?e:new n.Point(e,t,r)},n.Bounds=n.Class.extend({initialize:function(e,t){if(!e)return;var n=t?[e,t]:e;for(var r=0,i=n.length;r=this.min.x&&r.x<=this.max.x&&t.y>=this.min.y&&r.y<=this.max.y},intersects:function(e){e=n.bounds(e);var t=this.min,r=this.max,i=e.min,s=e.max,o=s.x>=t.x&&i.x<=r.x,u=s.y>=t.y&&i.y<=r.y;return o&&u}}),n.bounds=function(e,t){return!e||e instanceof n.Bounds?e:new n.Bounds(e,t)},n.Transformation=n.Class.extend({initialize:function(e,t,n,r){this._a=e,this._b=t,this._c=n,this._d=r},transform:function(e,t){return this._transform(e.clone(),t)},_transform:function(e,t){return t=t||1,e.x=t*(this._a*e.x+this._b),e.y=t*(this._c*e.y+this._d),e},untransform:function(e,t){return t=t||1,new n.Point((e.x/t-this._b)/this._a,(e.y/t-this._d)/this._c)}}),n.DomUtil={get:function(e){return typeof e=="string"?document.getElementById(e):e},getStyle:function(e,t){var n=e.style[t];!n&&e.currentStyle&&(n=e.currentStyle[t]);if(!n||n==="auto"){var r=document.defaultView.getComputedStyle(e,null);n=r?r[t]:null}return n==="auto"?null:n},getViewportOffset:function(e){var t=0,r=0,i=e,s=document.body;do{t+=i.offsetTop||0,r+=i.offsetLeft||0;if(i.offsetParent===s&&n.DomUtil.getStyle(i,"position")==="absolute")break;if(n.DomUtil.getStyle(i,"position")==="fixed"){t+=s.scrollTop||0,r+=s.scrollLeft||0;break}i=i.offsetParent}while(i);i=e;do{if(i===s)break;t-=i.scrollTop||0,r-=i.scrollLeft||0,i=i.parentNode}while(i);return new n.Point(r,t)},create:function(e,t,n){var r=document.createElement(e);return r.className=t,n&&n.appendChild(r),r},disableTextSelection:function(){document.selection&&document.selection.empty&&document.selection.empty(),this._onselectstart||(this._onselectstart=document.onselectstart,document.onselectstart=n.Util.falseFn)},enableTextSelection:function(){document.onselectstart=this._onselectstart,this._onselectstart=null},hasClass:function(e,t){return e.className.length>0&&RegExp("(^|\\s)"+t+"(\\s|$)").test(e.className)},addClass:function(e,t){n.DomUtil.hasClass(e,t)||(e.className+=(e.className?" ":"")+t)},removeClass:function(e,t){function n(e,n){return n===t?"":e}e.className=e.className.replace(/(\S+)\s*/g,n).replace(/(^\s+|\s+$)/,"")},setOpacity:function(e,t){if("opacity"in e.style)e.style.opacity=t;else if(n.Browser.ie){var r=!1,i="DXImageTransform.Microsoft.Alpha";try{r=e.filters.item(i)}catch(s){}t=Math.round(t*100),r?(r.Enabled=t!==100,r.Opacity=t):e.style.filter+=" progid:"+i+"(opacity="+t+")"}},testProp:function(e){var t=document.documentElement.style;for(var n=0;n=t.lat&&s.lat<=r.lat&&i.lng>=t.lng&&s.lng<=r.lng},intersects:function(e){e=n.latLngBounds(e);var t=this._southWest,r=this._northEast,i=e.getSouthWest(),s=e.getNorthEast(),o=s.lat>=t.lat&&i.lat<=r.lat,u=s.lng>=t.lng&&i.lng<=r.lng;return o&&u},toBBoxString:function(){var e=this._southWest,t=this._northEast;return[e.lng,e.lat,t.lng,t.lat].join(",")},equals:function(e){return e?(e=n.latLngBounds(e),this._southWest.equals(e.getSouthWest())&&this._northEast.equals(e.getNorthEast())):!1}}),n.latLngBounds=function(e,t){return!e||e instanceof n.LatLngBounds?e:new n.LatLngBounds(e,t)},n.Projection={},n.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(e){var t=n.LatLng.DEG_TO_RAD,r=this.MAX_LATITUDE,i=Math.max(Math.min(r,e.lat),-r),s=e.lng*t,o=i*t;return o=Math.log(Math.tan(Math.PI/4+o/2)),new n.Point(s,o)},unproject:function(e){var t=n.LatLng.RAD_TO_DEG,r=e.x*t,i=(2*Math.atan(Math.exp(e.y))-Math.PI/2)*t;return new n.LatLng(i,r,!0)}},n.Projection.LonLat={project:function(e){return new n.Point(e.lng,e.lat)},unproject:function(e){return new n.LatLng(e.y,e.x,!0)}},n.CRS={latLngToPoint:function(e,t){var n=this.projection.project(e),r=this.scale(t);return this.transformation._transform(n,r)},pointToLatLng:function(e,t){var n=this.scale(t),r=this.transformation.untransform(e,n);return this.projection.unproject(r)},project:function(e){return this.projection.project(e)},scale:function(e){return 256*Math.pow(2,e)}},n.CRS.EPSG3857=n.Util.extend({},n.CRS,{code:"EPSG:3857",projection:n.Projection.SphericalMercator,transformation:new n.Transformation(.5/Math.PI,.5,-0.5/Math.PI,.5),project:function(e){var t=this.projection.project(e),n=6378137;return t.multiplyBy(n)}}),n.CRS.EPSG900913=n.Util.extend({},n.CRS.EPSG3857,{code:"EPSG:900913"}),n.CRS.EPSG4326=n.Util.extend({},n.CRS,{code:"EPSG:4326",projection:n.Projection.LonLat,transformation:new n.Transformation(1/360,.5,-1/360,.5)}),n.Map=n.Class.extend({includes:n.Mixin.Events,options:{crs:n.CRS.EPSG3857,fadeAnimation:n.DomUtil.TRANSITION&&!n.Browser.android23,trackResize:!0,markerZoomAnimation:n.DomUtil.TRANSITION&&n.Browser.any3d},initialize:function(e,r){r=n.Util.setOptions(this,r),this._initContainer(e),this._initLayout(),this._initHooks(),this._initEvents(),r.maxBounds&&this.setMaxBounds(r.maxBounds),r.center&&r.zoom!==t&&this.setView(n.latLng(r.center),r.zoom,!0),this._initLayers(r.layers)},setView:function(e,t){return this._resetView(n.latLng(e),this._limitZoom(t)),this},setZoom:function(e){return this.setView(this.getCenter(),e)},zoomIn:function(){return this.setZoom(this._zoom+1)},zoomOut:function(){return this.setZoom(this._zoom-1)},fitBounds:function(e){var t=this.getBoundsZoom(e);return this.setView(n.latLngBounds(e).getCenter(),t)},fitWorld:function(){var e=new n.LatLng(-60,-170),t=new n.LatLng(85,179);return this.fitBounds(new n.LatLngBounds(e,t))},panTo:function(e){return this.setView(e,this._zoom)},panBy:function(e){return this.fire("movestart"),this._rawPanBy(n.point(e)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(e){e=n.latLngBounds(e),this.options.maxBounds=e;if(!e)return this._boundsMinZoom=null,this;var t=this.getBoundsZoom(e,!0);return this._boundsMinZoom=t,this._loaded&&(this._zoomo.x&&(u=o.x-i.x),r.y>s.y&&(a=s.y-r.y),r.xc&&--h>0)d=u*Math.sin(f),p=Math.PI/2-2*Math.atan(a*Math.pow((1-d)/(1+d),.5*u))-f,f+=p;return new n.LatLng(f*t,s,!0)}},n.CRS.EPSG3395=n.Util.extend({},n.CRS,{code:"EPSG:3395",projection:n.Projection.Mercator,transformation:function(){var e=n.Projection.Mercator,t=e.R_MAJOR,r=e.R_MINOR;return new n.Transformation(.5/(Math.PI*t),.5,-0.5/(Math.PI*r),.5)}()}),n.TileLayer=n.Class.extend({includes:n.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:n.Browser.mobile,updateWhenIdle:n.Browser.mobile},initialize:function(e,t){t=n.Util.setOptions(this,t),t.detectRetina&&n.Browser.retina&&t.maxZoom>0&&(t.tileSize=Math.floor(t.tileSize/2),t.zoomOffset++,t.minZoom>0&&t.minZoom--,this.options.maxZoom--),this._url=e;var r=this.options.subdomains;typeof r=="string"&&(this.options.subdomains=r.split(""))},onAdd:function(e){this._map=e,this._initContainer(),this._createTileProto(),e.on({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||(this._limitedUpdate=n.Util.limitExecByInterval(this._update,150,this),e.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(e){return e.addLayer(this),this},onRemove:function(e){e._panes.tilePane.removeChild(this._container),e.off({viewreset:this._resetCallback,moveend:this._update},this),this.options.updateWhenIdle||e.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var e=this._map._panes.tilePane;return this._container&&(e.appendChild(this._container),this._setAutoZIndex(e,Math.max)),this},bringToBack:function(){var e=this._map._panes.tilePane;return this._container&&(e.insertBefore(this._container,e.firstChild),this._setAutoZIndex(e,Math.min)),this},getAttribution:function(){return this.options.attribution},setOpacity:function(e){return this.options.opacity=e,this._map&&this._updateOpacity(),this},setZIndex:function(e){return this.options.zIndex=e,this._updateZIndex(),this},setUrl:function(e,t){return this._url=e,t||this.redraw(),this},redraw:function(){return this._map&&(this._map._panes.tilePane.empty=!1,this._reset(!0),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==t&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(e,t){var n=e.getElementsByClassName("leaflet-layer"),r=-t(Infinity,-Infinity),i;for(var s=0,o=n.length;sthis.options.maxZoom||r=t)||e.y<0||e.y>=t)return!1}return!0},_removeOtherTiles:function(e){var t,n,r,i;for(i in this._tiles)this._tiles.hasOwnProperty(i)&&(t=i.split(":"),n=parseInt(t[0],10),r=parseInt(t[1],10),(ne.max.x||re.max.y)&&this._removeTile(i))},_removeTile:function(e){var t=this._tiles[e];this.fire("tileunload",{tile:t,url:t.src}),this.options.reuseTiles?(n.DomUtil.removeClass(t,"leaflet-tile-loaded"),this._unusedTiles.push(t)):t.parentNode===this._container&&this._container.removeChild(t),n.Browser.android||(t.src=n.Util.emptyImageUrl),delete this._tiles[e]},_addTile:function(e,t){var r=this._getTilePos(e),i=this._getTile();n.DomUtil.setPosition(i,r,n.Browser.chrome||n.Browser.android23),this._tiles[e.x+":"+e.y]=i,this._loadTile(i,e),i.parentNode!==this._container&&t.appendChild(i)},_getZoomForUrl:function(){var e=this.options,t=this._map.getZoom();return e.zoomReverse&&(t=e.maxZoom-t),t+e.zoomOffset},_getTilePos:function(e){var t=this._map.getPixelOrigin(),n=this.options.tileSize;return e.multiplyBy(n).subtract(t)},getTileUrl:function(e){return this._adjustTilePoint(e),n.Util.template(this._url,n.Util.extend({s:this._getSubdomain(e),z:this._getZoomForUrl(),x:e.x,y:e.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(e){var t=this._getWrapTileNum();!this.options.continuousWorld&&!this.options.noWrap&&(e.x=(e.x%t+t)%t),this.options.tms&&(e.y=t-e.y-1)},_getSubdomain:function(e){var t=(e.x+e.y)%this.options.subdomains.length;return this.options.subdomains[t]},_createTileProto:function(){var e=this._tileImg=n.DomUtil.create("img","leaflet-tile");e.galleryimg="no";var t=this.options.tileSize;e.style.width=t+"px",e.style.height=t+"px"},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var e=this._unusedTiles.pop();return this._resetTile(e),e}return this._createTile()},_resetTile:function(e){},_createTile:function(){var e=this._tileImg.cloneNode(!1);return e.onselectstart=e.onmousemove=n.Util.falseFn,e},_loadTile:function(e,t){e._layer=this,e.onload=this._tileOnLoad,e.onerror=this._tileOnError,e.src=this.getTileUrl(t)},_tileLoaded:function(){this._tilesToLoad--,this._tilesToLoad||this.fire("load")},_tileOnLoad:function(e){var t=this._layer;this.src!==n.Util.emptyImageUrl&&(n.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(e){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var n=t.options.errorTileUrl;n&&(this.src=n),t._tileLoaded()}}),n.tileLayer=function(e,t){return new n.TileLayer(e,t)},n.TileLayer.WMS=n.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(e,t){this._url=e;var r=n.Util.extend({},this.defaultWmsParams);t.detectRetina&&n.Browser.retina?r.width=r.height=this.options.tileSize*2:r.width=r.height=this.options.tileSize;for(var i in t)this.options.hasOwnProperty(i)||(r[i]=t[i]);this.wmsParams=r,n.Util.setOptions(this,t)},onAdd:function(e){var t=parseFloat(this.wmsParams.version)>=1.3?"crs":"srs";this.wmsParams[t]=e.options.crs.code,n.TileLayer.prototype.onAdd.call(this,e)},getTileUrl:function(e,t){var r=this._map,i=r.options.crs,s=this.options.tileSize,o=e.multiplyBy(s),u=o.add(new n.Point(s,s)),a=i.project(r.unproject(o,t)),f=i.project(r.unproject(u,t)),l=[a.x,f.y,f.x,a.y].join(","),c=n.Util.template(this._url,{s:this._getSubdomain(e)});return c+n.Util.getParamString(this.wmsParams)+"&bbox="+l},setParams:function(e,t){return n.Util.extend(this.wmsParams,e),t||this.redraw(),this}}),n.tileLayer.wms=function(e,t){return new n.TileLayer.WMS(e,t)},n.TileLayer.Canvas=n.TileLayer.extend({options:{async:!1},initialize:function(e){n.Util.setOptions(this,e)},redraw:function(){var e,t=this._tiles;for(e in t)t.hasOwnProperty(e)&&this._redrawTile(t[e])},_redrawTile:function(e){this.drawTile(e,e._tilePoint,e._zoom)},_createTileProto:function(){var e=this._canvasProto=n.DomUtil.create("canvas","leaflet-tile"),t=this.options.tileSize;e.width=t,e.height=t},_createTile:function(){var e=this._canvasProto.cloneNode(!1);return e.onselectstart=e.onmousemove=n.Util.falseFn,e},_loadTile:function(e,t,n){e._layer=this,e._tilePoint=t,e._zoom=n,this.drawTile(e,t,n),this.options.async||this.tileDrawn(e)},drawTile:function(e,t,n){},tileDrawn:function(e){this._tileOnLoad.call(e)}}),n.tileLayer.canvas=function(e){return new n.TileLayer.Canvas(e)},n.ImageOverlay=n.Class.extend({includes:n.Mixin.Events,options:{opacity:1},initialize:function(e,t,r){this._url=e,this._bounds=n.latLngBounds(t),n.Util.setOptions(this,r)},onAdd:function(e){this._map=e,this._image||this._initImage(),e._panes.overlayPane.appendChild(this._image),e.on("viewreset",this._reset,this),e.options.zoomAnimation&&n.Browser.any3d&&e.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(e){e.getPanes().overlayPane.removeChild(this._image),e.off("viewreset",this._reset,this),e.options.zoomAnimation&&e.off("zoomanim",this._animateZoom,this)},addTo:function(e){return e.addLayer(this),this},setOpacity:function(e){return this.options.opacity=e,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var e=this._map._panes.overlayPane;return this._image&&e.insertBefore(this._image,e.firstChild),this},_initImage:function(){this._image=n.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&n.Browser.any3d?n.DomUtil.addClass(this._image,"leaflet-zoom-animated"):n.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),n.Util.extend(this._image,{galleryimg:"no",onselectstart:n.Util.falseFn,onmousemove:n.Util.falseFn,onload:n.Util.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(e){var t=this._map,r=this._image,i=t.getZoomScale(e.zoom),s=this._bounds.getNorthWest(),o=this._bounds.getSouthEast(),u=t._latLngToNewLayerPoint(s,e.zoom,e.center),a=t._latLngToNewLayerPoint(o,e.zoom,e.center).subtract(u),f=t.latLngToLayerPoint(o).subtract(t.latLngToLayerPoint(s)),l=u.add(a.subtract(f).divideBy(2));r.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(l)+" scale("+i+") "},_reset:function(){var e=this._image,t=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),r=this._map.latLngToLayerPoint(this._bounds.getSouthEast()).subtract(t);n.DomUtil.setPosition(e,t),e.style.width=r.x+"px",e.style.height=r.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){n.DomUtil.setOpacity(this._image,this.options.opacity)}}),n.imageOverlay=function(e,t,r){return new n.ImageOverlay(e,t,r)},n.Icon=n.Class.extend({options:{className:""},initialize:function(e){n.Util.setOptions(this,e)},createIcon:function(){return this._createIcon("icon")},createShadow:function(){return this._createIcon("shadow")},_createIcon:function(e){var t=this._getIconUrl(e);if(!t){if(e==="icon")throw Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(t);return this._setIconStyles(n,e),n},_setIconStyles:function(e,t){var r=this.options,i=n.point(r[t+"Size"]),s;t==="shadow"?s=n.point(r.shadowAnchor||r.iconAnchor):s=n.point(r.iconAnchor),!s&&i&&(s=i.divideBy(2,!0)),e.className="leaflet-marker-"+t+" "+r.className,s&&(e.style.marginLeft=-s.x+"px",e.style.marginTop=-s.y+"px"),i&&(e.style.width=i.x+"px",e.style.height=i.y+"px")},_createImg:function(e){var t;return n.Browser.ie6?(t=document.createElement("div"),t.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+e+'")'):(t=document.createElement("img"),t.src=e),t},_getIconUrl:function(e){return this.options[e+"Url"]}}),n.icon=function(e){return new n.Icon(e)},n.Icon.Default=n.Icon.extend({options:{iconSize:new n.Point(25,41),iconAnchor:new n.Point(13,41),popupAnchor:new n.Point(1,-34),shadowSize:new n.Point(41,41)},_getIconUrl:function(e){var t=e+"Url";if(this.options[t])return this.options[t];var r=n.Icon.Default.imagePath;if(!r)throw Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return r+"/marker-"+e+".png"}}),n.Icon.Default.imagePath=function(){var e=document.getElementsByTagName("script"),t=/\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/,n,r,i,s;for(n=0,r=e.length;ns?(t.height=s+"px",n.DomUtil.addClass(e,o)):n.DomUtil.removeClass(e,o),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){var e=this._map.latLngToLayerPoint(this._latlng),t=n.Browser.any3d,r=this.options.offset;t&&n.DomUtil.setPosition(this._container,e),this._containerBottom=-r.y-(t?0:e.y),this._containerLeft=-Math.round(this._containerWidth/2)+r.x+(t?0:e.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"},_zoomAnimation:function(e){var t=this._map._latLngToNewLayerPoint(this._latlng,e.zoom,e.center);n.DomUtil.setPosition(this._container,t)},_adjustPan:function(){if(!this.options.autoPan)return;var e=this._map,t=this._container.offsetHeight,r=this._containerWidth,i=new n.Point(this._containerLeft,-t-this._containerBottom);n.Browser.any3d&&i._add(n.DomUtil.getPosition(this._container));var s=e.layerPointToContainerPoint(i),o=this.options.autoPanPadding,u=e.getSize(),a=0,f=0;s.x<0&&(a=s.x-o.x),s.x+r>u.x&&(a=s.x+r-u.x+o.x),s.y<0&&(f=s.y-o.y),s.y+t>u.y&&(f=s.y+t-u.y+o.y),(a||f)&&e.panBy(new n.Point(a,f))},_onCloseButtonClick:function(e){this._close(),n.DomEvent.stop(e)}}),n.popup=function(e,t){return new n.Popup(e,t)},n.Marker.include({openPopup:function(){return this._popup&&this._map&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},bindPopup:function(e,t){var r=n.point(this.options.icon.options.popupAnchor)||new n.Point(0,0);return r=r.add(n.Popup.prototype.options.offset),t&&t.offset&&(r=r.add(t.offset)),t=n.Util.extend({offset:r},t),this._popup||this.on("click",this.openPopup,this),this._popup=(new n.Popup(t,this)).setContent(e),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.openPopup)),this}}),n.Map.include({openPopup:function(e){return this.closePopup(),this._popup=e,this.addLayer(e).fire("popupopen",{popup:this._popup})},closePopup:function(){return this._popup&&this._popup._close(),this}}),n.LayerGroup=n.Class.extend({initialize:function(e){this._layers={};var t,n;if(e)for(t=0,n=e.length;t';var t=e.firstChild;return t.style.behavior="url(#default#VML)",t&&typeof t.adj=="object"}catch(n){return!1}}(),n.Path=n.Browser.svg||!n.Browser.vml?n.Path:n.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(e){return document.createElement("')}}catch(e){return function(e){return document.createElement("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var e=this._container=this._createElement("shape");n.DomUtil.addClass(e,"leaflet-vml-shape"),this.options.clickable&&n.DomUtil.addClass(e,"leaflet-clickable"),e.coordsize="1 1",this._path=this._createElement("path"),e.appendChild(this._path),this._map._pathRoot.appendChild(e)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var e=this._stroke,t=this._fill,n=this.options,r=this._container;r.stroked=n.stroke,r.filled=n.fill,n.stroke?(e||(e=this._stroke=this._createElement("stroke"),e.endcap="round",r.appendChild(e)),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=n.dashArray.replace(/ *, */g," "):e.dashStyle=""):e&&(r.removeChild(e),this._stroke=null),n.fill?(t||(t=this._fill=this._createElement("fill"),r.appendChild(t)),t.color=n.fillColor||n.color,t.opacity=n.fillOpacity):t&&(r.removeChild(t),this._fill=null)},_updatePath:function(){var e=this._container.style;e.display="none",this._path.v=this.getPathString()+" ",e.display=""}}),n.Map.include(n.Browser.svg||!n.Browser.vml?{}:{_initPathRoot:function(){if(this._pathRoot)return;var e=this._pathRoot=document.createElement("div");e.className="leaflet-vml-container",this._panes.overlayPane.appendChild(e),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}),n.Browser.canvas=function(){return!!document.createElement("canvas").getContext}(),n.Path=n.Path.SVG&&!e.L_PREFER_CANVAS||!n.Browser.canvas?n.Path:n.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(e){return n.Util.setOptions(this,e),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(e){e.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&(n.Util.cancelAnimFrame(this._fireMapMoveEnd),this._updateRequest=n.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var e=this.options;e.stroke&&(this._ctx.lineWidth=e.weight,this._ctx.strokeStyle=e.color),e.fill&&(this._ctx.fillStyle=e.fillColor||e.color)},_drawPath:function(){var e,t,r,i,s,o;this._ctx.beginPath();for(e=0,r=this._parts.length;es&&(o=u,s=a);s>n&&(t[o]=1,this._simplifyDPStep(e,t,n,r,o),this._simplifyDPStep(e,t,n,o,i))},_reducePoints:function(e,t){var n=[e[0]];for(var r=1,i=0,s=e.length;rt&&(n.push(e[r]),i=r);return it.max.x&&(n|=2),e.yt.max.y&&(n|=8),n},_sqDist:function(e,t){var n=t.x-e.x,r=t.y-e.y;return n*n+r*r},_sqClosestPointOnSegment:function(e,t,r,i){var s=t.x,o=t.y,u=r.x-s,a=r.y-o,f=u*u+a*a,l;return f>0&&(l=((e.x-s)*u+(e.y-o)*a)/f,l>1?(s=r.x,o=r.y):l>0&&(s+=u*l,o+=a*l)),u=e.x-s,a=e.y-o,i?u*u+a*a:new n.Point(s,o)}},n.Polyline=n.Path.extend({initialize:function(e,t){n.Path.prototype.initialize.call(this,t),this._latlngs=this._convertLatLngs(e),n.Handler.PolyEdit&&(this.editing=new n.Handler.PolyEdit(this),this.options.editable&&this.editing.enable())},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var e=0,t=this._latlngs.length;ee.max.x||n.y-t>e.max.y||n.x+te.y!=s.y>e.y&&e.x<(s.x-i.x)*(e.y-i.y)/(s.y-i.y)+i.x&&(t=!t)}return t}}:{}),n.Circle.include(n.Path.CANVAS?{_drawPath:function(){var e=this._point;this._ctx.beginPath(),this._ctx.arc(e.x,e.y,this._radius,0,Math.PI*2,!1)},_containsPoint:function(e){var t=this._point,n=this.options.stroke?this.options.weight/2:0;return e.distanceTo(t)<=this._radius+n}}:{}),n.GeoJSON=n.FeatureGroup.extend({initialize:function(e,t){n.Util.setOptions(this,t),this._layers={},e&&this.addData(e)},addData:function(e){var t=e instanceof Array?e:e.features,r,i;if(t){for(r=0,i=t.length;r1){this._simulateClick=!1;return}var t=e.touches&&e.touches.length===1?e.touches[0]:e,r=t.target;n.DomEvent.preventDefault(e),n.Browser.touch&&r.tagName.toLowerCase()==="a"&&n.DomUtil.addClass(r,"leaflet-active"),this._moved=!1;if(this._moving)return;this._startPos=this._newPos=n.DomUtil.getPosition(this._element),this._startPoint=new n.Point(t.clientX,t.clientY),n.DomEvent.on(document,n.Draggable.MOVE,this._onMove,this),n.DomEvent.on(document,n.Draggable.END,this._onUp,this)},_onMove:function(e){if(e.touches&&e.touches.length>1)return;var t=e.touches&&e.touches.length===1?e.touches[0]:e,r=new n.Point(t.clientX,t.clientY),i=r.subtract(this._startPoint);if(!i.x&&!i.y)return;n.DomEvent.preventDefault(e),this._moved||(this.fire("dragstart"),this._moved=!0,n.Browser.touch||(n.DomUtil.disableTextSelection(),this._setMovingCursor())),this._newPos=this._startPos.add(i),this._moving=!0,n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)},_updatePosition:function(){this.fire("predrag"),n.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(e){if(this._simulateClick&&e.changedTouches){var t=e.changedTouches[0],r=t.target,i=this._newPos&&this._newPos.distanceTo(this._startPos)||0;r.tagName.toLowerCase()==="a"&&n.DomUtil.removeClass(r,"leaflet-active"),i200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var e=this._map.getSize().divideBy(2),t=this._map.latLngToLayerPoint(new n.LatLng(0,0));this._initialWorldOffset=t.subtract(e).x,this._worldWidth=this._map.project(new n.LatLng(0,180)).x},_onPreDrag:function(){var e=this._map,t=this._worldWidth,n=Math.round(t/2),r=this._initialWorldOffset,i=this._draggable._newPos.x,s=(i-n+r)%t+n-r,o=(i+n+r)%t-n-r,u=Math.abs(s+r)r.inertiaThreshold||this._positions[0]===t;if(s)e.fire("moveend");else{var o=this._lastPos.subtract(this._positions[0]),u=(this._lastTime+i-this._times[0])/1e3,a=o.multiplyBy(.58/u),f=a.distanceTo(new n.Point(0,0)),l=Math.min(r.inertiaMaxSpeed,f),c=a.multiplyBy(l/f),h=l/r.inertiaDeceleration,p=c.multiplyBy(-h/2).round(),d={duration:h,easing:"ease-out"};n.Util.requestAnimFrame(n.Util.bind(function(){this._map.panBy(p,d)},this))}e.fire("dragend"),r.maxBounds&&n.Util.requestAnimFrame(this._panInsideMaxBounds,e,!0,e._container)},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)}}),n.Map.addInitHook("addHandler","dragging",n.Map.Drag),n.Map.mergeOptions({doubleClickZoom:!0}),n.Map.DoubleClickZoom=n.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick)},_onDoubleClick:function(e){this.setView(e.latlng,this._zoom+1)}}),n.Map.addInitHook("addHandler","doubleClickZoom",n.Map.DoubleClickZoom),n.Map.mergeOptions({scrollWheelZoom:!n.Browser.touch}),n.Map.ScrollWheelZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){n.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll)},_onWheelScroll:function(e){var t=n.DomEvent.getWheelDelta(e);this._delta+=t,this._lastMousePos=this._map.mouseEventToContainerPoint(e),clearTimeout(this._timer),this._timer=setTimeout(n.Util.bind(this._performZoom,this),40),n.DomEvent.preventDefault(e)},_performZoom:function(){var e=this._map,t=Math.round(this._delta),n=e.getZoom();t=Math.max(Math.min(t,4),-4),t=e._limitZoom(n+t)-n,this._delta=0;if(!t)return;var r=n+t,i=this._getCenterForScrollWheelZoom(this._lastMousePos,r);e.setView(i,r)},_getCenterForScrollWheelZoom:function(e,t){var n=this._map,r=n.getZoomScale(t),i=n.getSize().divideBy(2),s=e.subtract(i).multiplyBy(1-1/r),o=n._getTopLeftPoint().add(i).add(s);return n.unproject(o)}}),n.Map.addInitHook("addHandler","scrollWheelZoom",n.Map.ScrollWheelZoom),n.Util.extend(n.DomEvent,{addDoubleTapListener:function(e,t,n){function l(e){if(e.touches.length!==1)return;var t=Date.now(),n=t-(r||t);o=e.touches[0],i=n>0&&n<=s,r=t}function c(e){i&&(o.type="dblclick",t(o),r=null)}var r,i=!1,s=250,o,u="_leaflet_",a="touchstart",f="touchend";return e[u+a+n]=l,e[u+f+n]=c,e.addEventListener(a,l,!1),e.addEventListener(f,c,!1),this},removeDoubleTapListener:function(e,t){var n="_leaflet_";return e.removeEventListener(e,e[n+"touchstart"+t],!1),e.removeEventListener(e,e[n+"touchend"+t],!1),this}}),n.Map.mergeOptions({touchZoom:n.Browser.touch&&!n.Browser.android23}),n.Map.TouchZoom=n.Handler.extend({addHooks:function(){n.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){n.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(e){var t=this._map;if(!e.touches||e.touches.length!==2||t._animatingZoom||this._zooming)return;var r=t.mouseEventToLayerPoint(e.touches[0]),i=t.mouseEventToLayerPoint(e.touches[1]),s=t._getCenterLayerPoint();this._startCenter=r.add(i).divideBy(2,!0),this._startDist=r.distanceTo(i),this._moved=!1,this._zooming=!0,this._centerOffset=s.subtract(this._startCenter),n.DomEvent.on(document,"touchmove",this._onTouchMove,this).on(document,"touchend",this._onTouchEnd,this),n.DomEvent.preventDefault(e)},_onTouchMove:function(e){if(!e.touches||e.touches.length!==2)return;var t=this._map,r=t.mouseEventToLayerPoint(e.touches[0]),i=t.mouseEventToLayerPoint(e.touches[1]);this._scale=r.distanceTo(i)/this._startDist,this._delta=r.add(i).divideBy(2,!0).subtract(this._startCenter);if(this._scale===1)return;this._moved||(n.DomUtil.addClass(t._mapPane,"leaflet-zoom-anim leaflet-touching"),t.fire("movestart").fire("zoomstart")._prepareTileBg(),this._moved=!0),n.Util.cancelAnimFrame(this._animRequest),this._animRequest=n.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),n.DomEvent.preventDefault(e)},_updateOnMove:function(){var e=this._map,t=this._getScaleOrigin(),r=e.layerPointToLatLng(t);e.fire("zoomanim",{center:r,zoom:e.getScaleZoom(this._scale)}),e._tileBg.style[n.DomUtil.TRANSFORM]=n.DomUtil.getTranslateString(this._delta)+" "+n.DomUtil.getScaleString(this._scale,this._startCenter)},_onTouchEnd:function(e){if(!this._moved||!this._zooming)return;var t=this._map;this._zooming=!1,n.DomUtil.removeClass(t._mapPane,"leaflet-touching"),n.DomEvent.off(document,"touchmove",this._onTouchMove).off(document,"touchend",this._onTouchEnd);var r=this._getScaleOrigin(),i=t.layerPointToLatLng(r),s=t.getZoom(),o=t.getScaleZoom(this._scale)-s,u=o>0?Math.ceil(o):Math.floor(o),a=t._limitZoom(s+u);t.fire("zoomanim",{center:i,zoom:a}),t._runAnimation(i,a,t.getZoomScale(a)/this._scale,r,!0)},_getScaleOrigin:function(){var e=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(e)}}),n.Map.addInitHook("addHandler","touchZoom",n.Map.TouchZoom),n.Map.mergeOptions({boxZoom:!0}),n.Map.BoxZoom=n.Handler.extend({initialize:function(e){this._map=e,this._container=e._container,this._pane=e._panes.overlayPane},addHooks:function(){n.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){n.DomEvent.off(this._container,"mousedown",this._onMouseDown)},_onMouseDown:function(e){if(!e.shiftKey||e.which!==1&&e.button!==1)return!1;n.DomUtil.disableTextSelection(),this._startLayerPoint=this._map.mouseEventToLayerPoint(e),this._box=n.DomUtil.create("div","leaflet-zoom-box",this._pane),n.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",n.DomEvent.on(document,"mousemove",this._onMouseMove,this).on(document,"mouseup",this._onMouseUp,this).preventDefault(e),this._map.fire("boxzoomstart")},_onMouseMove:function(e){var t=this._startLayerPoint,r=this._box,i=this._map.mouseEventToLayerPoint(e),s=i.subtract(t),o=new n.Point(Math.min(i.x,t.x),Math.min(i.y,t.y));n.DomUtil.setPosition(r,o),r.style.width=Math.abs(s.x)-4+"px",r.style.height=Math.abs(s.y)-4+"px"},_onMouseUp:function(e){this._pane.removeChild(this._box),this._container.style.cursor="",n.DomUtil.enableTextSelection(),n.DomEvent.off(document,"mousemove",this._onMouseMove).off(document,"mouseup",this._onMouseUp);var t=this._map,r=t.mouseEventToLayerPoint(e),i=new n.LatLngBounds(t.layerPointToLatLng(this._startLayerPoint),t.layerPointToLatLng(r));t.fitBounds(i),t.fire("boxzoomend",{boxZoomBounds:i})}}),n.Map.addInitHook("addHandler","boxZoom",n.Map.BoxZoom),n.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),n.Map.Keyboard=n.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61],zoomOut:[189,109]},initialize:function(e){this._map=e,this._setPanOffset(e.options.keyboardPanOffset),this._setZoomOffset(e.options.keyboardZoomOffset)},addHooks:function(){var e=this._map._container;e.tabIndex===-1&&(e.tabIndex="0"),n.DomEvent.addListener(e,"focus",this._onFocus,this).addListener(e,"blur",this._onBlur,this).addListener(e,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var e=this._map._container;n.DomEvent.removeListener(e,"focus",this._onFocus,this).removeListener(e,"blur",this._onBlur,this).removeListener(e,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){this._focused||this._map._container.focus()},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(e){var t=this._panKeys={},n=this.keyCodes,r,i;for(r=0,i=n.left.length;re&&(n._index+=t)})},_createMiddleMarker:function(e,t){var n=this._getMiddleLatLng(e,t),r=this._createMarker(n),i,s,o;r.setOpacity(.6),e._middleRight=t._middleLeft=r,s=function(){var s=t._index;r._index=s,r.off("click",i).on("click",this._onMarkerClick,this),n.lat=r.getLatLng().lat,n.lng=r.getLatLng().lng,this._poly.spliceLatLngs(s,0,n),this._markers.splice(s,0,r),r.setOpacity(1),this._updateIndexes(s,1),t._index++,this._updatePrevNext(e,r),this._updatePrevNext(r,t)},o=function(){r.off("dragstart",s,this),r.off("dragend",o,this),this._createMiddleMarker(e,r),this._createMiddleMarker(r,t)},i=function(){s.call(this),o.call(this),this._poly.fire("edit")},r.on("click",i,this).on("dragstart",s,this).on("dragend",o,this),this._markerGroup.addLayer(r)},_updatePrevNext:function(e,t){e._next=t,t._prev=e},_getMiddleLatLng:function(e,t){var n=this._poly._map,r=n.latLngToLayerPoint(e.getLatLng()),i=n.latLngToLayerPoint(t.getLatLng());return n.layerPointToLatLng(r._add(i).divideBy(2))}}),n.Control=n.Class.extend({options:{position:"topright"},initialize:function(e){n.Util.setOptions(this,e)},getPosition:function(){return this.options.position},setPosition:function(e){var t=this._map;return t&&t.removeControl(this),this.options.position=e,t&&t.addControl(this),this},addTo:function(e){this._map=e;var t=this._container=this.onAdd(e),r=this.getPosition(),i=e._controlCorners[r];return n.DomUtil.addClass(t,"leaflet-control"),r.indexOf("bottom")!==-1?i.insertBefore(t,i.firstChild):i.appendChild(t),this},removeFrom:function(e){var t=this.getPosition(),n=e._controlCorners[t];return n.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(e),this}}),n.control=function(e){return new n.Control(e)},n.Map.include({addControl:function(e){return e.addTo(this),this},removeControl:function(e){return e.removeFrom(this),this},_initControlPos:function(){function i(i,s){var o=t+i+" "+t+s;e[i+s]=n.DomUtil.create("div",o,r)}var e=this._controlCorners={},t="leaflet-",r=this._controlContainer=n.DomUtil.create("div",t+"control-container",this._container);i("top","left"),i("top","right"),i("bottom","left"),i("bottom","right")}}),n.Control.Zoom=n.Control.extend({options:{position:"topleft"},onAdd:function(e){var t="leaflet-control-zoom",r=n.DomUtil.create("div",t);return this._createButton("Zoom in",t+"-in",r,e.zoomIn,e),this._createButton("Zoom out",t+"-out",r,e.zoomOut,e),r},_createButton:function(e,t,r,i,s){var o=n.DomUtil.create("a",t,r);return o.href="#",o.title=e,n.DomEvent.on(o,"click",n.DomEvent.stopPropagation).on(o,"click",n.DomEvent.preventDefault).on(o,"click",i,s).on(o,"dblclick",n.DomEvent.stopPropagation),o}}),n.Map.mergeOptions({zoomControl:!0}),n.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new n.Control.Zoom,this.addControl(this.zoomControl))}),n.control.zoom=function(e){return new n.Control.Zoom(e)},n.Control.Attribution=n.Control.extend({options:{position:"bottomright",prefix:'Powered by Leaflet'},initialize:function(e){n.Util.setOptions(this,e),this._attributions={}},onAdd:function(e){return this._container=n.DomUtil.create("div","leaflet-control-attribution"),n.DomEvent.disableClickPropagation(this._container),e.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(e){e.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(e){return this.options.prefix=e,this._update(),this},addAttribution:function(e){if(!e)return;return this._attributions[e]||(this._attributions[e]=0),this._attributions[e]++,this._update(),this},removeAttribution:function(e){if(!e)return;return this._attributions[e]--,this._update(),this},_update:function(){if(!this._map)return;var e=[];for(var t in this._attributions)this._attributions.hasOwnProperty(t)&&this._attributions[t]&&e.push(t);var n=[];this.options.prefix&&n.push(this.options.prefix),e.length&&n.push(e.join(", ")),this._container.innerHTML=n.join(" — ")},_onLayerAdd:function(e){e.layer.getAttribution&&this.addAttribution(e.layer.getAttribution())},_onLayerRemove:function(e){e.layer.getAttribution&&this.removeAttribution(e.layer.getAttribution())}}),n.Map.mergeOptions({attributionControl:!0}),n.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new n.Control.Attribution).addTo(this))}),n.control.attribution=function(e){return new n.Control.Attribution(e)},n.Control.Scale=n.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(e){this._map=e;var t="leaflet-control-scale",r=n.DomUtil.create("div",t),i=this.options;return this._addScales(i,t,r),e.on(i.updateWhenIdle?"moveend":"move",this._update,this),this._update(),r},onRemove:function(e){e.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(e,t,r){e.metric&&(this._mScale=n.DomUtil.create("div",t+"-line",r)),e.imperial&&(this._iScale=n.DomUtil.create("div",t+"-line",r))},_update:function(){var e=this._map.getBounds(),t=e.getCenter().lat,n=6378137*Math.PI*Math.cos(t*Math.PI/180),r=n*(e.getNorthEast().lng-e.getSouthWest().lng)/180,i=this._map.getSize(),s=this.options,o=0;i.x>0&&(o=r*(s.maxWidth/i.x)),this._updateScales(s,o)},_updateScales:function(e,t){e.metric&&t&&this._updateMetric(t),e.imperial&&t&&this._updateImperial(t)},_updateMetric:function(e){var t=this._getRoundNum(e);this._mScale.style.width=this._getScaleWidth(t/e)+"px",this._mScale.innerHTML=t<1e3?t+" m":t/1e3+" km"},_updateImperial:function(e){var t=e*3.2808399,n=this._iScale,r,i,s;t>5280?(r=t/5280,i=this._getRoundNum(r),n.style.width=this._getScaleWidth(i/r)+"px",n.innerHTML=i+" mi"):(s=this._getRoundNum(t),n.style.width=this._getScaleWidth(s/t)+"px",n.innerHTML=s+" ft")},_getScaleWidth:function(e){return Math.round(this.options.maxWidth*e)-10},_getRoundNum:function(e){var t=Math.pow(10,(Math.floor(e)+"").length-1),n=e/t;return n=n>=10?10:n>=5?5:n>=3?3:n>=2?2:1,t*n}}),n.control.scale=function(e){return new n.Control.Scale(e)},n.Control.Layers=n.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(e,t,r){n.Util.setOptions(this,r),this._layers={},this._lastZIndex=0;for(var i in e)e.hasOwnProperty(i)&&this._addLayer(e[i],i);for(i in t)t.hasOwnProperty(i)&&this._addLayer(t[i],i,!0)},onAdd:function(e){return this._initLayout(),this._update(),this._container},addBaseLayer:function(e,t){return this._addLayer(e,t),this._update(),this},addOverlay:function(e,t){return this._addLayer(e,t,!0),this._update(),this},removeLayer:function(e){var t=n.Util.stamp(e);return delete this._layers[t],this._update(),this},_initLayout:function(){var e="leaflet-control-layers",t=this._container=n.DomUtil.create("div",e);n.Browser.touch?n.DomEvent.on(t,"click",n.DomEvent.stopPropagation):n.DomEvent.disableClickPropagation(t);var r=this._form=n.DomUtil.create("form",e+"-list");if(this.options.collapsed){n.DomEvent.on(t,"mouseover",this._expand,this).on(t,"mouseout",this._collapse,this);var i=this._layersLink=n.DomUtil.create("a",e+"-toggle",t);i.href="#",i.title="Layers",n.Browser.touch?n.DomEvent.on(i,"click",n.DomEvent.stopPropagation).on(i,"click",n.DomEvent.preventDefault).on(i,"click",this._expand,this):n.DomEvent.on(i,"focus",this._expand,this),this._map.on("movestart",this._collapse,this)}else this._expand();this._baseLayersList=n.DomUtil.create("div",e+"-base",r),this._separator=n.DomUtil.create("div",e+"-separator",r),this._overlaysList=n.DomUtil.create("div",e+"-overlays",r),t.appendChild(r)},_addLayer:function(e,t,r){var i=n.Util.stamp(e);this._layers[i]={layer:e,name:t,overlay:r},this.options.autoZIndex&&e.setZIndex&&(this._lastZIndex++,e.setZIndex(this._lastZIndex))},_update:function(){if(!this._container)return;this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var e=!1,t=!1;for(var n in this._layers)if(this._layers.hasOwnProperty(n)){var r=this._layers[n];this._addItem(r),t=t||r.overlay,e=e||!r.overlay}this._separator.style.display=t&&e?"":"none"},_createRadioElement:function(e,t){var n='.5&&this._getLoadedTilesPercentage(e)<.5){e.style.visibility="hidden",e.empty=!0,this._stopLoadingImages(e);return}t||(t=this._tileBg=this._createPane("leaflet-tile-pane",this._mapPane),t.style.zIndex=1),t.style[n.DomUtil.TRANSFORM]="",t.style.visibility="hidden",t.empty=!0,e.empty=!1,this._tilePane=this._panes.tilePane=t;var r=this._tileBg=e;n.DomUtil.addClass(r,"leaflet-zoom-animated"),this._stopLoadingImages(r)},_getLoadedTilesPercentage:function(e){var t=e.getElementsByTagName("img"),n,r,i=0;for(n=0,r=t.length;n + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/iOS/ExtractZipFile/ExtractZipFilePlugin.h b/iOS/ExtractZipFile/ExtractZipFilePlugin.h new file mode 100644 index 0000000..e183408 --- /dev/null +++ b/iOS/ExtractZipFile/ExtractZipFilePlugin.h @@ -0,0 +1,20 @@ +// +// ExtractZipFilePlugin.h +// +// Created by Shaun Rowe on 10/05/2012. +// Copyright (c) 2012 Pobl Creative Cyf. All rights reserved. +// + +#import +#import "SSZipArchive.h" + +@interface ExtractZipFilePlugin : CDVPlugin +{ + NSString *callbackID; +} + +@property (nonatomic, copy) NSString* callbackID; + +- (void)extract:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + +@end diff --git a/iOS/ExtractZipFile/ExtractZipFilePlugin.m b/iOS/ExtractZipFile/ExtractZipFilePlugin.m new file mode 100644 index 0000000..d9d8618 --- /dev/null +++ b/iOS/ExtractZipFile/ExtractZipFilePlugin.m @@ -0,0 +1,32 @@ +// +// ExtractZipFilePlugin.m +// +// Created by Shaun Rowe on 10/05/2012. +// Copyright (c) 2012 Pobl Creative Cyf. All rights reserved. +// + +#import "ExtractZipFilePlugin.h" +#import "SSZipArchive.h" + +@implementation ExtractZipFilePlugin + +@synthesize callbackID; + +- (void)extract:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + callbackID = [arguments pop]; + + NSString *file = [arguments objectAtIndex:0]; + NSString *destination = [arguments objectAtIndex:1]; + + CDVPluginResult *result; + if([SSZipArchive unzipFileAtPath:file toDestination:destination delegate:nil]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[destination stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + [self writeJavascript:[result toSuccessCallbackString:callbackID]]; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[@"Could not extract archive" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + [self writeJavascript:[result toErrorCallbackString:callbackID]]; + } +} + +@end \ No newline at end of file diff --git a/iOS/ExtractZipFile/README.md b/iOS/ExtractZipFile/README.md new file mode 100644 index 0000000..f5092f3 --- /dev/null +++ b/iOS/ExtractZipFile/README.md @@ -0,0 +1,37 @@ +# ExtractZipFile Phonegap Plugin for iOS # +by Shaun Rowe (@shakie), Pobl Creative Cyf. (@poblcreative) + +This plugin allows you to extract a zip file + +## Adding the Plugin to your project ## + +To install the plugin, copy ZipPlugin.js to your project's www folder and include a reference to it in your html files. + + + +Add SSZipArchive directory, ExtractZipFilePlugin.h and ExtractZipFilePlugin.m to the Plugins directory of your phonegap project. + +In the Cordova.plist section, you need to add the plugin with the key/pair value of: + +ExtractZipFilePlugin ExtractZipFilePlugin + +## Using the plugin ## + + + + \ No newline at end of file diff --git a/iOS/ExtractZipFile/SSZipArchive/Changelog.markdown b/iOS/ExtractZipFile/SSZipArchive/Changelog.markdown new file mode 100644 index 0000000..294350b --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/Changelog.markdown @@ -0,0 +1,28 @@ +# SSZipArchive Changelog + +### Version 0.2.0 + +[Released May 7, 2012](https://github.com/samsoffes/sskeychain/tree/0.2.0) + +* Add unzipping delegate +* Fix unzipping with passwords +* Fix modified at dates + +### Version 0.1.2 + +[Released December 26, 2011](https://github.com/samsoffes/sskeychain/tree/0.1.2) + +* Add creation support. Thanks Johnnie Walker ([@randomsequence](https://github.com/randomsequence))! + +### Version 0.1.1 + +[Released October 31, 2011](https://github.com/samsoffes/sskeychain/tree/0.1.1) + +* Added basic tests +* Upgraded minizip to 1.1 to support zip64 + +### Version 0.1.0 + +[Released September 18, 2011](https://github.com/samsoffes/sskeychain/tree/0.1.0) + +* Initial release diff --git a/iOS/ExtractZipFile/SSZipArchive/LICENSE b/iOS/ExtractZipFile/SSZipArchive/LICENSE new file mode 100644 index 0000000..b904e35 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010-2011 Sam Soffes + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/iOS/ExtractZipFile/SSZipArchive/Readme.markdown b/iOS/ExtractZipFile/SSZipArchive/Readme.markdown new file mode 100644 index 0000000..4244bdf --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/Readme.markdown @@ -0,0 +1,43 @@ +# SSZipArchive + +SSZipArchive is a simple utility class for zipping and unzipping files. Features: + +* Unzipping zip files +* Unzipping password protected zip files +* Creating zip files +* Appending to zip files +* Zipping files +* Zipping NSData with a filename +* Works in ARC and non-ARC projects + +## Adding to your project + +1. Add `SSZipArchive.h`, `SSZipArchive.m`, and `minizip` to your project. +2. Add the `libz` library to your target + +You don't need to do anything regarding ARC. SSZipArchive will detect if you're not using ARC and add the required memory management code. + +## Usage + +``` objective-c +// Unzipping +NSString *zipPath = @"path_to_your_zip_file"; +NSString *destinationPath = @"path_to_the_folder_where_you_want_it_unzipped"; +[SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath]; + +// Zipping +NSString *zippedPath = @"path_where_you_want_the_file_created"; +NSArray *inputPaths = [NSArray arrayWithObjects: + [[NSBundle mainBundle] pathForResource:@"photo1" ofType:@"jpg"], + [[NSBundle mainBundle] pathForResource:@"photo2" ofType:@"jpg"] + nil]; +[SSZipArchive createZipFileAtPath:zippedPath withFilesAtPaths:inputPaths]; +``` + +## License + +SSZipArchive is licensed under the [MIT license](https://github.com/samsoffes/ssziparchive/raw/master/LICENSE). A slightly modified version of [Minizip](http://www.winimage.com/zLibDll/minizip.html) 1.1 is also included and is licensed under the [Zlib license](http://www.zlib.net/zlib_license.html). + +## Thanks + +Thanks [aish](http://code.google.com/p/ziparchive) for creating [ZipArchive](http://code.google.com/p/ziparchive) which SSZipArchive is based on, Johnnie Walker ([@randomsequence](https://github.com/randomsequence)) for implementing creation support, and John Engelhart ([@johnezang](https://github.com/johnezang)) for all his amazing help along the way. diff --git a/iOS/ExtractZipFile/SSZipArchive/SSZipArchive.h b/iOS/ExtractZipFile/SSZipArchive/SSZipArchive.h new file mode 100644 index 0000000..fb8d055 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/SSZipArchive.h @@ -0,0 +1,45 @@ +// +// SSZipArchive.h +// SSZipArchive +// +// Created by Sam Soffes on 7/21/10. +// Copyright (c) Sam Soffes 2010-2011. All rights reserved. +// + +#import +#include "minizip/unzip.h" + +@protocol SSZipArchiveDelegate; + +@interface SSZipArchive : NSObject + +// Unzip ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination; ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error; + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id)delegate; ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id)delegate; + +// Zip ++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)filenames; + +- (id)initWithPath:(NSString *)path; +- (BOOL)open; +- (BOOL)writeFile:(NSString *)path; +- (BOOL)writeData:(NSData *)data filename:(NSString *)filename; +- (BOOL)close; + +@end + + +@protocol SSZipArchiveDelegate + +@optional + +- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo; +- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath; + +- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; +- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; + +@end diff --git a/iOS/ExtractZipFile/SSZipArchive/SSZipArchive.m b/iOS/ExtractZipFile/SSZipArchive/SSZipArchive.m new file mode 100644 index 0000000..d07bf93 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/SSZipArchive.m @@ -0,0 +1,362 @@ +// +// SSZipArchive.m +// SSZipArchive +// +// Created by Sam Soffes on 7/21/10. +// Copyright (c) Sam Soffes 2010-2011. All rights reserved. +// + +#import "SSZipArchive.h" +#include "minizip/zip.h" +#import "zlib.h" +#import "zconf.h" + +#define CHUNK 16384 + +@interface SSZipArchive () ++ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime; +@end + + +@implementation SSZipArchive { + NSString *_path; + NSString *_filename; + zipFile _zip; +} + + +#pragma mark - Unzipping + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination { + return [self unzipFileAtPath:path toDestination:destination delegate:nil]; +} + + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error { + return [self unzipFileAtPath:path toDestination:destination overwrite:overwrite password:password error:error delegate:nil]; +} + + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id)delegate { + return [self unzipFileAtPath:path toDestination:destination overwrite:YES password:nil error:nil delegate:delegate]; +} + + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error delegate:(id)delegate { + // Begin opening + zipFile zip = unzOpen((const char*)[path UTF8String]); + if (zip == NULL) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"failed to open zip file" forKey:NSLocalizedDescriptionKey]; + if (error) { + *error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:-1 userInfo:userInfo]; + } + return NO; + } + + unz_global_info globalInfo = {0ul, 0ul}; + unzGetGlobalInfo(zip, &globalInfo); + + // Begin unzipping + if (unzGoToFirstFile(zip) != UNZ_OK) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"failed to open first file in zip file" forKey:NSLocalizedDescriptionKey]; + if (error) { + *error = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:-2 userInfo:userInfo]; + } + return NO; + } + + BOOL success = YES; + int ret = 0; + unsigned char buffer[4096] = {0}; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSMutableSet *directoriesModificationDates = [[NSMutableSet alloc] init]; + + // Message delegate + if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) { + [delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo]; + } + + NSInteger currentFileNumber = 0; + do { + if ([password length] == 0) { + ret = unzOpenCurrentFile(zip); + } else { + ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSASCIIStringEncoding]); + } + + if (ret != UNZ_OK) { + success = NO; + break; + } + + // Reading data and write to file + unz_file_info fileInfo; + memset(&fileInfo, 0, sizeof(unz_file_info)); + + ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + if (ret != UNZ_OK) { + success = NO; + unzCloseCurrentFile(zip); + break; + } + + // Message delegate + if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { + [delegate zipArchiveWillUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry + archivePath:path fileInfo:fileInfo]; + } + + char *filename = (char *)malloc(fileInfo.size_filename + 1); + unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0); + filename[fileInfo.size_filename] = '\0'; + + // Check if it contains directory + NSString *strPath = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; + BOOL isDirectory = NO; + if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') { + isDirectory = YES; + } + free(filename); + + // Contains a path + if ([strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location != NSNotFound) { + strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]; + } + + NSString *fullPath = [destination stringByAppendingPathComponent:strPath]; + NSError *err = nil; + NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate]; + NSDictionary *directoryAttr = [NSDictionary dictionaryWithObjectsAndKeys:modDate, NSFileCreationDate, modDate, NSFileModificationDate, nil]; + + if (isDirectory) { + [fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err]; + } else { + [fileManager createDirectoryAtPath:[fullPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:directoryAttr error:&err]; + } + if (nil != err) { + NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription); + } + + [directoriesModificationDates addObject: [NSDictionary dictionaryWithObjectsAndKeys:fullPath, @"path", modDate, @"modDate", nil]]; + + if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) { + unzCloseCurrentFile(zip); + ret = unzGoToNextFile(zip); + continue; + } + + FILE *fp = fopen((const char*)[fullPath UTF8String], "wb"); + while (fp) { + int readBytes = unzReadCurrentFile(zip, buffer, 4096); + + if (readBytes > 0) { + fwrite(buffer, readBytes, 1, fp ); + } else { + break; + } + } + + if (fp) { + fclose(fp); + + // Set the original datetime property + if (fileInfo.dosDate != 0) { + NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate]; + NSDictionary *attr = [NSDictionary dictionaryWithObject:orgDate forKey:NSFileModificationDate]; + + if (attr) { + if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) { + // Can't set attributes + NSLog(@"[SSZipArchive] Failed to set attributes"); + } + } + } + } + + unzCloseCurrentFile( zip ); + ret = unzGoToNextFile( zip ); + + // Message delegate + if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { + [delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry + archivePath:path fileInfo:fileInfo]; + } + + currentFileNumber++; + } while(ret == UNZ_OK && UNZ_OK != UNZ_END_OF_LIST_OF_FILE); + + // Close + unzClose(zip); + + // The process of decompressing the .zip archive causes the modification times on the folders + // to be set to the present time. So, when we are done, they need to be explicitly set. + // set the modification date on all of the directories. + NSError * err = nil; + for (NSDictionary * d in directoriesModificationDates) { + if (![[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[d objectForKey:@"modDate"], NSFileModificationDate, nil] ofItemAtPath:[d objectForKey:@"path"] error:&err]) { + NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", [d objectForKey:@"path"]); + } + if (err) { + NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@",err.localizedDescription); + } + } + +#if !__has_feature(objc_arc) + [directoriesModificationDates release]; +#endif + + // Message delegate + if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) { + [delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination]; + } + + return success; +} + + +#pragma mark - Zipping + ++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths { + BOOL success = NO; + SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path]; + if ([zipArchive open]) { + for (NSString *path in paths) { + [zipArchive writeFile:path]; + } + success = [zipArchive close]; + } + +#if !__has_feature(objc_arc) + [zipArchive release]; +#endif + + return success; +} + + +- (id)initWithPath:(NSString *)path { + if ((self = [super init])) { + _path = [path copy]; + } + return self; +} + + +#if !__has_feature(objc_arc) +- (void)dealloc { + [_path release]; + [super dealloc]; +} +#endif + + +- (BOOL)open { + NSAssert((_zip == NULL), @"Attempting open an archive which is already open"); + _zip = zipOpen([_path UTF8String], APPEND_STATUS_CREATE); + return (NULL != _zip); +} + + +- (void)zipInfo:(zip_fileinfo*)zipInfo setDate:(NSDate*)date { + NSCalendar *currentCalendar = [NSCalendar currentCalendar]; + uint flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; + NSDateComponents *components = [currentCalendar components:flags fromDate:date]; + zipInfo->tmz_date.tm_sec = (unsigned int)components.second; + zipInfo->tmz_date.tm_min = (unsigned int)components.minute; + zipInfo->tmz_date.tm_hour = (unsigned int)components.hour; + zipInfo->tmz_date.tm_mday = (unsigned int)components.day; + zipInfo->tmz_date.tm_mon = (unsigned int)components.month - 1; + zipInfo->tmz_date.tm_year = (unsigned int)components.year; +} + + +- (BOOL)writeFile:(NSString *)path { + NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened"); + + FILE *input = fopen([path UTF8String], "r"); + if (NULL == input) { + return NO; + } + + zipOpenNewFileInZip(_zip, [[path lastPathComponent] UTF8String], NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + void *buffer = malloc(CHUNK); + unsigned int len = 0; + while (!feof(input)) { + len = (unsigned int) fread(buffer, 1, CHUNK, input); + zipWriteInFileInZip(_zip, buffer, len); + } + + zipCloseFileInZip(_zip); + free(buffer); + return YES; +} + + +- (BOOL)writeData:(NSData *)data filename:(NSString *)filename { + if (!_zip) { + return NO; + } + if (!data) { + return NO; + } + zip_fileinfo zipInfo = {0}; + [self zipInfo:&zipInfo setDate:[NSDate date]]; + + zipOpenNewFileInZip(_zip, [filename UTF8String], &zipInfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(_zip, data.bytes, (unsigned int)data.length); + + zipCloseFileInZip(_zip); + return YES; +} + + +- (BOOL)close { + NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened"); + zipClose(_zip, NULL); + return YES; +} + + +#pragma mark - Private + +// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html +// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss +// YYYYYYY is years from 1980 = 0 +// sssss is (seconds/2). +// +// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24 +// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 2 = 14:33:06 ++ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime { + static const UInt32 kYearMask = 0xFE000000; + static const UInt32 kMonthMask = 0x1E00000; + static const UInt32 kDayMask = 0x1F0000; + static const UInt32 kHourMask = 0xF800; + static const UInt32 kMinuteMask = 0x7E0; + static const UInt32 kSecondMask = 0x1F; + + NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; + NSDateComponents *components = [[NSDateComponents alloc] init]; + + NSAssert(0xFFFFFFFF == (kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask), @"[SSZipArchive] MSDOS date masks don't add up"); + + [components setYear:1980 + ((msdosDateTime & kYearMask) >> 25)]; + [components setMonth:(msdosDateTime & kMonthMask) >> 21]; + [components setDay:(msdosDateTime & kDayMask) >> 16]; + [components setHour:(msdosDateTime & kHourMask) >> 11]; + [components setMinute:(msdosDateTime & kMinuteMask) >> 5]; + [components setSecond:(msdosDateTime & kSecondMask) * 2]; + + NSDate *date = [NSDate dateWithTimeInterval:0 sinceDate:[gregorian dateFromComponents:components]]; + +#if !__has_feature(objc_arc) + [gregorian release]; + [components release]; +#endif + + return date; +} + +@end diff --git a/iOS/ExtractZipFile/SSZipArchive/Tests/IncorrectHeaders.zip b/iOS/ExtractZipFile/SSZipArchive/Tests/IncorrectHeaders.zip new file mode 100644 index 0000000..b70f748 Binary files /dev/null and b/iOS/ExtractZipFile/SSZipArchive/Tests/IncorrectHeaders.zip differ diff --git a/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchive.xcodeproj/project.pbxproj b/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchive.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f398753 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchive.xcodeproj/project.pbxproj @@ -0,0 +1,314 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + B215FB32143AD3C7003AC546 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B215FB31143AD3C7003AC546 /* SenTestingKit.framework */; }; + B215FB63143AD514003AC546 /* SSZipArchiveTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B215FB61143AD514003AC546 /* SSZipArchiveTests.m */; }; + B215FB65143AD527003AC546 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B215FB64143AD527003AC546 /* libz.dylib */; }; + B215FB66143AD52D003AC546 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B215FB18143AD3C7003AC546 /* Foundation.framework */; }; + B215FB67143AD56D003AC546 /* SSZipArchive.m in Sources */ = {isa = PBXBuildFile; fileRef = B215FB58143AD460003AC546 /* SSZipArchive.m */; }; + B215FB68143AD576003AC546 /* ioapi.c in Sources */ = {isa = PBXBuildFile; fileRef = B215FB4F143AD460003AC546 /* ioapi.c */; }; + B215FB69143AD576003AC546 /* mztools.c in Sources */ = {isa = PBXBuildFile; fileRef = B215FB51143AD460003AC546 /* mztools.c */; }; + B215FB6A143AD576003AC546 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = B215FB53143AD460003AC546 /* unzip.c */; }; + B215FB6B143AD576003AC546 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = B215FB55143AD460003AC546 /* zip.c */; }; + B215FB6D143AD6FF003AC546 /* TestArchive.zip in Resources */ = {isa = PBXBuildFile; fileRef = B215FB6C143AD6FF003AC546 /* TestArchive.zip */; }; + B23FCC7F1558F1B70026375C /* TestPasswordArchive.zip in Resources */ = {isa = PBXBuildFile; fileRef = B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */; }; + C5AE4E64155A12760045F3ED /* IncorrectHeaders.zip in Resources */ = {isa = PBXBuildFile; fileRef = C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + B215FB18143AD3C7003AC546 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + B215FB30143AD3C7003AC546 /* SSZipArchiveTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SSZipArchiveTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + B215FB31143AD3C7003AC546 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; + B215FB4E143AD460003AC546 /* crypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypt.h; sourceTree = ""; }; + B215FB4F143AD460003AC546 /* ioapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ioapi.c; sourceTree = ""; }; + B215FB50143AD460003AC546 /* ioapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ioapi.h; sourceTree = ""; }; + B215FB51143AD460003AC546 /* mztools.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mztools.c; sourceTree = ""; }; + B215FB52143AD460003AC546 /* mztools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mztools.h; sourceTree = ""; }; + B215FB53143AD460003AC546 /* unzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unzip.c; sourceTree = ""; }; + B215FB54143AD460003AC546 /* unzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unzip.h; sourceTree = ""; }; + B215FB55143AD460003AC546 /* zip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zip.c; sourceTree = ""; }; + B215FB56143AD460003AC546 /* zip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zip.h; sourceTree = ""; }; + B215FB57143AD460003AC546 /* SSZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SSZipArchive.h; path = ../SSZipArchive.h; sourceTree = ""; }; + B215FB58143AD460003AC546 /* SSZipArchive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SSZipArchive.m; path = ../SSZipArchive.m; sourceTree = ""; }; + B215FB5F143AD514003AC546 /* SSZipArchiveTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SSZipArchiveTests-Info.plist"; sourceTree = ""; }; + B215FB61143AD514003AC546 /* SSZipArchiveTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSZipArchiveTests.m; sourceTree = ""; }; + B215FB64143AD527003AC546 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + B215FB6C143AD6FF003AC546 /* TestArchive.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = TestArchive.zip; sourceTree = ""; }; + B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = TestPasswordArchive.zip; sourceTree = ""; }; + C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = IncorrectHeaders.zip; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B215FB2C143AD3C7003AC546 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B215FB65143AD527003AC546 /* libz.dylib in Frameworks */, + B215FB66143AD52D003AC546 /* Foundation.framework in Frameworks */, + B215FB32143AD3C7003AC546 /* SenTestingKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B215FB04143AD3C7003AC546 = { + isa = PBXGroup; + children = ( + B215FB4B143AD449003AC546 /* SSZipArchive */, + B215FB5E143AD505003AC546 /* SSZipArchiveTests */, + B215FB12143AD3C7003AC546 /* Frameworks */, + B215FB10143AD3C7003AC546 /* Products */, + ); + sourceTree = ""; + }; + B215FB10143AD3C7003AC546 /* Products */ = { + isa = PBXGroup; + children = ( + B215FB30143AD3C7003AC546 /* SSZipArchiveTests.octest */, + ); + name = Products; + sourceTree = ""; + }; + B215FB12143AD3C7003AC546 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B215FB64143AD527003AC546 /* libz.dylib */, + B215FB31143AD3C7003AC546 /* SenTestingKit.framework */, + B215FB18143AD3C7003AC546 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B215FB4B143AD449003AC546 /* SSZipArchive */ = { + isa = PBXGroup; + children = ( + B215FB57143AD460003AC546 /* SSZipArchive.h */, + B215FB58143AD460003AC546 /* SSZipArchive.m */, + B215FB4D143AD460003AC546 /* minizip */, + ); + name = SSZipArchive; + sourceTree = ""; + }; + B215FB4D143AD460003AC546 /* minizip */ = { + isa = PBXGroup; + children = ( + B215FB4E143AD460003AC546 /* crypt.h */, + B215FB4F143AD460003AC546 /* ioapi.c */, + B215FB50143AD460003AC546 /* ioapi.h */, + B215FB51143AD460003AC546 /* mztools.c */, + B215FB52143AD460003AC546 /* mztools.h */, + B215FB53143AD460003AC546 /* unzip.c */, + B215FB54143AD460003AC546 /* unzip.h */, + B215FB55143AD460003AC546 /* zip.c */, + B215FB56143AD460003AC546 /* zip.h */, + ); + name = minizip; + path = ../minizip; + sourceTree = ""; + }; + B215FB5E143AD505003AC546 /* SSZipArchiveTests */ = { + isa = PBXGroup; + children = ( + B215FB61143AD514003AC546 /* SSZipArchiveTests.m */, + B215FB5F143AD514003AC546 /* SSZipArchiveTests-Info.plist */, + C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */, + B215FB6C143AD6FF003AC546 /* TestArchive.zip */, + B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */, + ); + name = SSZipArchiveTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B215FB2F143AD3C7003AC546 /* SSZipArchiveTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B215FB44143AD3C7003AC546 /* Build configuration list for PBXNativeTarget "SSZipArchiveTests" */; + buildPhases = ( + B215FB2B143AD3C7003AC546 /* Sources */, + B215FB2C143AD3C7003AC546 /* Frameworks */, + B215FB2D143AD3C7003AC546 /* Resources */, + B215FB2E143AD3C7003AC546 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SSZipArchiveTests; + productName = SSZipArchiveTests; + productReference = B215FB30143AD3C7003AC546 /* SSZipArchiveTests.octest */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B215FB06143AD3C7003AC546 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0440; + ORGANIZATIONNAME = "Sam Soffes"; + }; + buildConfigurationList = B215FB09143AD3C7003AC546 /* Build configuration list for PBXProject "SSZipArchive" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = B215FB04143AD3C7003AC546; + productRefGroup = B215FB10143AD3C7003AC546 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B215FB2F143AD3C7003AC546 /* SSZipArchiveTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B215FB2D143AD3C7003AC546 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B215FB6D143AD6FF003AC546 /* TestArchive.zip in Resources */, + B23FCC7F1558F1B70026375C /* TestPasswordArchive.zip in Resources */, + C5AE4E64155A12760045F3ED /* IncorrectHeaders.zip in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + B215FB2E143AD3C7003AC546 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B215FB2B143AD3C7003AC546 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B215FB63143AD514003AC546 /* SSZipArchiveTests.m in Sources */, + B215FB67143AD56D003AC546 /* SSZipArchive.m in Sources */, + B215FB68143AD576003AC546 /* ioapi.c in Sources */, + B215FB69143AD576003AC546 /* mztools.c in Sources */, + B215FB6A143AD576003AC546 /* unzip.c in Sources */, + B215FB6B143AD576003AC546 /* zip.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B215FB3F143AD3C7003AC546 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + B215FB40143AD3C7003AC546 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + SDKROOT = macosx; + }; + name = Release; + }; + B215FB45143AD3C7003AC546 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "SSZipArchiveTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + B215FB46143AD3C7003AC546 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "SSZipArchiveTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = octest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B215FB09143AD3C7003AC546 /* Build configuration list for PBXProject "SSZipArchive" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B215FB3F143AD3C7003AC546 /* Debug */, + B215FB40143AD3C7003AC546 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B215FB44143AD3C7003AC546 /* Build configuration list for PBXNativeTarget "SSZipArchiveTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B215FB45143AD3C7003AC546 /* Debug */, + B215FB46143AD3C7003AC546 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B215FB06143AD3C7003AC546 /* Project object */; +} diff --git a/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchiveTests-Info.plist b/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchiveTests-Info.plist new file mode 100644 index 0000000..6b8a339 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchiveTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.samsoffes.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchiveTests.m b/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchiveTests.m new file mode 100644 index 0000000..6bb542c --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/Tests/SSZipArchiveTests.m @@ -0,0 +1,151 @@ +// +// SSZipArchiveTests.m +// SSZipArchiveTests +// +// Created by Sam Soffes on 10/3/11. +// Copyright (c) 2011 Sam Soffes. All rights reserved. +// + +#import "SSZipArchive.h" +#import +#import + +@interface SSZipArchiveTests : SenTestCase + +- (NSString *)_cachesPath:(NSString *)directory; + +- (NSString *)_calculateMD5Digest:(NSData *)data; + +@end + +@implementation SSZipArchiveTests + +- (void)setUp { + [[NSFileManager defaultManager] removeItemAtPath:[self _cachesPath:nil] error:nil]; +} + + +- (void)testZipping { + NSString *outputPath = [self _cachesPath:@"Zipped"]; + NSArray *inputPaths = [NSArray arrayWithObjects: + [outputPath stringByAppendingPathComponent:@"Readme.markdown"], + [outputPath stringByAppendingPathComponent:@"LICENSE"], + nil]; + NSString *archivePath = [outputPath stringByAppendingPathComponent:@"CreatedArchive.zip"]; + [SSZipArchive createZipFileAtPath:archivePath withFilesAtPaths:inputPaths]; + + // TODO: Make sure the files are actually unzipped. They are, but the test should be better. + STAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:archivePath], @"Archive created"); +} + + +- (void)testUnzipping { + NSString *zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestArchive" ofType:@"zip"]; + NSString *outputPath = [self _cachesPath:@"Regular"]; + + [SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath delegate:self]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *testPath = [outputPath stringByAppendingPathComponent:@"Readme.markdown"]; + STAssertTrue([fileManager fileExistsAtPath:testPath], @"Readme unzipped"); + + testPath = [outputPath stringByAppendingPathComponent:@"LICENSE"]; + STAssertTrue([fileManager fileExistsAtPath:testPath], @"LICENSE unzipped"); +} + + +- (void)testUnzippingWithPassword { + NSString *zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestPasswordArchive" ofType:@"zip"]; + NSString *outputPath = [self _cachesPath:@"Password"]; + + NSError *error = nil; + [SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath overwrite:YES password:@"passw0rd" error:&error delegate:self]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *testPath = [outputPath stringByAppendingPathComponent:@"Readme.markdown"]; + STAssertTrue([fileManager fileExistsAtPath:testPath], @"Readme unzipped"); + + testPath = [outputPath stringByAppendingPathComponent:@"LICENSE"]; + STAssertTrue([fileManager fileExistsAtPath:testPath], @"LICENSE unzipped"); +} + +- (void)testUnzippingTruncatedFileFix { + NSString* zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"IncorrectHeaders" ofType:@"zip"]; + NSString* outputPath = [self _cachesPath:@"IncorrectHeaders"]; + + [SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath delegate:self]; + + NSString* intendedReadmeTxtMD5 = @"31ac96301302eb388070c827447290b5"; + + NSString* filePath = [outputPath stringByAppendingPathComponent:@"IncorrectHeaders/Readme.txt"]; + NSData* data = [NSData dataWithContentsOfFile:filePath]; + + NSString* actualReadmeTxtMD5 = [self _calculateMD5Digest:data]; + STAssertTrue([actualReadmeTxtMD5 isEqualToString:intendedReadmeTxtMD5], @"Readme.txt MD5 digest should match original."); +} + + +// Commented out to avoid checking in several gig file into the repository. Simply add a file named +// `LargeArchive.zip` to the project and uncomment out these lines to test. +// +//- (void)testUnzippingLargeFiles { +// NSString *zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"LargeArchive" ofType:@"zip"]; +// NSString *outputPath = [self _cachesPath:@"Large"]; +// +// [SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath]; +//} + + +#pragma mark - SSZipArchiveDelegate + +- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo { + NSLog(@"*** zipArchiveWillUnzipArchiveAtPath: `%@` zipInfo:", path); +} + + +- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath { + NSLog(@"*** zipArchiveDidUnzipArchiveAtPath: `%@` zipInfo: unzippedPath: `%@`", path, unzippedPath); +} + + +- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo { + NSLog(@"*** zipArchiveWillUnzipFileAtIndex: `%ld` totalFiles: `%ld` archivePath: `%@` fileInfo:", fileIndex, totalFiles, archivePath); +} + + +- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo { + NSLog(@"*** zipArchiveDidUnzipFileAtIndex: `%ld` totalFiles: `%ld` archivePath: `%@` fileInfo:", fileIndex, totalFiles, archivePath); +} + + +#pragma mark - Private + +- (NSString *)_cachesPath:(NSString *)directory { + NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] + stringByAppendingPathComponent:@"com.samsoffes.ssziparchive.tests"]; + if (directory) { + path = [path stringByAppendingPathComponent:directory]; + } + + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:path]) { + [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; + } + + return path; +} + +- (NSString *)_calculateMD5Digest:(NSData *)data +{ + unsigned char buffer[CC_MD5_DIGEST_LENGTH]; + CC_MD5([data bytes], [data length], buffer); + + NSMutableString* digest = [NSMutableString string]; + + for(int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) + [digest appendFormat:@"%02x", buffer[i]]; + + return digest; +} + +@end diff --git a/iOS/ExtractZipFile/SSZipArchive/Tests/TestArchive.zip b/iOS/ExtractZipFile/SSZipArchive/Tests/TestArchive.zip new file mode 100644 index 0000000..55ce84f Binary files /dev/null and b/iOS/ExtractZipFile/SSZipArchive/Tests/TestArchive.zip differ diff --git a/iOS/ExtractZipFile/SSZipArchive/Tests/TestPasswordArchive.zip b/iOS/ExtractZipFile/SSZipArchive/Tests/TestPasswordArchive.zip new file mode 100644 index 0000000..055cc4c Binary files /dev/null and b/iOS/ExtractZipFile/SSZipArchive/Tests/TestPasswordArchive.zip differ diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/crypt.h b/iOS/ExtractZipFile/SSZipArchive/minizip/crypt.h new file mode 100755 index 0000000..a01d08d --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const unsigned long* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/ioapi.c b/iOS/ExtractZipFile/SSZipArchive/minizip/ioapi.c new file mode 100755 index 0000000..e0f7382 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/ioapi.c @@ -0,0 +1,239 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if (defined(_WIN32)) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == ((uLong)-1)) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + +#ifndef __clang_analyzer__ + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; +#endif + + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen64((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = ftello64((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(fseeko64((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/ioapi.h b/iOS/ExtractZipFile/SSZipArchive/minizip/ioapi.h new file mode 100755 index 0000000..7e20e95 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/ioapi.h @@ -0,0 +1,201 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#include +#include +#include "zlib.h" + +#define USE_FILE32API +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/mztools.c b/iOS/ExtractZipFile/SSZipArchive/minizip/mztools.c new file mode 100755 index 0000000..80d50e0 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/mztools.c @@ -0,0 +1,284 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" +#include "mztools.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) +const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[256]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char centralDirectoryEntryHeader[46]; + //char* comment = ""; + //int comsize = (int) strlen(comment); + WRITE_32(centralDirectoryEntryHeader, 0x02014b50); + WRITE_16(centralDirectoryEntryHeader + 4, version); + WRITE_16(centralDirectoryEntryHeader + 6, version); + WRITE_16(centralDirectoryEntryHeader + 8, gpflag); + WRITE_16(centralDirectoryEntryHeader + 10, method); + WRITE_16(centralDirectoryEntryHeader + 12, filetime); + WRITE_16(centralDirectoryEntryHeader + 14, filedate); + WRITE_32(centralDirectoryEntryHeader + 16, crc); + WRITE_32(centralDirectoryEntryHeader + 20, cpsize); + WRITE_32(centralDirectoryEntryHeader + 24, uncpsize); + WRITE_16(centralDirectoryEntryHeader + 28, fnsize); + WRITE_16(centralDirectoryEntryHeader + 30, extsize); + WRITE_16(centralDirectoryEntryHeader + 32, 0 /*comsize*/); + WRITE_16(centralDirectoryEntryHeader + 34, 0); /* disk # */ + WRITE_16(centralDirectoryEntryHeader + 36, 0); /* int attrb */ + WRITE_32(centralDirectoryEntryHeader + 38, 0); /* ext attrb */ + WRITE_32(centralDirectoryEntryHeader + 42, currentOffset); + /* Header */ + if (fwrite(centralDirectoryEntryHeader, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + /* + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + */ + + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char finalCentralDirectoryHeader[22]; + //char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + //int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(finalCentralDirectoryHeader, 0x06054b50); + WRITE_16(finalCentralDirectoryHeader + 4, 0); /* disk # */ + WRITE_16(finalCentralDirectoryHeader + 6, 0); /* disk # */ + WRITE_16(finalCentralDirectoryHeader + 8, entriesZip); /* hack */ + WRITE_16(finalCentralDirectoryHeader + 10, entriesZip); /* hack */ + WRITE_32(finalCentralDirectoryHeader + 12, offsetCD); /* size of CD */ + WRITE_32(finalCentralDirectoryHeader + 16, offset); /* offset to CD */ + WRITE_16(finalCentralDirectoryHeader + 20, 0 /*comsize*/); /* comment */ + + /* Header */ + if (fwrite(finalCentralDirectoryHeader, 1, 22, fpOutCD) == 22) { + + /* Comment field */ + /* + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + */ + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/mztools.h b/iOS/ExtractZipFile/SSZipArchive/minizip/mztools.h new file mode 100755 index 0000000..88b3459 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/mztools.h @@ -0,0 +1,31 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + +/* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery +*/ +extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + +#endif diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/unzip.c b/iOS/ExtractZipFile/SSZipArchive/minizip/unzip.c new file mode 100755 index 0000000..5a51d8a --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/unzip.c @@ -0,0 +1,2152 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +//#ifndef NOUNCRYPT +// #define NOUNCRYPT +//#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == (unsigned long)-1) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + { +#ifndef __clang_analyzer__ + lSeek=0; +#endif + } + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; +#ifndef __clang_analyzer__ + lSeek+=file_info.size_file_comment - uSizeRead; +#endif + } +#ifndef __clang_analyzer__ + else + lSeek+=file_info.size_file_comment; +#endif + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if (err==UNZ_OK) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + { +#ifndef __clang_analyzer__ + err=UNZ_BADZIPFILE; +#endif + } + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; +#ifndef __clang_analyzer__ + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; +#endif + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + // NOTE: + // This bit of code seems to try to set the amount of space in the output buffer based on the + // value stored in the headers stored in the .zip file. However, if those values are incorrect + // it may result in a loss of data when uncompresssing that file. The compressed data is still + // legit and will deflate without knowing the uncompressed code so this tidbit is unnecessary and + // may cause issues for some .zip files. + // + // It's removed in here to fix those issues. + // + // See: https://github.com/samsoffes/ssziparchive/issues/16 + // + + /* + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + */ + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/unzip.h b/iOS/ExtractZipFile/SSZipArchive/minizip/unzip.h new file mode 100755 index 0000000..3183968 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/zip.c b/iOS/ExtractZipFile/SSZipArchive/minizip/zip.c new file mode 100755 index 0000000..044c73e --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/zip.c @@ -0,0 +1,2022 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit); +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def); +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local); +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + +#ifndef __clang_analyzer__ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); +#endif + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if (level==8 || level==9) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + { +#ifndef __clang_analyzer__ + err = ZIP_ERRNO; +#endif + } + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; +#ifndef __clang_analyzer__ + zi->ci.stream.next_out = zi->ci.buffered_data; +#endif + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); +#ifndef __clang_analyzer__ + p += 8; +#endif + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip); +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} + +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip); +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment); +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/iOS/ExtractZipFile/SSZipArchive/minizip/zip.h b/iOS/ExtractZipFile/SSZipArchive/minizip/zip.h new file mode 100755 index 0000000..eec1082 --- /dev/null +++ b/iOS/ExtractZipFile/SSZipArchive/minizip/zip.h @@ -0,0 +1,362 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/iOS/ExtractZipFile/ZipPlugin.js b/iOS/ExtractZipFile/ZipPlugin.js new file mode 100644 index 0000000..c23a102 --- /dev/null +++ b/iOS/ExtractZipFile/ZipPlugin.js @@ -0,0 +1,21 @@ +/** + * ZipPlugin.js + * + * Phonegap Extract Zip File plugin + * + * Created by Shaun Rowe on 10/05/2012. + * Copyright (c) Pobl Creative Cyf. 2012 + * + */ +var ExtractZipFilePlugin = function(){ +} + +cordova.addConstructor(function(){ + if(!window.plugins) window.plugins = {}; + window.plugins.extractZipFile = new ExtractZipFilePlugin(); +}); + +ExtractZipFilePlugin.prototype.extractFile = function(file, destination, successCallback, errorCallback) +{ + return cordova.exec(successCallback, errorCallback, "ExtractZipFilePlugin", "extract", [file, destination]); +}; \ No newline at end of file diff --git a/iOS/Geofencing/DGGeofencing.js b/iOS/Geofencing/DGGeofencing.js new file mode 100644 index 0000000..7590e06 --- /dev/null +++ b/iOS/Geofencing/DGGeofencing.js @@ -0,0 +1,59 @@ +/** + * Geofencing.js + * + * Phonegap Geofencing Plugin + * Copyright (c) Dov Goldberg 2012 + * http://www.ogonium.com + * dov.goldberg@ogonium.com + * + */ +var DGGeofencing = { + /* + Params: + #define KEY_REGION_ID @"fid" + #define KEY_REGION_LAT @"latitude" + #define KEY_REGION_LNG @"longitude" + #define KEY_REGION_RADIUS @"radius" + #define KEY_REGION_ACCURACY @"accuracy" + */ + addRegion: function(params, success, fail) { + return PhoneGap.exec(success, fail, "DGGeofencing", "addRegion", [params]); + }, + + /* + Params: + #define KEY_REGION_ID @"fid" + #define KEY_REGION_LAT @"latitude" + #define KEY_REGION_LNG @"longitude" + */ + removeRegion: function(params, success, fail) { + return PhoneGap.exec(success, fail, "DGGeofencing", "removeRegion", [params]); + }, + + /* + Params: + NONE + */ + getWatchedRegionIds: function(success, fail) { + return PhoneGap.exec(success, fail, "DGGeofencing", "getWatchedRegionIds", []); + }, + + /* + Params: + NONE + */ + getPendingRegionUpdates: function(success, fail) { + return PhoneGap.exec(success, fail, "DGGeofencing", "getPendingRegionUpdates", []); + }, + + /* + This is used so the JavaScript can be updated when a region is entered or exited + */ + regionMonitorUpdate: function(regionupdate) { + console.log("regionMonitorUpdate: " + regionupdate); + var ev = document.createEvent('HTMLEvents'); + ev.regionupdate = regionupdate; + ev.initEvent('region-update', true, true, arguments); + document.dispatchEvent(ev); + } +}; \ No newline at end of file diff --git a/iOS/Geofencing/Geofencing/DGGeofencing.h b/iOS/Geofencing/Geofencing/DGGeofencing.h new file mode 100644 index 0000000..5d6ab23 --- /dev/null +++ b/iOS/Geofencing/Geofencing/DGGeofencing.h @@ -0,0 +1,75 @@ +// +// Geofencing.h +// TikalTimeTracker +// Sections of this code adapted from Apache Cordova +// +// Created by Dov Goldberg on 5/3/12. +// Copyright (c) 2012 Ogonium. All rights reserved. +// + +#import +#import + +#import + +#import "DGGeofencingHelper.h" + +#define KEY_REGION_ID @"fid" +#define KEY_REGION_LAT @"latitude" +#define KEY_REGION_LNG @"longitude" +#define KEY_REGION_RADIUS @"radius" +#define KEY_REGION_ACCURACY @"accuracy" + +enum DGLocationStatus { + PERMISSIONDENIED = 1, + POSITIONUNAVAILABLE, + TIMEOUT, + REGIONMONITORINGPERMISSIONDENIED, + REGIONMONITORINGUNAVAILABLE +}; +typedef NSInteger DGLocationStatus; + +enum DGLocationAccuracy { + DGLocationAccuracyBestForNavigation, + DGLocationAccuracyBest, + DGLocationAccuracyNearestTenMeters, + DGLocationAccuracyHundredMeters, + DGLocationAccuracyThreeKilometers +}; +typedef NSInteger DGLocationAccuracy; + +// simple ojbect to keep track of location information +@interface DGLocationData : NSObject + +@property (nonatomic, assign) DGLocationStatus locationStatus; +@property (nonatomic, retain) CLLocation* locationInfo; +@property (nonatomic, retain) NSMutableArray* locationCallbacks; + +@end + +@interface DGGeofencing : CDVPlugin + +//@property (nonatomic, retain) CLLocationManager *locationManager; +@property (nonatomic, retain) DGLocationData* locationData; + +- (BOOL) isLocationServicesEnabled; +- (BOOL) isAuthorized; +- (BOOL) isRegionMonitoringAvailable; +- (BOOL) isRegionMonitoringEnabled; +- (void) saveGeofenceCallbackId:(NSString *) callbackId; +- (void) addRegionToMonitor:(NSMutableDictionary *)params; +- (void) removeRegionToMonitor:(NSMutableDictionary *)params; + +- (void) returnLocationError: (NSUInteger) errorCode withMessage: (NSString*) message; +- (void) returnRegionSuccess; + +#pragma mark Plugin Functions +- (void) addRegion:(CDVInvokedUrlCommand*)command; +- (void) removeRegion:(CDVInvokedUrlCommand*)command; +- (void) getWatchedRegionIds:(CDVInvokedUrlCommand*)command; +- (void) getPendingRegionUpdates:(CDVInvokedUrlCommand*)command; +//- (void)addRegion:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +//- (void)removeRegion:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +//- (void)getWatchedRegionIds:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + +@end diff --git a/iOS/Geofencing/Geofencing/DGGeofencing.m b/iOS/Geofencing/Geofencing/DGGeofencing.m new file mode 100644 index 0000000..161b590 --- /dev/null +++ b/iOS/Geofencing/Geofencing/DGGeofencing.m @@ -0,0 +1,334 @@ +// +// Geofencing.m +// TikalTimeTracker +// +// Created by Dov Goldberg on 5/3/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "DGGeofencing.h" +#import + +@implementation DGLocationData + +@synthesize locationStatus, locationInfo, locationCallbacks; + +-(DGLocationData*) init +{ + self = (DGLocationData*)[super init]; + if (self) + { + self.locationInfo = nil; + self.locationCallbacks = nil; + [CDVPluginResult setVerbose:YES]; + } + return self; +} +-(void) dealloc +{ + self.locationInfo = nil; + self.locationCallbacks = nil; + [super dealloc]; +} + +@end + +@implementation DGGeofencing + +@synthesize locationData; + +- (CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (DGGeofencing*)[super initWithWebView:(UIWebView*)theWebView]; + if (self) + { + //self.locationManager = [[[CLLocationManager alloc] init] autorelease]; + //self.locationManager.delegate = self; // Tells the location manager to send updates to this object + self.locationData = nil; + } + return self; +} + +- (void) dealloc +{ + //self.locationManager.delegate = nil; + //self.locationManager = nil; + [super dealloc]; +} + +- (BOOL) isRegionMonitoringAvailable +{ + BOOL regionMonitoringAvailableClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(regionMonitoringAvailable)]; + if (regionMonitoringAvailableClassPropertyAvailable) + { + BOOL regionMonitoringAvailable = [CLLocationManager regionMonitoringAvailable]; + return (regionMonitoringAvailable); + } + + // by default, assume NO + return NO; +} + +- (BOOL) isRegionMonitoringEnabled +{ + BOOL regionMonitoringEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(regionMonitoringEnabled)]; + if (regionMonitoringEnabledClassPropertyAvailable) + { + BOOL regionMonitoringEnabled = [CLLocationManager regionMonitoringEnabled]; + return (regionMonitoringEnabled); + } + + // by default, assume NO + return NO; +} + +- (BOOL) isAuthorized +{ + BOOL authorizationStatusClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + if (authorizationStatusClassPropertyAvailable) + { + NSUInteger authStatus = [CLLocationManager authorizationStatus]; + return (authStatus == kCLAuthorizationStatusAuthorized) || (authStatus == kCLAuthorizationStatusNotDetermined); + } + + // by default, assume YES (for iOS < 4.2) + return YES; +} + +- (BOOL) isLocationServicesEnabled +{ + BOOL locationServicesEnabledInstancePropertyAvailable = [[[DGGeofencingHelper sharedGeofencingHelper] locationManager] respondsToSelector:@selector(locationServicesEnabled)]; // iOS 3.x + BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x + + if (locationServicesEnabledClassPropertyAvailable) + { // iOS 4.x + return [CLLocationManager locationServicesEnabled]; + } + else if (locationServicesEnabledInstancePropertyAvailable) + { // iOS 2.x, iOS 3.x + return [(id)[[DGGeofencingHelper sharedGeofencingHelper] locationManager] locationServicesEnabled]; + } + else + { + return NO; + } +} + +- (void) saveGeofenceCallbackId:(NSString *) callbackId { + NSLog(@"callbackId: %@", callbackId); + if (!self.locationData) { + self.locationData = [[[DGLocationData alloc] init] autorelease]; + } + + DGLocationData* lData = self.locationData; + if (!lData.locationCallbacks) { + lData.locationCallbacks = [NSMutableArray array];//]WithCapacity:1]; + } + + // add the callbackId into the array so we can call back when get data + [lData.locationCallbacks enqueue:callbackId]; +} + +- (void) returnRegionSuccess; { + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + [posError setObject: [NSNumber numberWithInt: CDVCommandStatus_OK] forKey:@"code"]; + [posError setObject: @"Region Success" forKey: @"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:posError]; + NSString *callbackId = [self.locationData.locationCallbacks dequeue]; + if (callbackId) { + [self writeJavascript:[result toSuccessCallbackString:callbackId]]; + } +} + +- (void)returnLocationError: (NSUInteger) errorCode withMessage: (NSString*) message +{ + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + [posError setObject: [NSNumber numberWithInt: errorCode] forKey:@"code"]; + [posError setObject: message ? message : @"" forKey: @"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; + NSString *callbackId = [self.locationData.locationCallbacks dequeue]; + if (callbackId) { + [self writeJavascript:[result toErrorCallbackString:callbackId]]; + } +} + +#pragma mark Plugin Functions + +- (void)addRegion:(CDVInvokedUrlCommand*)command { + + NSString* callbackId = command.callbackId; + + [self saveGeofenceCallbackId:callbackId]; + + if (![self isLocationServicesEnabled]) + { + BOOL forcePrompt = NO; + if (!forcePrompt) + { + [self returnLocationError:PERMISSIONDENIED withMessage: nil]; + return; + } + } + + if (![self isAuthorized]) + { + NSString* message = nil; + BOOL authStatusAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + if (authStatusAvailable) { + NSUInteger code = [CLLocationManager authorizationStatus]; + if (code == kCLAuthorizationStatusNotDetermined) { + // could return POSITION_UNAVAILABLE but need to coordinate with other platforms + message = @"User undecided on application's use of location services"; + } else if (code == kCLAuthorizationStatusRestricted) { + message = @"application use of location services is restricted"; + } + } + //PERMISSIONDENIED is only PositionError that makes sense when authorization denied + [self returnLocationError:PERMISSIONDENIED withMessage: message]; + + return; + } + + if (![self isRegionMonitoringAvailable]) + { + [self returnLocationError:REGIONMONITORINGUNAVAILABLE withMessage: @"Region monitoring is unavailable"]; + return; + } + + if (![self isRegionMonitoringEnabled]) + { + [self returnLocationError:REGIONMONITORINGPERMISSIONDENIED withMessage: @"User has restricted the use of region monitoring"]; + return; + } + NSMutableDictionary *options; + [command legacyArguments:nil andDict:&options]; + [self addRegionToMonitor:options]; + + [self returnRegionSuccess]; +} + +- (void) getPendingRegionUpdates:(CDVInvokedUrlCommand*)command { + NSString* callbackId = command.callbackId; + + NSString *path = [DGGeofencingHelper applicationDocumentsDirectory]; + NSString *finalPath = [path stringByAppendingPathComponent:@"notifications.dg"]; + NSMutableArray *updates = [NSMutableArray arrayWithContentsOfFile:finalPath]; + + if (updates) { + NSError *error; + [[NSFileManager defaultManager] removeItemAtPath:finalPath error:&error]; + } else { + updates = [NSMutableArray array]; + } + + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:3]; + [posError setObject: [NSNumber numberWithInt: CDVCommandStatus_OK] forKey:@"code"]; + [posError setObject: @"Region Success" forKey: @"message"]; + [posError setObject: updates forKey: @"pendingupdates"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:posError]; + if (callbackId) { + [self writeJavascript:[result toSuccessCallbackString:callbackId]]; + } + NSLog(@"pendingupdates: %@", updates); +} + +- (void) addRegionToMonitor:(NSMutableDictionary *)params { + // Parse Incoming Params + NSString *regionId = [params objectForKey:KEY_REGION_ID]; + NSString *latitude = [params objectForKey:KEY_REGION_LAT]; + NSString *longitude = [params objectForKey:KEY_REGION_LNG]; + double radius = [[params objectForKey:KEY_REGION_RADIUS] doubleValue]; + + CLLocationCoordinate2D coord = CLLocationCoordinate2DMake([latitude doubleValue], [longitude doubleValue]); + CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:coord radius:radius identifier:regionId]; + [[[DGGeofencingHelper sharedGeofencingHelper] locationManager] startMonitoringForRegion:region desiredAccuracy:kCLLocationAccuracyBestForNavigation]; + [region release]; +} + +- (void) removeRegionToMonitor:(NSMutableDictionary *)params { + // Parse Incoming Params + NSString *regionId = [params objectForKey:KEY_REGION_ID]; + NSString *latitude = [params objectForKey:KEY_REGION_LAT]; + NSString *longitude = [params objectForKey:KEY_REGION_LNG]; + + CLLocationCoordinate2D coord = CLLocationCoordinate2DMake([latitude doubleValue], [longitude doubleValue]); + CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:coord radius:10.0 identifier:regionId]; + [[[DGGeofencingHelper sharedGeofencingHelper] locationManager] stopMonitoringForRegion:region]; + [region release]; +} + +- (void)removeRegion:(CDVInvokedUrlCommand*)command { + + NSString* callbackId = command.callbackId; + + [self saveGeofenceCallbackId:callbackId]; + + if (![self isLocationServicesEnabled]) + { + BOOL forcePrompt = NO; + if (!forcePrompt) + { + [self returnLocationError:PERMISSIONDENIED withMessage: nil]; + return; + } + } + + if (![self isAuthorized]) + { + NSString* message = nil; + BOOL authStatusAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + if (authStatusAvailable) { + NSUInteger code = [CLLocationManager authorizationStatus]; + if (code == kCLAuthorizationStatusNotDetermined) { + // could return POSITION_UNAVAILABLE but need to coordinate with other platforms + message = @"User undecided on application's use of location services"; + } else if (code == kCLAuthorizationStatusRestricted) { + message = @"application use of location services is restricted"; + } + } + //PERMISSIONDENIED is only PositionError that makes sense when authorization denied + [self returnLocationError:PERMISSIONDENIED withMessage: message]; + + return; + } + + if (![self isRegionMonitoringAvailable]) + { + [self returnLocationError:REGIONMONITORINGUNAVAILABLE withMessage: @"Region monitoring is unavailable"]; + return; + } + + if (![self isRegionMonitoringEnabled]) + { + [self returnLocationError:REGIONMONITORINGPERMISSIONDENIED withMessage: @"User has restricted the use of region monitoring"]; + return; + } + NSMutableDictionary *options; + [command legacyArguments:nil andDict:&options]; + [self removeRegionToMonitor:options]; + + [self returnRegionSuccess]; +} + +- (void)getWatchedRegionIds:(CDVInvokedUrlCommand*)command { + NSString* callbackId = command.callbackId; + + NSSet *regions = [[DGGeofencingHelper sharedGeofencingHelper] locationManager].monitoredRegions; + NSMutableArray *watchedRegions = [NSMutableArray array]; + for (CLRegion *region in regions) { + [watchedRegions addObject:region.identifier]; + } + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:3]; + [posError setObject: [NSNumber numberWithInt: CDVCommandStatus_OK] forKey:@"code"]; + [posError setObject: @"Region Success" forKey: @"message"]; + [posError setObject: watchedRegions forKey: @"regionids"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:posError]; + if (callbackId) { + [self writeJavascript:[result toSuccessCallbackString:callbackId]]; + } + NSLog(@"watchedRegions: %@", watchedRegions); +} + +#pragma mark Core Location Delegates + +@end diff --git a/iOS/Geofencing/Geofencing/DGGeofencingHelper.h b/iOS/Geofencing/Geofencing/DGGeofencingHelper.h new file mode 100644 index 0000000..eb11efe --- /dev/null +++ b/iOS/Geofencing/Geofencing/DGGeofencingHelper.h @@ -0,0 +1,25 @@ +// +// DGGeofencingHelper.h +// Geofencing +// +// Created by Dov Goldberg on 10/18/12. +// +// + +#import +#import +#import + +@class CDVCordovaView; +@interface DGGeofencingHelper : NSObject + +@property (nonatomic, retain) CLLocationManager *locationManager; +@property (nonatomic, assign) CDVCordovaView *webView; +@property (nonatomic, assign) BOOL didLaunchForRegionUpdate; + ++(DGGeofencingHelper*)sharedGeofencingHelper; + +- (void) dispose; ++ (NSString*) applicationDocumentsDirectory; + +@end diff --git a/iOS/Geofencing/Geofencing/DGGeofencingHelper.m b/iOS/Geofencing/Geofencing/DGGeofencingHelper.m new file mode 100644 index 0000000..4fda24e --- /dev/null +++ b/iOS/Geofencing/Geofencing/DGGeofencingHelper.m @@ -0,0 +1,125 @@ +// +// DGGeofencingHelper.m +// Geofencing +// +// Created by Dov Goldberg on 10/18/12. +// +// + +#import "DGGeofencingHelper.h" +#import + +static DGGeofencingHelper *sharedGeofencingHelper = nil; + +@implementation DGGeofencingHelper + +@synthesize webView; +@synthesize locationManager; +@synthesize didLaunchForRegionUpdate; + +- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region +{ + if (self.didLaunchForRegionUpdate) { + NSString *path = [DGGeofencingHelper applicationDocumentsDirectory]; + NSString *finalPath = [path stringByAppendingPathComponent:@"notifications.dg"]; + NSMutableArray *updates = [NSMutableArray arrayWithContentsOfFile:finalPath]; + + if (!updates) { + updates = [NSMutableArray array]; + } + + NSMutableDictionary *update = [NSMutableDictionary dictionary]; + + [update setObject:region.identifier forKey:@"fid"]; + [update setObject:[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"]; + [update setObject:@"enter" forKey:@"status"]; + + [updates addObject:update]; + + [updates writeToFile:finalPath atomically:YES]; + } else { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict setObject:@"enter" forKey:@"status"]; + [dict setObject:region.identifier forKey:@"fid"]; + NSString *jsStatement = [NSString stringWithFormat:@"DGGeofencing.regionMonitorUpdate(%@);", [dict cdvjk_JSONString]]; + [self.webView stringByEvaluatingJavaScriptFromString:jsStatement]; + } +} + +- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region +{ + if (self.didLaunchForRegionUpdate) { + NSString *path = [DGGeofencingHelper applicationDocumentsDirectory]; + NSString *finalPath = [path stringByAppendingPathComponent:@"notifications.dg"]; + NSMutableArray *updates = [NSMutableArray arrayWithContentsOfFile:finalPath]; + + if (!updates) { + updates = [NSMutableArray array]; + } + + NSMutableDictionary *update = [NSMutableDictionary dictionary]; + + [update setObject:region.identifier forKey:@"fid"]; + [update setObject:[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"]; + [update setObject:@"left" forKey:@"status"]; + + [updates addObject:update]; + + [updates writeToFile:finalPath atomically:YES]; + } else { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict setObject:@"left" forKey:@"status"]; + [dict setObject:region.identifier forKey:@"fid"]; + NSString *jsStatement = [NSString stringWithFormat:@"DGGeofencing.regionMonitorUpdate(%@);", [dict cdvjk_JSONString]]; + [self.webView stringByEvaluatingJavaScriptFromString:jsStatement]; + } +} + +- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error { +#pragma mark TODO - Monitoring Failure Callback +// NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; +// [posError setObject: [NSNumber numberWithInt: error.code] forKey:@"code"]; +// [posError setObject: region.identifier forKey: @"regionid"]; +// CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; +// NSString *callbackId = [self.locationData.locationCallbacks dequeue]; +// if (callbackId) { +// [self writeJavascript:[result toErrorCallbackString:callbackId]]; +// } +} + +- (id) init { + self = [super init]; + if (self) { + self.locationManager = [[CLLocationManager alloc] init]; + self.locationManager.delegate = self; // Tells the location manager to send updates to this object + } + return self; +} + ++(DGGeofencingHelper *)sharedGeofencingHelper +{ + //objects using shard instance are responsible for retain/release count + //retain count must remain 1 to stay in mem + + if (!sharedGeofencingHelper) + { + sharedGeofencingHelper = [[DGGeofencingHelper alloc] init]; + } + + return sharedGeofencingHelper; +} + +- (void) dispose { + locationManager.delegate = nil; + [locationManager release]; + [sharedGeofencingHelper release]; +} + ++ (NSString*) applicationDocumentsDirectory +{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; + return basePath; +} + +@end diff --git a/iOS/Geofencing/README.md b/iOS/Geofencing/README.md new file mode 100644 index 0000000..3c3c4c1 --- /dev/null +++ b/iOS/Geofencing/README.md @@ -0,0 +1,166 @@ +PhoneGap-Geofencing +=================== + +Geofencing Plugin For PhoneGap. + +## DESCRIPTION ## + +* This plugin provides a simple way to use iOS Region Monitoring in PhoneGap applications. +* Simple JS interface is exposed to allow the adding and removing of regions to monitor. +* Included ability to receive realtime region notifications when regions are entered and exited. + +## WHAT IS GEOFENCING ## + +Geofencing is a way to monitor geographic regions. In iOS it allows an app to be informed when a specified geographic region is entered or exited. + +## SETUP ## + +Using this plugin requires [Cordova iOS](https://github.com/apache/incubator-cordova-ios). + +1. Make sure your Xcode project has been [updated for Cordova](https://github.com/apache/incubator-cordova-ios/blob/master/guides/Cordova%20Upgrade%20Guide.md) +2. Drag and drop the DGGeofencing.h and DGGeofencing.m files from the DGGeofencing folder in Finder to your Plugins folder in XCode. +3. Add the .js files to your `www` folder on disk, and add reference(s) to the .js files using + +4. Add new entry with key `DGGeofencing` and value `DGGeofencing` to `Plugins` in `Cordova.plist/Cordova.plist` + +## INCLUDED FUNTIONS ## + +DGGeofencing.js contains the following functions: + +1. addRegion - Adds a new region to be monitored. +2. removeRegion - Clears an existing region from being monitored. +3. getWatchedRegionIds - Returns a list of currently monitored region identifiers. + +## PLUGIN CODE EXAMPLE ## + +To add a new region to be monitored use the DGGeofencing addRegion function. +The parameters are: + +1. fid - String - This is a unique identifier. +2. radius - Integer - Specifies the radius in meters of the region. +3. latitude - String - latitude of the region. +4. longitude - String - latitude of the region. + +Example: + + var params = {"fid": location.id, "radius": 15, "latitude": location.lat, "longitude": location.lng}; + console.log(params); + DGGeofencing.addRegion( + params, + function(result) { + console.log("add success"); + }, + function(error) { + alert("failed to add region"); + } + ); + +To remove an existing region use the DGGeofencing removeRegion function. +The parameters are: +1. fid - String - This is a unique identifier. +2. latitude - String - latitude of the region. +3. longitude - String - latitude of the region. + +Example: + + var params = {"fid": item.fid, "latitude": item.latitude, "longitude": item.longitude}; + DGGeofencing.removeRegion( + params, + function(result) { + alert("delete success") + }, + function(error) { + alert("delete error"); + } + ); + +To retrieve the list of identifiers of currently monitored regions use the DGGeofencing getWatchedRegionIds function. +No parameters. + +The result object contains an array of strings in regionids + +Example: + + DGGeofencing.getWatchedRegionIds( + function(result) { + alert("success: " + result.regionids) + }, + function(error) { + alert("error"); + } + ); + +## HOW TO SETUP REGION NOTIFICATIONS ## + +Of course adding and removing monitored regions would be useless without the ability to receive real time notifications when region boundries are crossed. +This setup will allow the JavaScript to receive updates both when the app is running and not running. + +Follow these steps to setup region notifications when the app is running: + +1. Drag and drop the DGGeofencingHelper.h and DGGeofencingHelper.m files from the DGGeofencing folder in Finder to your Plugins folder in XCode. +2. Add the following code to the viewDidLoad function in the MainViewController.m file after [super viewDidLoad]; + +
[[DGGeofencingHelper sharedGeofencingHelper] setWebView:self.webView];
+ +3. Make sure to import DGGeofencingHelper.h in the MainViewController.m file. +4. In your JavaScript add the following code in the same place where you process the documentReady event. + +
document.addEventListener("region-update", function(event) {
+		var fid = event.regionupdate.fid;
+		var status = event.regionupdate.status;
+	});
+ +When the app is not running, even in the background, region notifications are saved as they come in. +In order to retrieve these pending region notifications follow these instructions. + +1. Add the following code in the app delegate - (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions + +
    if ([[launchOptions allKeys] containsObject:UIApplicationLaunchOptionsLocationKey]) {
+    [[DGGeofencingHelper sharedGeofencingHelper] setDidLaunchForRegionUpdate:YES];
+} else {
+    [[DGGeofencingHelper sharedGeofencingHelper] setDidLaunchForRegionUpdate:NO];
+}
+ +2. In the JavaScript you will need to use the following code to retrieve these notifications. + +
    DGGeofencing.getPendingRegionUpdates(
+			function(result) { 
+				var updates = result.pendingupdates;
+				$(updates).each(function(index, update){
+					var fid = update.fid;
+					var status = update.status;
+					var timestamp = update.timestamp;
+					console.log("fid: " + fid + " status: " + status + " timestamp: " + timestamp);
+				});   
+	      	},
+	      	function(error) {   
+		  		alert("failed");
+	      	}
+		);
+ +## USAGE SAMPLE CODE ## + +Feel free to take a look at a project I have made that uses the above plugin. +You can find this project in my github repository [Phonegap-Geofencing](https://github.com/radshag/PhoneGap-Geofencing/tree/master/iOS/Sample). + +## QUESTIONS AND COMMENTS ## + +All questions and comments are welcome. Please do so on my [GitHub Page](https://github.com/radshag/PhoneGap-Geofencing/issues). + +The latest version of the DGGeofencing plugin can always be found [here](https://github.com/radshag/PhoneGap-Geofencing/tree/master/iOS/DGGeofencing). + +## LICENSE ## + +The MIT License + +Copyright (c) 2012 Dov Goldberg +EMAIL: dov.goldberg@ogonium.com +WEBSITE: http://www.ogonium.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/iOS/Globalization/Globalization.h b/iOS/Globalization/Globalization.h index 0b1a56a..5fab51d 100644 --- a/iOS/Globalization/Globalization.h +++ b/iOS/Globalization/Globalization.h @@ -6,12 +6,7 @@ */ #import -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "CDVPlugin.h" -#endif - #define FORMAT_SHORT 0 #define FORMAT_MEDIUM 1 @@ -32,6 +27,9 @@ typedef NSUInteger GlobalizationError; @interface Globalization : CDVPlugin { CFLocaleRef currentLocale; } + +- (void) getPreferredLanguage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + /** * Returns the string identifier for the client’s current locale setting. * It returns the locale identifier string to the successCB callback with a diff --git a/iOS/Globalization/Globalization.js b/iOS/Globalization/Globalization.js index c3cae97..b462de9 100644 --- a/iOS/Globalization/Globalization.js +++ b/iOS/Globalization/Globalization.js @@ -5,17 +5,27 @@ * Copyright (c) 2010-2011, IBM Corporation * */ -if (!window.plugins.globalization) { - // shim to work in 1.5 and 1.6 - if (!window.Cordova) { - window.Cordova = cordova; - }; - -function Globalization() -{ +var Globalization = function() { }; - + +Globalization.prototype.getPreferredLanguage = function(successCB, failureCB) +{ + // successCallback required + if (typeof successCB != "function") { + console.log("Globalization.getPreferredLanguage Error: successCB is not a function"); + return; + } + + // errorCallback required + if (typeof failureCB != "function") { + console.log("Globalization.getPreferredLanguage Error: failureCB is not a function"); + return; + } + + Codova.exec(successCB, failureCB, "Globalization","getPreferredLanguage", []); +}; + /** * Returns the string identifier for the client's current locale setting. * It returns the locale identifier string to the successCB callback with a @@ -40,7 +50,7 @@ Globalization.prototype.getLocaleName = function(successCB, failureCB) console.log("Globalization.getLocaleName Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.getLocaleName Error: failureCB is not a function"); @@ -49,7 +59,7 @@ Globalization.prototype.getLocaleName = function(successCB, failureCB) Cordova.exec(successCB, failureCB, "Globalization","getLocaleName", []); }; - + /** * Returns a date formatted as a string according to the client's user preferences and * calendar using the time zone of the client. It returns the formatted date string to the @@ -82,14 +92,14 @@ Globalization.prototype.dateToString = function(date, successCB, failureCB, opti console.log("Globalization.dateToString Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.dateToString Error: failureCB is not a function"); return; } - - + + if (date instanceof Date){ var dateValue; dateValue = date.valueOf(); @@ -143,13 +153,13 @@ Globalization.prototype.stringToDate = function(dateString, successCB, failureCB console.log("Globalization.stringToDate Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.stringToDate Error: failureCB is not a function"); return; } - + if (typeof dateString == "string"){ Cordova.exec(successCB, failureCB, "Globalization", "stringToDate", [{"dateString": dateString, "options": options}]); } @@ -158,7 +168,7 @@ Globalization.prototype.stringToDate = function(dateString, successCB, failureCB } }; - + /** * Returns a pattern string for formatting and parsing dates according to the client's * user preferences. It returns the pattern to the successCB callback with a @@ -198,17 +208,17 @@ Globalization.prototype.getDatePattern = function(successCB, failureCB, options) console.log("Globalization.getDatePattern Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.getDatePattern Error: failureCB is not a function"); return; } - + Cordova.exec(successCB, failureCB, "Globalization", "getDatePattern", [{"options": options}]); }; - + /** * Returns an array of either the names of the months or days of the week * according to the client's user preferences and calendar. It returns the array of names to the @@ -241,13 +251,13 @@ Globalization.prototype.getDateNames = function(successCB, failureCB, options) console.log("Globalization.getDateNames Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.getDateNames Error: failureCB is not a function"); return; } - + Cordova.exec(successCB, failureCB, "Globalization", "getDateNames", [{"options": options}]); }; @@ -278,14 +288,14 @@ Globalization.prototype.isDayLightSavingsTime = function(date, successCB, failur console.log("Globalization.isDayLightSavingsTime Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.isDayLightSavingsTime Error: failureCB is not a function"); return; } - - + + if (date instanceof Date){ var dateValue; dateValue = date.valueOf(); @@ -294,7 +304,7 @@ Globalization.prototype.isDayLightSavingsTime = function(date, successCB, failur else { console.log("Globalization.isDayLightSavingsTime Error: date is not a Date object"); } - + }; /** @@ -322,17 +332,17 @@ Globalization.prototype.getFirstDayOfWeek = function(successCB, failureCB) console.log("Globalization.getFirstDayOfWeek Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.getFirstDayOfWeek Error: failureCB is not a function"); return; } - + Cordova.exec(successCB, failureCB, "Globalization", "getFirstDayOfWeek", []); }; - + /** * Returns a number formatted as a string according to the client's user preferences. * It returns the formatted number string to the successCB callback with a properties object as a @@ -363,13 +373,13 @@ Globalization.prototype.numberToString = function(number, successCB, failureCB, console.log("Globalization.numberToString Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.numberToString Error: failureCB is not a function"); return; } - + if(typeof number == "number") { Cordova.exec(successCB, failureCB, "Globalization", "numberToString", [{"number": number, "options": options}]); } @@ -408,13 +418,13 @@ Globalization.prototype.stringToNumber = function(numberString, successCB, failu console.log("Globalization.stringToNumber Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.stringToNumber Error: failureCB is not a function"); return; } - + if(typeof numberString == "string") { Cordova.exec(successCB, failureCB, "Globalization", "stringToNumber", [{"numberString": numberString, "options": options}]); } @@ -460,13 +470,13 @@ Globalization.prototype.getNumberPattern = function(successCB, failureCB, option console.log("Globalization.getNumberPattern Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.getNumberPattern Error: failureCB is not a function"); return; } - + Cordova.exec(successCB, failureCB, "Globalization", "getNumberPattern", [{"options": options}]); }; @@ -504,13 +514,13 @@ Globalization.prototype.getCurrencyPattern = function(currencyCode, successCB, f console.log("Globalization.getCurrencyPattern Error: successCB is not a function"); return; } - + // errorCallback required if (typeof failureCB != "function") { console.log("Globalization.getCurrencyPattern Error: failureCB is not a function"); return; } - + if(typeof currencyCode == "string") { Cordova.exec(successCB, failureCB, "Globalization", "getCurrencyPattern", [{"currencyCode": currencyCode}]); } @@ -519,18 +529,6 @@ Globalization.prototype.getCurrencyPattern = function(currencyCode, successCB, f } }; -Globalization.install = function() -{ - if(!window.plugins) - { - window.plugins = {}; - } - if (!window.plugins.globalization) { - window.plugins.globalization = new Globalization(); - } - return window.plugins.globalization; -}; - GlobalizationError = function() { this.code = null; } @@ -541,5 +539,10 @@ GlobalizationError.FORMATTING_ERROR = 1; GlobalizationError.PARSING_ERROR = 2; GlobalizationError.PATTERN_ERROR = 3; -Cordova.addConstructor(Globalization.install); -}; \ No newline at end of file + +if(!window.plugins) { + window.plugins = {}; +} +if (!window.plugins.globalization) { + window.plugins.globalization = new Globalization(); +} \ No newline at end of file diff --git a/iOS/Globalization/Globalization.m b/iOS/Globalization/Globalization.m index 8a6d494..1ab1ac3 100644 --- a/iOS/Globalization/Globalization.m +++ b/iOS/Globalization/Globalization.m @@ -19,6 +19,36 @@ } return self; } + +- (void) getPreferredLanguage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + NSString* callbackId = [arguments objectAtIndex:0]; + + NSString* jsString = nil; // result string + NSLog(@"log1"); + // Source: http://stackoverflow.com/questions/3910244/getting-current-device-language-in-ios + // (should be OK) + NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0]; + + if(language) { + NSDictionary * dictionary = [NSDictionary dictionaryWithObject: language forKey:@"value"]; + + CDVPluginResult * result = [CDVPluginResult resultWithStatus: CDVCommandStatus_OK + messageAsDictionary: dictionary]; + + jsString = [result toSuccessCallbackString:callbackId]; + } + else { + // TBD is this ever expected to happen? + CDVPluginResult * result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsString: @"UNKNOWN_ERROR"]; + + jsString = [result toErrorCallbackString:callbackId]; + } + + [self writeJavascript:jsString]; +} + - (void) getLocaleName:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { CDVPluginResult* result = nil; diff --git a/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.h b/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.h index 6723859..c0c499d 100644 --- a/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.h +++ b/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.h @@ -9,11 +9,7 @@ // #import -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "CDVPlugin.h" -#endif #import "GANTracker.h" @interface GoogleAnalyticsPlugin : CDVPlugin { diff --git a/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.js b/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.js index 8a44279..9d27492 100755 --- a/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.js +++ b/iOS/GoogleAnalytics/GoogleAnalyticsPlugin.js @@ -1,11 +1,11 @@ function GoogleAnalyticsPlugin() {} GoogleAnalyticsPlugin.prototype.startTrackerWithAccountID = function(id) { - PhoneGap.exec("GoogleAnalyticsPlugin.startTrackerWithAccountID",id); + cordova.exec("GoogleAnalyticsPlugin.startTrackerWithAccountID",id); }; GoogleAnalyticsPlugin.prototype.trackPageview = function(pageUri) { - PhoneGap.exec("GoogleAnalyticsPlugin.trackPageview",pageUri); + cordova.exec("GoogleAnalyticsPlugin.trackPageview",pageUri); }; GoogleAnalyticsPlugin.prototype.trackEvent = function(category,action,label,value) { @@ -13,14 +13,14 @@ GoogleAnalyticsPlugin.prototype.trackEvent = function(category,action,label,valu action:action, label:label, value:value}; - PhoneGap.exec("GoogleAnalyticsPlugin.trackEvent",options); + cordova.exec("GoogleAnalyticsPlugin.trackEvent",options); }; GoogleAnalyticsPlugin.prototype.setCustomVariable = function(index,name,value) { var options = {index:index, name:name, value:value}; - PhoneGap.exec("GoogleAnalyticsPlugin.setCustomVariable",options); + cordova.exec("GoogleAnalyticsPlugin.setCustomVariable",options); }; GoogleAnalyticsPlugin.prototype.hitDispatched = function(hitString) { @@ -30,7 +30,7 @@ GoogleAnalyticsPlugin.prototype.trackerDispatchDidComplete = function(count) { //console.log("trackerDispatchDidComplete :: " + count); }; -PhoneGap.addConstructor(function() { +cordova.addConstructor(function() { if(!window.plugins) window.plugins = {}; window.plugins.googleAnalyticsPlugin = new GoogleAnalyticsPlugin(); }); diff --git a/iOS/Keychain/README.md b/iOS/Keychain/README.md new file mode 100644 index 0000000..9e55301 --- /dev/null +++ b/iOS/Keychain/README.md @@ -0,0 +1,54 @@ +# Cordova Keychain Plugin # +by Shazron Abdullah + +## Adding the Plugin to your project ## + +Using this plugin requires [iOS Cordova](http://github.com/apache/incubator-cordova-ios) and Xcode 4. + +1. Make sure your Cordova Xcode project has been [updated for Cordova 1.6.0](https://github.com/apache/incubator-cordova-ios/blob/master/guides/Cordova%20Plugin%20Upgrade%20Guide.md) +2. Add the .h and .m files to your Plugins folder in your project (as a Group "yellow folder" not a Reference "blue folder") +3. Add the .js files to your "www" folder on disk, and add reference(s) to the .js files as <script> tags in your html file(s) +4. In **Cordova.plist** (1.5.0 or greater) or **PhoneGap.plist** (1.4.1 or lesser), under the **Plugins** section, add an idential key and value of **"SAiOSKeychainPlugin"** +5. Add the **"Security.framework"** to your project's Target, in the **Build Phase** tab - **Link Binary with Libraries** + + +## RELEASE NOTES ## + +### 20120709 ### + +* Updated for Cordova + +### 20101105 ### +* Initial release +* See the .js file for API docs, and the KeychainPlugin-Host/www/index.html for sample code + +## BUGS AND CONTRIBUTIONS ## + +Patches welcome! Send a pull request. Since this is not a part of Cordova Core (which requires an Apache iCLA), this should be easier. + +Post issues in the [PhoneGap Google Groups](http://groups.google.com/group/phonegap), include in the subject heading - "KeychainPlugin" or on [Github](http://github.com/phonegap/phonegap-plugins/issues) +(preferred) + +## LICENSE ## + +SFHFKeychainUtils code by: + Created by Buzz Andersen on 10/20/08. + Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. + Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. + +The rest: + +Copyright 2012 Shazron Abdullah + + 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. + diff --git a/iOS/Keychain/SAiOSKeychainPlugin.h b/iOS/Keychain/SAiOSKeychainPlugin.h new file mode 100644 index 0000000..574fdf4 --- /dev/null +++ b/iOS/Keychain/SAiOSKeychainPlugin.h @@ -0,0 +1,62 @@ +// +// SAiOSPaypalPlugin.h +// Keychain Plugin for Cordova +// +// Created by shazron on 10-11-05. +// Copyright 2012 Shazron Abdullah. All rights reserved. + +#import +#import +#ifdef CORDOVA_FRAMEWORK +#import +#else +#import "CDVPlugin.h" +#endif + +@interface SAiOSKeychainPlugin : CDVPlugin { +} + +- (void) getForKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) setForKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) removeForKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + +@end + +// +// SFHFKeychainUtils.h +// +// Created by Buzz Andersen on 10/20/08. +// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. +// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +@interface SFHFKeychainUtils : NSObject { + +} + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; ++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error; ++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; + +@end \ No newline at end of file diff --git a/iOS/Keychain/SAiOSKeychainPlugin.js b/iOS/Keychain/SAiOSKeychainPlugin.js new file mode 100644 index 0000000..1050118 --- /dev/null +++ b/iOS/Keychain/SAiOSKeychainPlugin.js @@ -0,0 +1,126 @@ +// ////////////////////////////////////// +// Keychain PhoneGap Plugin +// by Shazron Abdullah +// Nov 5th 2010 +// + + +// /////////////////// +(function(){ +// /////////////////// + +// get local ref to global PhoneGap/Cordova/cordova object for exec function +var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + +/** + * Constructor + */ +function SAiOSKeychainPlugin() +{ + this._getCallbacks = {}; + this._setCallbacks = {}; + this._removeCallbacks = {}; +} + +//MARK: Get + +SAiOSKeychainPlugin.prototype._onGetCallbackSuccess = function(key, value) +{ + if (this._getCallbacks[key] && this._getCallbacks[key].onSuccess) { + this._getCallbacks[key].onSuccess(key, value); + } + delete this._getCallbacks[key]; +} + +SAiOSKeychainPlugin.prototype._onGetCallbackFail = function(key, error) +{ + if (this._getCallbacks[key] && this._getCallbacks[key].onFail) { + this._getCallbacks[key].onFail(key, error); + } + delete this._getCallbacks[key]; +} + + +SAiOSKeychainPlugin.prototype.getForKey = function(key, servicename, onSuccess, onFail) +{ + this._getCallbacks[key] = { onSuccess:onSuccess, onFail:onFail }; + + cordovaRef.exec("SAiOSKeychainPlugin.getForKey", key, servicename); +} + +//MARK: Set + +SAiOSKeychainPlugin.prototype._onSetCallbackSuccess = function(key) +{ + if (this._setCallbacks[key] && this._setCallbacks[key].onSuccess) { + this._setCallbacks[key].onSuccess(key); + } + delete this._setCallbacks[key]; +} + +SAiOSKeychainPlugin.prototype._onSetCallbackFail = function(key, error) +{ + if (this._setCallbacks[key] && this._setCallbacks[key].onFail) { + this._setCallbacks[key].onFail(key, error); + } + delete this._setCallbacks[key]; +} + +SAiOSKeychainPlugin.prototype.setForKey = function(key, value, servicename, onSuccess, onFail) +{ + this._setCallbacks[key] = { onSuccess:onSuccess, onFail:onFail }; + + cordovaRef.exec("SAiOSKeychainPlugin.setForKey", key, value, servicename); +} + +//MARK: Remove + +SAiOSKeychainPlugin.prototype._onRemoveCallbackSuccess = function(key) +{ + if (this._removeCallbacks[key] && this._removeCallbacks[key].onSuccess) { + this._removeCallbacks[key].onSuccess(key); + } + delete this._removeCallbacks[key]; +} + +SAiOSKeychainPlugin.prototype._onRemoveCallbackFail = function(key, error) +{ + if (this._removeCallbacks[key] && this._removeCallbacks[key].onFail) { + this._removeCallbacks[key].onFail(key, error); + } + delete this._removeCallbacks[key]; +} + +SAiOSKeychainPlugin.prototype.removeForKey = function(key, servicename, onSuccess, onFail) +{ + this._removeCallbacks[key] = { onSuccess:onSuccess, onFail:onFail }; + + cordovaRef.exec("SAiOSKeychainPlugin.removeForKey", key, servicename); +} + +//MARK: Install + +SAiOSKeychainPlugin.install = function() +{ + if ( !window.plugins ) { + window.plugins = {}; + } + if ( !window.plugins.keychain ) { + window.plugins.keychain = new SAiOSKeychainPlugin(); + } +} + +/** + * Add to Cordova constructor + */ +if (cordovaRef && cordovaRef.addConstructor) { + cordovaRef.addConstructor(SAiOSKeychainPlugin.install); +} else { + console.log("Keychain Cordova Plugin could not be installed."); + return null; +} + +// /////////////////// +})(); +// /////////////////// + diff --git a/iOS/Keychain/SAiOSKeychainPlugin.m b/iOS/Keychain/SAiOSKeychainPlugin.m new file mode 100644 index 0000000..fe25133 --- /dev/null +++ b/iOS/Keychain/SAiOSKeychainPlugin.m @@ -0,0 +1,521 @@ +// +// SAiOSPaypalPlugin.m +// Cordova Plugin for Cordova +// +// Created by shazron on 10-11-05. +// Copyright 2010 Shazron Abdullah. All rights reserved. + +#import "SAiOSKeychainPlugin.h" + +@implementation SAiOSKeychainPlugin + +-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (SAiOSKeychainPlugin*)[super initWithWebView:(UIWebView*)theWebView]; + if (self) { + // initialization here + } + return self; +} + +- (void) getForKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + int argc = [arguments count]; + if (argc < 2) { + return; + } + + NSString* key = [arguments objectAtIndex:0]; + NSString* serviceName = [arguments objectAtIndex:1]; + NSError* error = nil; + + NSString* value = [SFHFKeychainUtils getPasswordForUsername:key andServiceName:serviceName error:&error]; + if (error == nil && value != nil) { + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.keychain._onGetCallbackSuccess(\"%@\", \"%@\");", key, value]; + [super writeJavascript:jsCallback]; + } else { + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.keychain._onGetCallbackFail(\"%@\", \"%@\");", key, [error localizedDescription]]; + [super writeJavascript:jsCallback]; + } +} + +- (void) setForKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + int argc = [arguments count]; + if (argc < 3) { + return; + } + + NSString* key = [arguments objectAtIndex:0]; + NSString* value = [arguments objectAtIndex:1]; + NSString* serviceName = [arguments objectAtIndex:2]; + NSError* error = nil; + + BOOL stored = [SFHFKeychainUtils storeUsername:key andPassword:value forServiceName:serviceName updateExisting:YES error:&error]; + if (stored) { + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.keychain._onSetCallbackSuccess(\"%@\");", key]; + [super writeJavascript:jsCallback]; + } else { + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.keychain._onSetCallbackFail(\"%@\", \"%@\");", key, [error localizedDescription]]; + [super writeJavascript:jsCallback]; + } +} + +- (void) removeForKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + int argc = [arguments count]; + if (argc < 2) { + return; + } + + NSString* key = [arguments objectAtIndex:0]; + NSString* serviceName = [arguments objectAtIndex:1]; + NSError* error = nil; + + BOOL deleted = [SFHFKeychainUtils deleteItemForUsername:key andServiceName:serviceName error:&error]; + if (deleted) { + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.keychain._onRemoveCallbackSuccess(\"%@\");", key]; + [super writeJavascript:jsCallback]; + } else { + NSString* jsCallback = [NSString stringWithFormat:@"window.plugins.keychain._onRemoveCallbackFail(\"%@\", \"%@\");", key, [error localizedDescription]]; + [super writeJavascript:jsCallback]; + } +} + + +@end + + +// +// SFHFKeychainUtils.m +// +// Created by Buzz Andersen on 10/20/08. +// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone. +// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain"; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR +@interface SFHFKeychainUtils (PrivateMethods) ++ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error; +@end +#endif + +@implementation SFHFKeychainUtils + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + return nil; + } + + SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error]; + + if (*error || !item) { + return nil; + } + + // from Advanced Mac OS X Programming, ch. 16 + UInt32 length; + char *password; + SecKeychainAttribute attributes[8]; + SecKeychainAttributeList list; + + attributes[0].tag = kSecAccountItemAttr; + attributes[1].tag = kSecDescriptionItemAttr; + attributes[2].tag = kSecLabelItemAttr; + attributes[3].tag = kSecModDateItemAttr; + + list.count = 4; + list.attr = attributes; + + OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password); + + if (status != noErr) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + return nil; + } + + NSString *passwordString = nil; + + if (password != NULL) { + char passwordBuffer[1024]; + + if (length > 1023) { + length = 1023; + } + strncpy(passwordBuffer, password, length); + + passwordBuffer[length] = '\0'; + passwordString = [NSString stringWithCString:passwordBuffer]; + } + + SecKeychainItemFreeContent(&list, password); + + CFRelease(item); + + return passwordString; +} + ++ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error { + if (!username || !password || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + return; + } + + OSStatus status = noErr; + + SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error]; + + if (*error && [*error code] != noErr) { + return; + } + + *error = nil; + + if (item) { + status = SecKeychainItemModifyAttributesAndData(item, + NULL, + strlen([password UTF8String]), + [password UTF8String]); + + CFRelease(item); + } + else { + status = SecKeychainAddGenericPassword(NULL, + strlen([serviceName UTF8String]), + [serviceName UTF8String], + strlen([username UTF8String]), + [username UTF8String], + strlen([password UTF8String]), + [password UTF8String], + NULL); + } + + if (status != noErr) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } +} + ++ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil]; + return; + } + + *error = nil; + + SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error]; + + if (*error && [*error code] != noErr) { + return; + } + + OSStatus status; + + if (item) { + status = SecKeychainItemDelete(item); + + CFRelease(item); + } + + if (status != noErr) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } +} + ++ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + return nil; + } + + *error = nil; + + SecKeychainItemRef item; + + OSStatus status = SecKeychainFindGenericPassword(NULL, + strlen([serviceName UTF8String]), + [serviceName UTF8String], + strlen([username UTF8String]), + [username UTF8String], + NULL, + NULL, + &item); + + if (status != noErr) { + if (status != errSecItemNotFound) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } + + return nil; + } + + return item; +} + +#else + ++ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error { + if (!username || !serviceName) { + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return nil; + } + + if (error != nil) { + *error = nil; + } + + // Set up a query dictionary with the base query attributes: item type (generic), username, and service + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil] autorelease]; + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil] autorelease]; + + NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + // First do a query for attributes, in case we already have a Keychain item with no password data set. + // One likely way such an incorrect item could have come about is due to the previous (incorrect) + // version of this code (which set the password as a generic attribute instead of password data). + + NSDictionary *attributeResult = NULL; + NSMutableDictionary *attributeQuery = [query mutableCopy]; + [attributeQuery setObject: (id) kCFBooleanTrue forKey:(id) kSecReturnAttributes]; + OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult); + + [attributeResult release]; + [attributeQuery release]; + + if (status != noErr) { + // No existing item found--simply return nil for the password + if (error != nil && status != errSecItemNotFound) { + //Only return an error if a real exception happened--not simply for "not found." + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } + + return nil; + } + + // We have an existing item, now query for the password data associated with it. + + NSData *resultData = nil; + NSMutableDictionary *passwordQuery = [query mutableCopy]; + [passwordQuery setObject: (id) kCFBooleanTrue forKey: (id) kSecReturnData]; + + status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData); + + [resultData autorelease]; + [passwordQuery release]; + + if (status != noErr) { + if (status == errSecItemNotFound) { + // We found attributes for the item previously, but no password now, so return a special error. + // Users of this API will probably want to detect this error and prompt the user to + // re-enter their credentials. When you attempt to store the re-entered credentials + // using storeUsername:andPassword:forServiceName:updateExisting:error + // the old, incorrect entry will be deleted and a new one with a properly encrypted + // password will be added. + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil]; + } + } + else { + // Something else went wrong. Simply return the normal Keychain API error code. + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + } + } + + return nil; + } + + NSString *password = nil; + + if (resultData) { + password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding]; + } + else { + // There is an existing item, but we weren't able to get password data for it for some reason, + // Possibly as a result of an item being incorrectly entered by the previous code. + // Set the -1999 error so the code above us can prompt the user again. + if (error != nil) { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil]; + } + } + + return [password autorelease]; +} + ++ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error +{ + if (!username || !password || !serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + // See if we already have a password entered for these credentials. + NSError *getError = nil; + NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError]; + + if ([getError code] == -1999) + { + // There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code. + // Delete the existing item before moving on entering a correct one. + + getError = nil; + + [self deleteItemForUsername: username andServiceName: serviceName error: &getError]; + + if ([getError code] != noErr) + { + if (error != nil) + { + *error = getError; + } + return NO; + } + } + else if ([getError code] != noErr) + { + if (error != nil) + { + *error = getError; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + OSStatus status = noErr; + + if (existingPassword) + { + // We have an existing, properly entered item with a password. + // Update the existing item. + + if (![existingPassword isEqualToString:password] && updateExisting) + { + //Only update if we're allowed to update existing. If not, simply do nothing. + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, + kSecAttrService, + kSecAttrLabel, + kSecAttrAccount, + nil] autorelease]; + + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, + serviceName, + serviceName, + username, + nil] autorelease]; + + NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *) kSecValueData]); + } + } + else + { + // No existing entry (or an existing, improperly entered, and therefore now + // deleted, entry). Create a new entry. + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, + kSecAttrService, + kSecAttrLabel, + kSecAttrAccount, + kSecValueData, + nil] autorelease]; + + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, + serviceName, + serviceName, + username, + [password dataUsingEncoding: NSUTF8StringEncoding], + nil] autorelease]; + + NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + status = SecItemAdd((CFDictionaryRef) query, NULL); + } + + if (error != nil && status != noErr) + { + // Something went wrong with adding the new item. Return the Keychain error code. + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + ++ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error +{ + if (!username || !serviceName) + { + if (error != nil) + { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil]; + } + return NO; + } + + if (error != nil) + { + *error = nil; + } + + NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil] autorelease]; + NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil] autorelease]; + + NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease]; + + OSStatus status = SecItemDelete((CFDictionaryRef) query); + + if (error != nil && status != noErr) + { + *error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil]; + + return NO; + } + + return YES; +} + +#endif + +@end \ No newline at end of file diff --git a/iOS/Keychain/index.html b/iOS/Keychain/index.html new file mode 100755 index 0000000..7c9ba11 --- /dev/null +++ b/iOS/Keychain/index.html @@ -0,0 +1,125 @@ + + + + + + + + + paypal-plugin-host + + + + + + + + +
(using servicename )
+ +
+
+ +
GET FROM KEYCHAIN
+
+ + + +
+
+
+ +
SET TO KEYCHAIN

+ +
+ + + +
+
+
+
REMOVE FROM KEYCHAIN

+ + + + + + diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/master.css b/iOS/Keychain/master.css similarity index 100% rename from iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/master.css rename to iOS/Keychain/master.css diff --git a/iOS/LocalNotifications/plugin/LocalNotification.m b/iOS/LocalNotifications/plugin/LocalNotification.m index a971052..3fbd34e 100755 --- a/iOS/LocalNotifications/plugin/LocalNotification.m +++ b/iOS/LocalNotifications/plugin/LocalNotification.m @@ -42,7 +42,7 @@ notif.soundName = sound; notif.applicationIconBadgeNumber = badge; - NSDictionary *userDict = [NSDictionary dictionaryWithObjectsAndKeys:notificationId,@"notificationId",bg,@"background",fg,@"forground",nil]; + NSDictionary *userDict = [NSDictionary dictionaryWithObjectsAndKeys:notificationId,@"notificationId",bg,@"background",fg,@"foreground",nil]; notif.userInfo = userDict; diff --git a/iOS/LowLatencyAudio/examples/drum machine/assets/README.txt b/iOS/LowLatencyAudio/examples/drum machine/assets/README.txt new file mode 100644 index 0000000..0606e48 --- /dev/null +++ b/iOS/LowLatencyAudio/examples/drum machine/assets/README.txt @@ -0,0 +1,4 @@ +YOU MAY DO WHAT YOU WISH WITH THESE RECORDINGS. +I RECORDED THEM MYSELF FROM A SYNTH. + +-Andrew Trice \ No newline at end of file diff --git a/iOS/LowLatencyAudio/examples/drum machine/assets/bass drum.mp3 b/iOS/LowLatencyAudio/examples/drum machine/assets/bass drum.mp3 new file mode 100644 index 0000000..5b072eb Binary files /dev/null and b/iOS/LowLatencyAudio/examples/drum machine/assets/bass drum.mp3 differ diff --git a/iOS/LowLatencyAudio/examples/drum machine/assets/bongo.mp3 b/iOS/LowLatencyAudio/examples/drum machine/assets/bongo.mp3 new file mode 100644 index 0000000..ad35a4c Binary files /dev/null and b/iOS/LowLatencyAudio/examples/drum machine/assets/bongo.mp3 differ diff --git a/iOS/LowLatencyAudio/examples/drum machine/assets/carbonFiber.png b/iOS/LowLatencyAudio/examples/drum machine/assets/carbonFiber.png new file mode 100644 index 0000000..503fab7 Binary files /dev/null and b/iOS/LowLatencyAudio/examples/drum machine/assets/carbonFiber.png differ diff --git a/iOS/LowLatencyAudio/examples/drum machine/assets/high hat closed.mp3 b/iOS/LowLatencyAudio/examples/drum machine/assets/high hat closed.mp3 new file mode 100644 index 0000000..7e5021e Binary files /dev/null and b/iOS/LowLatencyAudio/examples/drum machine/assets/high hat closed.mp3 differ diff --git a/iOS/LowLatencyAudio/examples/drum machine/assets/snare drum.mp3 b/iOS/LowLatencyAudio/examples/drum machine/assets/snare drum.mp3 new file mode 100644 index 0000000..5916978 Binary files /dev/null and b/iOS/LowLatencyAudio/examples/drum machine/assets/snare drum.mp3 differ diff --git a/iOS/LowLatencyAudio/examples/drum machine/index.html b/iOS/LowLatencyAudio/examples/drum machine/index.html new file mode 100644 index 0000000..44978a6 --- /dev/null +++ b/iOS/LowLatencyAudio/examples/drum machine/index.html @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + +
Bass
+
High Hat
+
Snare
+
bongo
+ + + diff --git a/iOS/LowLatencyAudio/examples/game simulator/assets/README.txt b/iOS/LowLatencyAudio/examples/game simulator/assets/README.txt new file mode 100644 index 0000000..750cc9b --- /dev/null +++ b/iOS/LowLatencyAudio/examples/game simulator/assets/README.txt @@ -0,0 +1,27 @@ +YOU CAN FIND THE FOLLOWING FILES AT THESE LOCATIONS: +I did not include them b/c of licensing. + +air raid.mp3: +http://www.freesound.org/people/guitarguy1985/sounds/57808/ + +war2.mp3: +http://www.freesound.org/people/Omar%20Alvarado/sounds/96533/ + +war.mp3: +http://www.freesound.org/people/Syna-Max/sounds/56900/ + +thunder.mp3: +http://www.freesound.org/people/RHumphries/sounds/2523/ + +missile strike.mp3: +http://www.freesound.org/people/digifishmusic/sounds/42024/ + +machine gun.mp3: +http://www.freesound.org/people/Matt_G/sounds/30749/ + +hirosima-radio.mp3: +http://www.freesound.org/people/ERH/sounds/31644/ + +explision.mp3: +http://www.freesound.org/people/ljudman/sounds/33245/ + diff --git a/iOS/LowLatencyAudio/examples/game simulator/assets/background.mp3 b/iOS/LowLatencyAudio/examples/game simulator/assets/background.mp3 new file mode 100644 index 0000000..b7c896d Binary files /dev/null and b/iOS/LowLatencyAudio/examples/game simulator/assets/background.mp3 differ diff --git a/iOS/LowLatencyAudio/examples/game simulator/index.html b/iOS/LowLatencyAudio/examples/game simulator/index.html new file mode 100644 index 0000000..5dca1a0 --- /dev/null +++ b/iOS/LowLatencyAudio/examples/game simulator/index.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Air RaidPlayLoopStop
Background MusicPlayLoopStop
ExplosionPlayLoopStop
Hiroshima Radio BroadcastPlayLoopStop
Machine GunPlayLoopStop
Missile StrikePlayLoopStop
Thunderstorm AmbiencePlayLoopStop
"War" SoundsPlayLoopStop
"War" Sounds (different)PlayLoopStop
+ + + + diff --git a/iOS/MapKit/MapKit.js b/iOS/MapKit/MapKit.js index 45852ef..f3dd15b 100644 --- a/iOS/MapKit/MapKit.js +++ b/iOS/MapKit/MapKit.js @@ -1,4 +1,7 @@ -(function(window) { + +(function() { + +var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the @@ -70,4 +73,4 @@ window.plugins.mapKit = new MapKit(); }); -}(window)); \ No newline at end of file +})(); \ No newline at end of file diff --git a/iOS/MessageBox/MessageBox.m b/iOS/MessageBox/MessageBox.m index 8a32891..228d497 100644 --- a/iOS/MessageBox/MessageBox.m +++ b/iOS/MessageBox/MessageBox.m @@ -55,7 +55,8 @@ [textField setPlaceholder:placeholder]; } if ([[type lowercaseString] isEqualToString:@"password"]) [textField setSecureTextEntry:YES]; - if ([[type lowercaseString] isEqualToString:@"decimalpad"]) [textField setKeyboardType:UIKeyboardTypeDecimalPad]; + if ([[type lowercaseString] isEqualToString:@"decimalpad"]) [textField setKeyboardType:UIKeyboardTypeDecimalPad]; + if ([[type lowercaseString] isEqualToString:@"email"]) [textField setKeyboardType:UIKeyboardTypeEmailAddress]; [prompt addSubview:textField]; diff --git a/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBar.xib b/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBar.xib new file mode 100644 index 0000000..0fc23f5 --- /dev/null +++ b/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBar.xib @@ -0,0 +1,223 @@ + + + + 1280 + 10K549 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + YES + IBUINavigationItem + IBUIBarButtonItem + IBUINavigationBar + IBProxyObject + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 290 + {320, 44} + + + + IBCocoaTouchFramework + + YES + + + Title + + Item + IBCocoaTouchFramework + 1 + + + + Item + IBCocoaTouchFramework + 1 + + + IBCocoaTouchFramework + + + + + + + YES + + + leftButton + + + + 38 + + + + rightButton + + + + 39 + + + + view + + + + 40 + + + + navItem + + + + 42 + + + + leftButtonTapped: + + + + 43 + + + + rightButtonTapped: + + + + 44 + + + + + YES + + 0 + + YES + + + + + + -1 + + + File's Owner + + + -2 + + + + + 34 + + + YES + + + + + + 35 + + + YES + + + + + + + 36 + + + + + 37 + + + + + + + YES + + YES + -1.CustomClassName + -1.IBPluginDependency + -2.CustomClassName + -2.IBPluginDependency + 34.IBPluginDependency + 35.IBPluginDependency + 36.IBPluginDependency + 37.IBPluginDependency + + + YES + PGNavigationBarController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + + + + YES + + + + + 44 + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + 3 + 933 + + diff --git a/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBarController.h b/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBarController.h new file mode 100644 index 0000000..b259623 --- /dev/null +++ b/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBarController.h @@ -0,0 +1,33 @@ +// +// CDVNavigationBarController.h +// HelloPhoneGap1 +// +// Created by Hiedi Utley on 8/18/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import + +@protocol CDVNavigationBarDelegate +-(void)leftButtonTapped; +-(void)rightButtonTapped; +@end + +@interface CDVNavigationBarController : UIViewController{ + + IBOutlet UIBarButtonItem * leftButton; + IBOutlet UIBarButtonItem * rightButton; + IBOutlet UINavigationItem * navItem; + id delegate; + +} + +@property (nonatomic, retain) UINavigationItem * navItem; +@property (nonatomic, retain) UIBarButtonItem * leftButton; +@property (nonatomic, retain) UIBarButtonItem * rightButton; +@property (nonatomic, retain) id delegate; + +-(IBAction)leftButtonTapped:(id)sender; +-(IBAction)rightButtonTapped:(id)sender; + +@end diff --git a/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBarController.m b/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBarController.m new file mode 100644 index 0000000..af0b464 --- /dev/null +++ b/iOS/NavigationBar/2.0.0 (not developed anymore)/CDVNavigationBarController.m @@ -0,0 +1,88 @@ +// +// CDVNavigationBarController.m +// HelloPhoneGap1 +// +// Created by Hiedi Utley on 8/18/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "CDVNavigationBarController.h" + +@implementation CDVNavigationBarController +@synthesize navItem; +@synthesize leftButton; +@synthesize rightButton; +@synthesize delegate; + +-(id) init +{ + self = [super initWithNibName:@"CDVNavigationBar" bundle:nil]; + if (self) { + // Custom initialization + } + return self; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + return [self init]; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +-(void) dealloc +{ + [navItem release]; + [leftButton release]; + [rightButton release]; + [delegate release]; + [super dealloc]; + +} +#pragma mark - View lifecycle + +/* +// Implement loadView to create a view hierarchy programmatically, without using a nib. +- (void)loadView +{ +} +*/ + +/* +// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. +- (void)viewDidLoad +{ + [super viewDidLoad]; +} +*/ + +- (void)viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +-(IBAction)leftButtonTapped:(id)sender +{ + [delegate leftButtonTapped]; +} +-(IBAction)rightButtonTapped:(id)sender +{ + [delegate rightButtonTapped]; +} + + +@end diff --git a/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.h b/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.h new file mode 100644 index 0000000..2e49aa7 --- /dev/null +++ b/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.h @@ -0,0 +1,44 @@ +#import +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVPlugin.h" +#import + +#import "CDVNavigationBarController.h" + +@interface NavigationBar : CDVPlugin { + UINavigationBar * navBar; + + // Represents bounds as if started in portrait mode! + CGRect originalWebViewBounds; + + CGFloat navBarHeight; + CGFloat tabBarHeight; + + CDVNavigationBarController * navBarController; +} + +@property (nonatomic, retain) CDVNavigationBarController *navBarController; + +- (void)create:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)setTitle:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options; +- (void)setLogo:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options; +- (void)show:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)hide:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)init:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)setupLeftButton:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)setupRightButton:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)leftButtonTapped; +- (void)rightButtonTapped; + +- (void)hideLeftButton:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)showRightButton:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)hideLeftButton:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)showRightButton:(NSArray*)arguments withDict:(NSDictionary*)options; + +@end + +@interface UITabBar (NavBarCompat) +@property (nonatomic) bool tabBarAtBottom; +@end \ No newline at end of file diff --git a/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.js b/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.js new file mode 100644 index 0000000..ac83b2f --- /dev/null +++ b/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.js @@ -0,0 +1,117 @@ +function NavigationBar() { + this.leftButtonCallback = null; + this.rightButtonCallback = null; +} + +/** + * Create a navigation bar. + * + * @param style: One of "BlackTransparent", "BlackOpaque", "Black" or "Default". The latter will be used if no style is given. + */ +NavigationBar.prototype.create = function(style, options) +{ + cordova.exec("NavigationBar.create", style || "Default", options || {}); +}; + +/** + * Must be called before any other method in order to initialize the plugin. + */ +NavigationBar.prototype.init = function() +{ + cordova.exec("NavigationBar.init"); +}; + +NavigationBar.prototype.resize = function() { + cordova.exec("NavigationBar.resize"); +}; + +/** + * Assign either title or image to the left navigation bar button, and assign the tap callback +*/ +NavigationBar.prototype.setupLeftButton = function(title, image, onselect, options) +{ + this.leftButtonCallback = onselect; + cordova.exec("NavigationBar.setupLeftButton", title || "", image || "", options || {}); +}; + +NavigationBar.prototype.hideLeftButton = function() +{ + cordova.exec("NavigationBar.hideLeftButton"); +}; + +NavigationBar.prototype.showLeftButton = function() +{ + cordova.exec("NavigationBar.showLeftButton"); +}; + +/** + * Internal function called by the plugin + */ +NavigationBar.prototype.leftButtonTapped = function() +{ + if(typeof(this.leftButtonCallback) === "function") + this.leftButtonCallback() +}; + +/** + * Assign either title or image to the right navigation bar button, and assign the tap callback +*/ +NavigationBar.prototype.setupRightButton = function(title, image, onselect, options) +{ + this.rightButtonCallback = onselect; + cordova.exec("NavigationBar.setupRightButton", title || "", image || "", options || {}); +}; + + +NavigationBar.prototype.hideRightButton = function() +{ + cordova.exec("NavigationBar.hideRightButton"); +}; + +NavigationBar.prototype.showRightButton = function() +{ + cordova.exec("NavigationBar.showRightButton"); +}; + +/** + * Internal function called by the plugin + */ +NavigationBar.prototype.rightButtonTapped = function() +{ + if(typeof(this.rightButtonCallback) === "function") + this.rightButtonCallback() +}; + +NavigationBar.prototype.setTitle = function(title) +{ + cordova.exec("NavigationBar.setTitle", title); +}; + +NavigationBar.prototype.setLogo = function(imageURL) +{ + cordova.exec("NavigationBar.setLogo", imageURL); +}; + +/** + * Shows the navigation bar. Make sure you called create() first. + */ +NavigationBar.prototype.show = function() { + cordova.exec("NavigationBar.show"); +}; + +/** + * Hides the navigation bar. Make sure you called create() first. + */ +NavigationBar.prototype.hide = function() { + + cordova.exec("NavigationBar.hide"); +}; + +cordova.addConstructor(function() +{ + if(!window.plugins) + { + window.plugins = {}; + } + window.plugins.navigationBar = new NavigationBar(); +}); diff --git a/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.m b/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.m new file mode 100644 index 0000000..d2ddaa3 --- /dev/null +++ b/iOS/NavigationBar/2.0.0 (not developed anymore)/NavigationBar.m @@ -0,0 +1,435 @@ +/* + NavigationBar.m + + Work based on the NativeControls plugin (Jesse MacFadyen, MIT licensed) and additions made by Hiedi Utley + (https://github.com/hutley/HelloPhoneGap1.0/) and zSprawl (https://github.com/zSprawl/NativeControls/). + + Navigation bar API cleaned, improved and moved in a separate plugin by Andreas Sommer + (AndiDog, https://github.com/AndiDog/phonegap-plugins). + */ + +#import "NavigationBar.h" +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVDebug.h" +#import + +@implementation NavigationBar +#ifndef __IPHONE_3_0 +@synthesize webView; +#endif +@synthesize navBarController; + +-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (NavigationBar*)[super initWithWebView:theWebView]; + if(self) + { + // The original web view bounds must be retrieved here. On iPhone, it would be 0,0,320,460 for example. Since + // Cordova seems to initialize plugins on the first call, there is a plugin method init() that has to be called + // in order to make Cordova call *this* method. If someone forgets the init() call and uses the navigation bar + // and tab bar plugins together, these values won't be the original web view bounds and layout will be wrong. + originalWebViewBounds = theWebView.bounds; + + // This code block is the same for both the navigation and tab bar plugin! + UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation]; + if(UIInterfaceOrientationIsLandscape(interfaceOrientation)) + { + // In this case, the app started in landscape mode and the web view is sized accordingly. The frame + // (0,0,480,300) is expected on the non-Retina display. Since the originalWebViewBounds variable represents + // the original frame as if the app was started in portrait mode, the values are corrected here: + + UIApplication *app = [UIApplication sharedApplication]; + + if(app.statusBarHidden) + { + originalWebViewBounds.size.width = theWebView.bounds.size.height; + originalWebViewBounds.size.height = theWebView.bounds.size.width; + } + else + { + float statusBarHeight = MIN(app.statusBarFrame.size.width, app.statusBarFrame.size.height); + originalWebViewBounds.size.width = theWebView.bounds.size.height + statusBarHeight; + originalWebViewBounds.size.height = theWebView.bounds.size.width - statusBarHeight; + } + } + + navBarHeight = 44.0f; + tabBarHeight = 49.0f; + } + return self; +} + +- (void)dealloc +{ + if (navBar) + [navBar release]; + + if (navBarController) + [navBarController release]; + + [super dealloc]; +} + +// NOTE: Returned object is owned +-(UIBarButtonItem*)backgroundButtonFromImage:(NSString*)imageName title:(NSString*)title fixedMarginLeft:(float)fixedMarginLeft fixedMarginRight:(float)fixedMarginRight target:(id)target action:(SEL)action +{ + UIButton *backButton = [[UIButton alloc] init]; + UIImage *imgNormal = [UIImage imageNamed:imageName]; + + // UIImage's resizableImageWithCapInsets method is only available from iOS 5.0. With earlier versions, the + // stretchableImageWithLeftCapWidth is used which behaves a bit differently. + if([imgNormal respondsToSelector:@selector(resizableImageWithCapInsets)]) + imgNormal = [imgNormal resizableImageWithCapInsets:UIEdgeInsetsMake(0, fixedMarginLeft, 0, fixedMarginRight)]; + else + imgNormal = [imgNormal stretchableImageWithLeftCapWidth:MAX(fixedMarginLeft, fixedMarginRight) topCapHeight:0]; + + [backButton setBackgroundImage:imgNormal forState:UIControlStateNormal]; + + backButton.titleLabel.textColor = [UIColor whiteColor]; + backButton.titleLabel.font = [UIFont boldSystemFontOfSize:12.0f]; + backButton.titleLabel.textAlignment = UITextAlignmentCenter; + + CGSize textSize = [title sizeWithFont:backButton.titleLabel.font]; + + float buttonWidth = MAX(imgNormal.size.width, textSize.width + fixedMarginLeft + fixedMarginRight);//imgNormal.size.width > (textSize.width + fixedMarginLeft + fixedMarginRight) + //? imgNormal.size.width : (textSize.width + fixedMarginLeft + fixedMarginRight); + backButton.frame = CGRectMake(0, 0, buttonWidth, imgNormal.size.height); + + CGFloat marginTopBottom = (backButton.frame.size.height - textSize.height) / 2; + [backButton setTitleEdgeInsets:UIEdgeInsetsMake(marginTopBottom, fixedMarginLeft, marginTopBottom, fixedMarginRight)]; + + [backButton setTitle:title forState:UIControlStateNormal]; + [backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [backButton.titleLabel setFont:[UIFont boldSystemFontOfSize:12.0f]]; + + UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; + [backButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; + + [backButton release]; + // imgNormal is autoreleased + + return backButtonItem; +} + +-(void)correctWebViewBounds +{ + if(!navBar) + return; + + const bool navBarShown = !navBar.hidden; + bool tabBarShown = false; + bool tabBarAtBottom = true; + + UIView *parent = [navBar superview]; + for(UIView *view in parent.subviews) + if([view isMemberOfClass:[UITabBar class]]) + { + tabBarShown = !view.hidden; + + // Tab bar height is customizable + if(tabBarShown) + { + tabBarHeight = view.bounds.size.height; + + // Since the navigation bar plugin plays together with the tab bar plugin, and the tab bar can as well + // be positioned at the top, here's some magic to find out where it's positioned: + tabBarAtBottom = true; + if([view respondsToSelector:@selector(tabBarAtBottom)]) + tabBarAtBottom = [view performSelector:@selector(tabBarAtBottom)]; + } + + break; + } + + // IMPORTANT: Below code is the same in both the navigation and tab bar plugins! + + CGFloat left = originalWebViewBounds.origin.x; + CGFloat right = left + originalWebViewBounds.size.width; + CGFloat top = originalWebViewBounds.origin.y; + CGFloat bottom = top + originalWebViewBounds.size.height; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + switch (orientation) + { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + // No need to change width/height from original bounds + break; + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + right = left + originalWebViewBounds.size.height + 20.0f; + bottom = top + originalWebViewBounds.size.width - 20.0f; + break; + default: + NSLog(@"Unknown orientation: %d", orientation); + break; + } + + if(navBarShown) + top += navBarHeight; + + if(tabBarShown) + { + if(tabBarAtBottom) + bottom -= tabBarHeight; + else + top += tabBarHeight; + } + + CGRect webViewBounds = CGRectMake(left, top, right - left, bottom - top); + + [self.webView setFrame:webViewBounds]; + + // NOTE: Following part again for navigation bar plugin only + + if(navBarShown) + { + if(tabBarAtBottom) + [navBar setFrame:CGRectMake(left, originalWebViewBounds.origin.y, right - left, navBarHeight)]; + else + [navBar setFrame:CGRectMake(left, originalWebViewBounds.origin.y + tabBarHeight, right - left, navBarHeight)]; + } +} + +/*********************************************************************************/ + +-(void) create:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + if (!navBar) + { + navBarController = [[CDVNavigationBarController alloc] init]; + navBar = (UINavigationBar*)[navBarController view]; + + NSString * style = [arguments objectAtIndex:0]; + + if(style && [style isEqualToString:@"BlackTranslucent"]) + navBar.barStyle = UIBarStyleBlackTranslucent; + else if(style && [style isEqualToString:@"BlackOpaque"]) + navBar.barStyle = UIBarStyleBlackOpaque; + else if(style && [style isEqualToString:@"Black"]) + navBar.barStyle = UIBarStyleBlack; + // else the default will be used + + NSString *tint = [options valueForKey:@"tintColorRgba"]; + + if(tint) + { + NSArray *rgba = [tint componentsSeparatedByString:@","]; + navBar.tintColor = [UIColor colorWithRed:[[rgba objectAtIndex:0] intValue]/255.0f + green:[[rgba objectAtIndex:1] intValue]/255.0f + blue:[[rgba objectAtIndex:2] intValue]/255.0f + alpha:[[rgba objectAtIndex:3] intValue]/255.0f]; + } + + [navBarController setDelegate:self]; + + [[navBarController view] setFrame:CGRectMake(0, 0, originalWebViewBounds.size.width, navBarHeight)]; + [[[self webView] superview] addSubview:[navBarController view]]; + [navBar setHidden:YES]; + } +} + ++ (UIBarButtonSystemItem)getUIBarButtonSystemItemForString:(NSString*)imageName +{ + UIBarButtonSystemItem systemItem = -1; + + if([imageName isEqualToString:@"barButton:Action"]) systemItem = UIBarButtonSystemItemAction; + else if([imageName isEqualToString:@"barButton:Add"]) systemItem = UIBarButtonSystemItemAdd; + else if([imageName isEqualToString:@"barButton:Bookmarks"]) systemItem = UIBarButtonSystemItemBookmarks; + else if([imageName isEqualToString:@"barButton:Camera"]) systemItem = UIBarButtonSystemItemCamera; + else if([imageName isEqualToString:@"barButton:Cancel"]) systemItem = UIBarButtonSystemItemCancel; + else if([imageName isEqualToString:@"barButton:Compose"]) systemItem = UIBarButtonSystemItemCompose; + else if([imageName isEqualToString:@"barButton:Done"]) systemItem = UIBarButtonSystemItemDone; + else if([imageName isEqualToString:@"barButton:Edit"]) systemItem = UIBarButtonSystemItemEdit; + else if([imageName isEqualToString:@"barButton:FastForward"]) systemItem = UIBarButtonSystemItemFastForward; + else if([imageName isEqualToString:@"barButton:FixedSpace"]) systemItem = UIBarButtonSystemItemFixedSpace; + else if([imageName isEqualToString:@"barButton:FlexibleSpace"]) systemItem = UIBarButtonSystemItemFlexibleSpace; + else if([imageName isEqualToString:@"barButton:Organize"]) systemItem = UIBarButtonSystemItemOrganize; + else if([imageName isEqualToString:@"barButton:PageCurl"]) systemItem = UIBarButtonSystemItemPageCurl; + else if([imageName isEqualToString:@"barButton:Pause"]) systemItem = UIBarButtonSystemItemPause; + else if([imageName isEqualToString:@"barButton:Play"]) systemItem = UIBarButtonSystemItemPlay; + else if([imageName isEqualToString:@"barButton:Redo"]) systemItem = UIBarButtonSystemItemRedo; + else if([imageName isEqualToString:@"barButton:Refresh"]) systemItem = UIBarButtonSystemItemRefresh; + else if([imageName isEqualToString:@"barButton:Reply"]) systemItem = UIBarButtonSystemItemReply; + else if([imageName isEqualToString:@"barButton:Rewind"]) systemItem = UIBarButtonSystemItemRewind; + else if([imageName isEqualToString:@"barButton:Save"]) systemItem = UIBarButtonSystemItemSave; + else if([imageName isEqualToString:@"barButton:Search"]) systemItem = UIBarButtonSystemItemSearch; + else if([imageName isEqualToString:@"barButton:Stop"]) systemItem = UIBarButtonSystemItemStop; + else if([imageName isEqualToString:@"barButton:Trash"]) systemItem = UIBarButtonSystemItemTrash; + else if([imageName isEqualToString:@"barButton:Undo"]) systemItem = UIBarButtonSystemItemUndo; + + return systemItem; +} + +-(void) init:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + // Dummy function, see initWithWebView +} + +// NOTE: Returned object is owned +- (UIBarButtonItem*)makeButtonWithOptions:(NSDictionary*)options title:(NSString*)title imageName:(NSString*)imageName actionOnSelf:(SEL)actionOnSelf +{ + NSNumber *useImageAsBackgroundOpt = [options objectForKey:@"useImageAsBackground"]; + float fixedMarginLeft = [[options objectForKey:@"fixedMarginLeft"] floatValue] ?: 13; + float fixedMarginRight = [[options objectForKey:@"fixedMarginRight"] floatValue] ?: 13; + bool useImageAsBackground = useImageAsBackgroundOpt ? [useImageAsBackgroundOpt boolValue] : false; + + if((title && [title length] > 0) || useImageAsBackground) + { + if(useImageAsBackground && imageName && [imageName length] > 0) + { + return [self backgroundButtonFromImage:imageName title:title + fixedMarginLeft:fixedMarginLeft fixedMarginRight:fixedMarginRight + target:self action:actionOnSelf]; + } + else + { + return [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:self action:actionOnSelf]; + } + } + else if (imageName && [imageName length] > 0) + { + UIBarButtonSystemItem systemItem = [NavigationBar getUIBarButtonSystemItemForString:imageName]; + + if(systemItem != -1) + return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:systemItem target:self action:actionOnSelf]; + else + return [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:imageName] style:UIBarButtonItemStylePlain target:self action:actionOnSelf]; + } + else + { + // Fail silently + NSLog(@"Invalid setup{Left/Right}Button parameters\n"); + return nil; + } +} + +- (void)setupLeftButton:(NSArray*)arguments withDict:(NSDictionary*)options +{ + NSString * title = [arguments objectAtIndex:0]; + NSString * imageName = [arguments objectAtIndex:1]; + + UIBarButtonItem *newButton = [self makeButtonWithOptions:options title:title imageName:imageName actionOnSelf:@selector(leftButtonTapped)]; + navBarController.navItem.leftBarButtonItem = newButton; + navBarController.leftButton = newButton; + [newButton release]; +} + +- (void)setupRightButton:(NSArray*)arguments withDict:(NSDictionary*)options +{ + NSString * title = [arguments objectAtIndex:0]; + NSString * imageName = [arguments objectAtIndex:1]; + + UIBarButtonItem *newButton = [self makeButtonWithOptions:options title:title imageName:imageName actionOnSelf:@selector(rightButtonTapped)]; + navBarController.navItem.rightBarButtonItem = newButton; + navBarController.rightButton = newButton; + [newButton release]; +} + +- (void)hideLeftButton:(NSArray*)arguments withDict:(NSDictionary*)options +{ + [[navBarController navItem] setLeftBarButtonItem:nil]; +} + +- (void)showLeftButton:(NSArray*)arguments withDict:(NSDictionary*)options +{ + [[navBarController navItem] setLeftBarButtonItem:[navBarController leftButton]]; +} + +- (void)hideRightButton:(NSArray*)arguments withDict:(NSDictionary*)options +{ + [[navBarController navItem] setRightBarButtonItem:nil]; +} + +- (void)showRightButton:(NSArray*)arguments withDict:(NSDictionary*)options +{ + [[navBarController navItem] setRightBarButtonItem:[navBarController rightButton]]; +} + +-(void) leftButtonTapped +{ + NSString * jsCallBack = @"window.plugins.navigationBar.leftButtonTapped();"; + [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack]; +} + +-(void) rightButtonTapped +{ + NSString * jsCallBack = @"window.plugins.navigationBar.rightButtonTapped();"; + [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack]; +} + +-(void) show:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + if (!navBar) + [self create:nil withDict:nil]; + + if ([navBar isHidden]) + { + [navBar setHidden:NO]; + [self correctWebViewBounds]; + } +} + + +-(void) hide:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + if (navBar && ![navBar isHidden]) + { + [navBar setHidden:YES]; + [self correctWebViewBounds]; + } +} + +/** + * Resize the navigation bar (this should be called on orientation change) + * This is important in playing together with the tab bar plugin, especially because the tab bar can be placed on top + * or at the bottom, so the navigation bar bounds also need to be changed. + * + * @param arguments unused + * @param options unused + */ +- (void)resize:(NSArray*)arguments withDict:(NSDictionary*)options +{ + [self correctWebViewBounds]; +} + +-(void) setTitle:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + if (navBar) + { + NSString *name = [arguments objectAtIndex:0]; + [navBarController navItem].title = name; + + // Reset otherwise overriding logo reference + [navBarController navItem].titleView = NULL; + } +} + +-(void) setLogo:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + NSString * logoURL = [arguments objectAtIndex:0]; + UIImage * image = nil; + + if (logoURL && logoURL != @"") + { + if ([logoURL hasPrefix:@"http://"] || [logoURL hasPrefix:@"https://"]) + { + NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoURL]]; + image = [UIImage imageWithData:data]; + } + else + image = [UIImage imageNamed:logoURL]; + + if (image) + { + UIImageView * view = [[[UIImageView alloc] initWithImage:image] autorelease]; + [view setContentMode:UIViewContentModeScaleAspectFit]; + [view setBounds: CGRectMake(0, 0, 100, 30)]; + [[navBarController navItem] setTitleView:view]; + } + } +} + +@end diff --git a/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBar.xib b/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBar.xib new file mode 100644 index 0000000..0fc23f5 --- /dev/null +++ b/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBar.xib @@ -0,0 +1,223 @@ + + + + 1280 + 10K549 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + YES + IBUINavigationItem + IBUIBarButtonItem + IBUINavigationBar + IBProxyObject + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 290 + {320, 44} + + + + IBCocoaTouchFramework + + YES + + + Title + + Item + IBCocoaTouchFramework + 1 + + + + Item + IBCocoaTouchFramework + 1 + + + IBCocoaTouchFramework + + + + + + + YES + + + leftButton + + + + 38 + + + + rightButton + + + + 39 + + + + view + + + + 40 + + + + navItem + + + + 42 + + + + leftButtonTapped: + + + + 43 + + + + rightButtonTapped: + + + + 44 + + + + + YES + + 0 + + YES + + + + + + -1 + + + File's Owner + + + -2 + + + + + 34 + + + YES + + + + + + 35 + + + YES + + + + + + + 36 + + + + + 37 + + + + + + + YES + + YES + -1.CustomClassName + -1.IBPluginDependency + -2.CustomClassName + -2.IBPluginDependency + 34.IBPluginDependency + 35.IBPluginDependency + 36.IBPluginDependency + 37.IBPluginDependency + + + YES + PGNavigationBarController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + + + + YES + + + + + 44 + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + 3 + 933 + + diff --git a/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBarController.h b/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBarController.h new file mode 100644 index 0000000..b259623 --- /dev/null +++ b/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBarController.h @@ -0,0 +1,33 @@ +// +// CDVNavigationBarController.h +// HelloPhoneGap1 +// +// Created by Hiedi Utley on 8/18/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import + +@protocol CDVNavigationBarDelegate +-(void)leftButtonTapped; +-(void)rightButtonTapped; +@end + +@interface CDVNavigationBarController : UIViewController{ + + IBOutlet UIBarButtonItem * leftButton; + IBOutlet UIBarButtonItem * rightButton; + IBOutlet UINavigationItem * navItem; + id delegate; + +} + +@property (nonatomic, retain) UINavigationItem * navItem; +@property (nonatomic, retain) UIBarButtonItem * leftButton; +@property (nonatomic, retain) UIBarButtonItem * rightButton; +@property (nonatomic, retain) id delegate; + +-(IBAction)leftButtonTapped:(id)sender; +-(IBAction)rightButtonTapped:(id)sender; + +@end diff --git a/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBarController.m b/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBarController.m new file mode 100644 index 0000000..af0b464 --- /dev/null +++ b/iOS/NavigationBar/2.1.0 (non-ARC)/CDVNavigationBarController.m @@ -0,0 +1,88 @@ +// +// CDVNavigationBarController.m +// HelloPhoneGap1 +// +// Created by Hiedi Utley on 8/18/11. +// Copyright (c) 2011 __MyCompanyName__. All rights reserved. +// + +#import "CDVNavigationBarController.h" + +@implementation CDVNavigationBarController +@synthesize navItem; +@synthesize leftButton; +@synthesize rightButton; +@synthesize delegate; + +-(id) init +{ + self = [super initWithNibName:@"CDVNavigationBar" bundle:nil]; + if (self) { + // Custom initialization + } + return self; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + return [self init]; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +-(void) dealloc +{ + [navItem release]; + [leftButton release]; + [rightButton release]; + [delegate release]; + [super dealloc]; + +} +#pragma mark - View lifecycle + +/* +// Implement loadView to create a view hierarchy programmatically, without using a nib. +- (void)loadView +{ +} +*/ + +/* +// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. +- (void)viewDidLoad +{ + [super viewDidLoad]; +} +*/ + +- (void)viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +-(IBAction)leftButtonTapped:(id)sender +{ + [delegate leftButtonTapped]; +} +-(IBAction)rightButtonTapped:(id)sender +{ + [delegate rightButtonTapped]; +} + + +@end diff --git a/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.h b/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.h new file mode 100644 index 0000000..79e753c --- /dev/null +++ b/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.h @@ -0,0 +1,46 @@ +#import +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVPlugin.h" +#import + +#import "CDVNavigationBarController.h" + +@interface NavigationBar : CDVPlugin { + UINavigationBar * navBar; + + // Represents frame of web view as if started in portrait mode. Coordinates are relative to the superview. With + // Cordova 2.1.0, frame.origin.y=0 means directly under the status bar, while in older versions it would have been + // frame.origin.y=20. + CGRect originalWebViewFrame; + + CGFloat navBarHeight; + CGFloat tabBarHeight; + + CDVNavigationBarController * navBarController; +} + +@property (nonatomic, retain) CDVNavigationBarController *navBarController; + +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)setTitle:(CDVInvokedUrlCommand*)command; +- (void)setLogo:(CDVInvokedUrlCommand*)command; +- (void)show:(CDVInvokedUrlCommand*)command; +- (void)hide:(CDVInvokedUrlCommand*)command; +- (void)init:(CDVInvokedUrlCommand*)command; +- (void)setupLeftButton:(CDVInvokedUrlCommand*)command; +- (void)setupRightButton:(CDVInvokedUrlCommand*)command; +- (void)leftButtonTapped; +- (void)rightButtonTapped; + +- (void)hideLeftButton:(CDVInvokedUrlCommand*)command; +- (void)showRightButton:(CDVInvokedUrlCommand*)command; +- (void)hideLeftButton:(CDVInvokedUrlCommand*)command; +- (void)showRightButton:(CDVInvokedUrlCommand*)command; + +@end + +@interface UITabBar (NavBarCompat) +@property (nonatomic) bool tabBarAtBottom; +@end \ No newline at end of file diff --git a/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.js b/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.js new file mode 100644 index 0000000..1a72ffb --- /dev/null +++ b/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.js @@ -0,0 +1,121 @@ +function NavigationBar() { + this.leftButtonCallback = null; + this.rightButtonCallback = null; +} + +/** + * Create a navigation bar. + * + * @param style: One of "BlackTransparent", "BlackOpaque", "Black" or "Default". The latter will be used if no style is given. + */ +NavigationBar.prototype.create = function(style, options) +{ + options = options || {}; + if(!("style" in options)) + options.style = style || "Default"; + + cordova.exec("NavigationBar.create", options); +}; + +/** + * Must be called before any other method in order to initialize the plugin. + */ +NavigationBar.prototype.init = function() +{ + cordova.exec("NavigationBar.init"); +}; + +NavigationBar.prototype.resize = function() { + cordova.exec("NavigationBar.resize"); +}; + +/** + * Assign either title or image to the left navigation bar button, and assign the tap callback +*/ +NavigationBar.prototype.setupLeftButton = function(title, image, onselect, options) +{ + this.leftButtonCallback = onselect; + cordova.exec("NavigationBar.setupLeftButton", title || "", image || "", options || {}); +}; + +NavigationBar.prototype.hideLeftButton = function() +{ + cordova.exec("NavigationBar.hideLeftButton"); +}; + +NavigationBar.prototype.showLeftButton = function() +{ + cordova.exec("NavigationBar.showLeftButton"); +}; + +/** + * Internal function called by the plugin + */ +NavigationBar.prototype.leftButtonTapped = function() +{ + if(typeof(this.leftButtonCallback) === "function") + this.leftButtonCallback() +}; + +/** + * Assign either title or image to the right navigation bar button, and assign the tap callback +*/ +NavigationBar.prototype.setupRightButton = function(title, image, onselect, options) +{ + this.rightButtonCallback = onselect; + cordova.exec("NavigationBar.setupRightButton", title || "", image || "", options || {}); +}; + + +NavigationBar.prototype.hideRightButton = function() +{ + cordova.exec("NavigationBar.hideRightButton"); +}; + +NavigationBar.prototype.showRightButton = function() +{ + cordova.exec("NavigationBar.showRightButton"); +}; + +/** + * Internal function called by the plugin + */ +NavigationBar.prototype.rightButtonTapped = function() +{ + if(typeof(this.rightButtonCallback) === "function") + this.rightButtonCallback() +}; + +NavigationBar.prototype.setTitle = function(title) +{ + cordova.exec("NavigationBar.setTitle", title); +}; + +NavigationBar.prototype.setLogo = function(imageURL) +{ + cordova.exec("NavigationBar.setLogo", imageURL); +}; + +/** + * Shows the navigation bar. Make sure you called create() first. + */ +NavigationBar.prototype.show = function() { + cordova.exec("NavigationBar.show"); +}; + +/** + * Hides the navigation bar. Make sure you called create() first. + */ +NavigationBar.prototype.hide = function() { + + cordova.exec("NavigationBar.hide"); +}; + +cordova.addConstructor(function() +{ + if(!window.plugins) + { + window.plugins = {}; + } + window.plugins.navigationBar = new NavigationBar(); +}); diff --git a/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.m b/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.m new file mode 100755 index 0000000..b896b0a --- /dev/null +++ b/iOS/NavigationBar/2.1.0 (non-ARC)/NavigationBar.m @@ -0,0 +1,466 @@ +/* + NavigationBar.m + + Work based on the NativeControls plugin (Jesse MacFadyen, MIT licensed) and additions made by Hiedi Utley + (https://github.com/hutley/HelloPhoneGap1.0/) and zSprawl (https://github.com/zSprawl/NativeControls/). + + Navigation bar API cleaned, improved and moved in a separate plugin by Andreas Sommer + (AndiDog, https://github.com/AndiDog/phonegap-plugins). + */ + +#import "NavigationBar.h" +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVDebug.h" +#import + +@implementation NavigationBar +#ifndef __IPHONE_3_0 +@synthesize webView; +#endif +@synthesize navBarController; + +-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (NavigationBar*)[super initWithWebView:theWebView]; + if(self) + { + // ----------------------------------------------------------------------- + // This code block is the same for both the navigation and tab bar plugin! + // ----------------------------------------------------------------------- + + // The original web view frame must be retrieved here. On iPhone, it would be 0,0,320,460 for example. Since + // Cordova seems to initialize plugins on the first call, there is a plugin method init() that has to be called + // in order to make Cordova call *this* method. If someone forgets the init() call and uses the navigation bar + // and tab bar plugins together, these values won't be the original web view frame and layout will be wrong. + originalWebViewFrame = theWebView.frame; + UIApplication *app = [UIApplication sharedApplication]; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + switch (orientation) + { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + break; + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + { + float statusBarHeight = 0; + if(!app.statusBarHidden) + statusBarHeight = MIN(app.statusBarFrame.size.width, app.statusBarFrame.size.height); + + originalWebViewFrame = CGRectMake(originalWebViewFrame.origin.y, + originalWebViewFrame.origin.x, + originalWebViewFrame.size.height + statusBarHeight, + originalWebViewFrame.size.width - statusBarHeight); + break; + } + default: + NSLog(@"Unknown orientation: %d", orientation); + break; + } + + navBarHeight = 44.0f; + tabBarHeight = 49.0f; + // ----------------------------------------------------------------------- + } + return self; +} + +- (void)dealloc +{ + if (navBar) + [navBar release]; + + if (navBarController) + [navBarController release]; + + [super dealloc]; +} + +// NOTE: Returned object is owned +-(UIBarButtonItem*)backgroundButtonFromImage:(NSString*)imageName title:(NSString*)title fixedMarginLeft:(float)fixedMarginLeft fixedMarginRight:(float)fixedMarginRight target:(id)target action:(SEL)action +{ + UIButton *backButton = [[UIButton alloc] init]; + UIImage *imgNormal = [UIImage imageNamed:imageName]; + + // UIImage's resizableImageWithCapInsets method is only available from iOS 5.0. With earlier versions, the + // stretchableImageWithLeftCapWidth is used which behaves a bit differently. + if([imgNormal respondsToSelector:@selector(resizableImageWithCapInsets)]) + imgNormal = [imgNormal resizableImageWithCapInsets:UIEdgeInsetsMake(0, fixedMarginLeft, 0, fixedMarginRight)]; + else + imgNormal = [imgNormal stretchableImageWithLeftCapWidth:MAX(fixedMarginLeft, fixedMarginRight) topCapHeight:0]; + + [backButton setBackgroundImage:imgNormal forState:UIControlStateNormal]; + + backButton.titleLabel.textColor = [UIColor whiteColor]; + backButton.titleLabel.font = [UIFont boldSystemFontOfSize:12.0f]; + backButton.titleLabel.textAlignment = UITextAlignmentCenter; + + CGSize textSize = [title sizeWithFont:backButton.titleLabel.font]; + + float buttonWidth = MAX(imgNormal.size.width, textSize.width + fixedMarginLeft + fixedMarginRight);//imgNormal.size.width > (textSize.width + fixedMarginLeft + fixedMarginRight) + //? imgNormal.size.width : (textSize.width + fixedMarginLeft + fixedMarginRight); + backButton.frame = CGRectMake(0, 0, buttonWidth, imgNormal.size.height); + + CGFloat marginTopBottom = (backButton.frame.size.height - textSize.height) / 2; + [backButton setTitleEdgeInsets:UIEdgeInsetsMake(marginTopBottom, fixedMarginLeft, marginTopBottom, fixedMarginRight)]; + + [backButton setTitle:title forState:UIControlStateNormal]; + [backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [backButton.titleLabel setFont:[UIFont boldSystemFontOfSize:12.0f]]; + + UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; + [backButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; + + [backButton release]; + // imgNormal is autoreleased + + return backButtonItem; +} + +-(void)correctWebViewFrame +{ + if(!navBar) + return; + + const bool navBarShown = !navBar.hidden; + bool tabBarShown = false; + bool tabBarAtBottom = true; + + UIView *parent = [navBar superview]; + for(UIView *view in parent.subviews) + if([view isMemberOfClass:[UITabBar class]]) + { + tabBarShown = !view.hidden; + + // Tab bar height is customizable + if(tabBarShown) + { + tabBarHeight = view.bounds.size.height; + + // Since the navigation bar plugin plays together with the tab bar plugin, and the tab bar can as well + // be positioned at the top, here's some magic to find out where it's positioned: + tabBarAtBottom = true; + if([view respondsToSelector:@selector(tabBarAtBottom)]) + tabBarAtBottom = [view performSelector:@selector(tabBarAtBottom)]; + } + + break; + } + + // ----------------------------------------------------------------------------- + // IMPORTANT: Below code is the same in both the navigation and tab bar plugins! + // ----------------------------------------------------------------------------- + + CGFloat left = originalWebViewFrame.origin.x; + CGFloat right = left + originalWebViewFrame.size.width; + CGFloat top = originalWebViewFrame.origin.y; + CGFloat bottom = top + originalWebViewFrame.size.height; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + switch (orientation) + { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + // No need to change width/height from original frame + break; + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + right = left + originalWebViewFrame.size.height + 20.0f; + bottom = top + originalWebViewFrame.size.width - 20.0f; + break; + default: + NSLog(@"Unknown orientation: %d", orientation); + break; + } + + if(navBarShown) + top += navBarHeight; + + if(tabBarShown) + { + if(tabBarAtBottom) + bottom -= tabBarHeight; + else + top += tabBarHeight; + } + + CGRect webViewFrame = CGRectMake(left, top, right - left, bottom - top); + + [self.webView setFrame:webViewFrame]; + + // ----------------------------------------------------------------------------- + + // NOTE: Following part again for navigation bar plugin only + + if(navBarShown) + { + if(tabBarAtBottom) + [navBar setFrame:CGRectMake(left, originalWebViewFrame.origin.y, right - left, navBarHeight)]; + else + [navBar setFrame:CGRectMake(left, originalWebViewFrame.origin.y + tabBarHeight, right - left, navBarHeight)]; + } +} + +/*********************************************************************************/ + +-(void) create:(CDVInvokedUrlCommand*)command +{ + if(navBar) + return; + + navBarController = [[CDVNavigationBarController alloc] init]; + navBar = (UINavigationBar*)[navBarController view]; + + const NSDictionary *options = command ? [command.arguments objectAtIndex:0] : nil; + + if(options) + { + id style = [options objectForKey:@"style"]; + + if(style && style != [NSNull null]) + { + if([style isEqualToString:@"BlackTranslucent"]) + navBar.barStyle = UIBarStyleBlackTranslucent; + else if([style isEqualToString:@"BlackOpaque"]) + navBar.barStyle = UIBarStyleBlackOpaque; + else if([style isEqualToString:@"Black"]) + navBar.barStyle = UIBarStyleBlack; + // else the default will be used + } + + id tint = [options objectForKey:@"tintColorRgba"]; + + if(tint && tint != [NSNull null]) + { + NSArray *rgba = [tint componentsSeparatedByString:@","]; + navBar.tintColor = [UIColor colorWithRed:[[rgba objectAtIndex:0] intValue]/255.0f + green:[[rgba objectAtIndex:1] intValue]/255.0f + blue:[[rgba objectAtIndex:2] intValue]/255.0f + alpha:[[rgba objectAtIndex:3] intValue]/255.0f]; + } + } + + [navBarController setDelegate:self]; + + [[navBarController view] setFrame:CGRectMake(0, 0, originalWebViewFrame.size.width, navBarHeight)]; + [[[self webView] superview] addSubview:[navBarController view]]; + [navBar setHidden:YES]; +} + ++ (UIBarButtonSystemItem)getUIBarButtonSystemItemForString:(NSString*)imageName +{ + UIBarButtonSystemItem systemItem = -1; + + if([imageName isEqualToString:@"barButton:Action"]) systemItem = UIBarButtonSystemItemAction; + else if([imageName isEqualToString:@"barButton:Add"]) systemItem = UIBarButtonSystemItemAdd; + else if([imageName isEqualToString:@"barButton:Bookmarks"]) systemItem = UIBarButtonSystemItemBookmarks; + else if([imageName isEqualToString:@"barButton:Camera"]) systemItem = UIBarButtonSystemItemCamera; + else if([imageName isEqualToString:@"barButton:Cancel"]) systemItem = UIBarButtonSystemItemCancel; + else if([imageName isEqualToString:@"barButton:Compose"]) systemItem = UIBarButtonSystemItemCompose; + else if([imageName isEqualToString:@"barButton:Done"]) systemItem = UIBarButtonSystemItemDone; + else if([imageName isEqualToString:@"barButton:Edit"]) systemItem = UIBarButtonSystemItemEdit; + else if([imageName isEqualToString:@"barButton:FastForward"]) systemItem = UIBarButtonSystemItemFastForward; + else if([imageName isEqualToString:@"barButton:FixedSpace"]) systemItem = UIBarButtonSystemItemFixedSpace; + else if([imageName isEqualToString:@"barButton:FlexibleSpace"]) systemItem = UIBarButtonSystemItemFlexibleSpace; + else if([imageName isEqualToString:@"barButton:Organize"]) systemItem = UIBarButtonSystemItemOrganize; + else if([imageName isEqualToString:@"barButton:PageCurl"]) systemItem = UIBarButtonSystemItemPageCurl; + else if([imageName isEqualToString:@"barButton:Pause"]) systemItem = UIBarButtonSystemItemPause; + else if([imageName isEqualToString:@"barButton:Play"]) systemItem = UIBarButtonSystemItemPlay; + else if([imageName isEqualToString:@"barButton:Redo"]) systemItem = UIBarButtonSystemItemRedo; + else if([imageName isEqualToString:@"barButton:Refresh"]) systemItem = UIBarButtonSystemItemRefresh; + else if([imageName isEqualToString:@"barButton:Reply"]) systemItem = UIBarButtonSystemItemReply; + else if([imageName isEqualToString:@"barButton:Rewind"]) systemItem = UIBarButtonSystemItemRewind; + else if([imageName isEqualToString:@"barButton:Save"]) systemItem = UIBarButtonSystemItemSave; + else if([imageName isEqualToString:@"barButton:Search"]) systemItem = UIBarButtonSystemItemSearch; + else if([imageName isEqualToString:@"barButton:Stop"]) systemItem = UIBarButtonSystemItemStop; + else if([imageName isEqualToString:@"barButton:Trash"]) systemItem = UIBarButtonSystemItemTrash; + else if([imageName isEqualToString:@"barButton:Undo"]) systemItem = UIBarButtonSystemItemUndo; + + return systemItem; +} + +-(void) init:(CDVInvokedUrlCommand*)command +{ + // Dummy function, see initWithWebView +} + +// NOTE: Returned object is owned +- (UIBarButtonItem*)makeButtonWithOptions:(const NSDictionary*)options title:(NSString*)title imageName:(NSString*)imageName actionOnSelf:(SEL)actionOnSelf +{ + NSNumber *useImageAsBackgroundOpt = [NSNumber numberWithInt:1]; + float fixedMarginLeft = 13; + float fixedMarginRight = 13; + + if(options) + { + useImageAsBackgroundOpt = [options objectForKey:@"useImageAsBackground"]; + id fixedMarginLeftOpt = [options objectForKey:@"fixedMarginLeft"]; + id fixedMarginRightOpt = [options objectForKey:@"fixedMarginRight"]; + + if(fixedMarginLeftOpt && fixedMarginLeftOpt != [NSNull null]) + fixedMarginLeft = [fixedMarginLeftOpt floatValue]; + if(fixedMarginRightOpt && fixedMarginRightOpt != [NSNull null]) + fixedMarginRight = [fixedMarginRightOpt floatValue]; + } + + bool useImageAsBackground = useImageAsBackgroundOpt ? [useImageAsBackgroundOpt boolValue] : false; + + if((title && [title length] > 0) || useImageAsBackground) + { + if(useImageAsBackground && imageName && [imageName length] > 0) + { + return [self backgroundButtonFromImage:imageName title:title + fixedMarginLeft:fixedMarginLeft fixedMarginRight:fixedMarginRight + target:self action:actionOnSelf]; + } + else + { + return [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStylePlain target:self action:actionOnSelf]; + } + } + else if (imageName && [imageName length] > 0) + { + UIBarButtonSystemItem systemItem = [NavigationBar getUIBarButtonSystemItemForString:imageName]; + + if(systemItem != -1) + return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:systemItem target:self action:actionOnSelf]; + else + return [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:imageName] style:UIBarButtonItemStylePlain target:self action:actionOnSelf]; + } + else + { + // Fail silently + NSLog(@"Invalid setup{Left/Right}Button parameters\n"); + return nil; + } +} + +- (void)setupLeftButton:(CDVInvokedUrlCommand*)command +{ + NSString * title = [command.arguments objectAtIndex:0]; + NSString * imageName = [command.arguments objectAtIndex:1]; + const NSDictionary *options = [command.arguments objectAtIndex:2]; + + UIBarButtonItem *newButton = [self makeButtonWithOptions:options title:title imageName:imageName actionOnSelf:@selector(leftButtonTapped)]; + navBarController.navItem.leftBarButtonItem = newButton; + navBarController.leftButton = newButton; + [newButton release]; +} + +- (void)setupRightButton:(CDVInvokedUrlCommand*)command +{ + NSString * title = [command.arguments objectAtIndex:0]; + NSString * imageName = [command.arguments objectAtIndex:1]; + const NSDictionary *options = [command.arguments objectAtIndex:2]; + + UIBarButtonItem *newButton = [self makeButtonWithOptions:options title:title imageName:imageName actionOnSelf:@selector(rightButtonTapped)]; + navBarController.navItem.rightBarButtonItem = newButton; + navBarController.rightButton = newButton; + [newButton release]; +} + +- (void)hideLeftButton:(CDVInvokedUrlCommand*)command +{ + [[navBarController navItem] setLeftBarButtonItem:nil]; +} + +- (void)showLeftButton:(CDVInvokedUrlCommand*)command +{ + [[navBarController navItem] setLeftBarButtonItem:[navBarController leftButton]]; +} + +- (void)hideRightButton:(CDVInvokedUrlCommand*)command +{ + [[navBarController navItem] setRightBarButtonItem:nil]; +} + +- (void)showRightButton:(CDVInvokedUrlCommand*)command +{ + [[navBarController navItem] setRightBarButtonItem:[navBarController rightButton]]; +} + +-(void) leftButtonTapped +{ + NSString * jsCallBack = @"window.plugins.navigationBar.leftButtonTapped();"; + [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack]; +} + +-(void) rightButtonTapped +{ + NSString * jsCallBack = @"window.plugins.navigationBar.rightButtonTapped();"; + [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack]; +} + +-(void) show:(CDVInvokedUrlCommand*)command +{ + if (!navBar) + [self create:nil]; + + if ([navBar isHidden]) + { + [navBar setHidden:NO]; + [self correctWebViewFrame]; + } +} + + +-(void) hide:(CDVInvokedUrlCommand*)command +{ + if (navBar && ![navBar isHidden]) + { + [navBar setHidden:YES]; + [self correctWebViewFrame]; + } +} + +/** + * Resize the navigation bar (this should be called on orientation change) + * This is important in playing together with the tab bar plugin, especially because the tab bar can be placed on top + * or at the bottom, so the navigation bar bounds also need to be changed. + */ +- (void)resize:(CDVInvokedUrlCommand*)command +{ + [self correctWebViewFrame]; +} + +-(void) setTitle:(CDVInvokedUrlCommand*)command +{ + if(!navBar) + return; + + NSString *title = [command.arguments objectAtIndex:0]; + [navBarController navItem].title = title; + + // Reset otherwise overriding logo reference + [navBarController navItem].titleView = NULL; +} + +-(void) setLogo:(CDVInvokedUrlCommand*)command +{ + NSString *logoURL = [command.arguments objectAtIndex:0]; + UIImage *image = nil; + + if (logoURL && logoURL != @"") + { + if ([logoURL hasPrefix:@"http://"] || [logoURL hasPrefix:@"https://"]) + { + NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoURL]]; + image = [UIImage imageWithData:data]; + } + else + image = [UIImage imageNamed:logoURL]; + + if (image) + { + UIImageView * view = [[[UIImageView alloc] initWithImage:image] autorelease]; + [view setContentMode:UIViewContentModeScaleAspectFit]; + [view setBounds: CGRectMake(0, 0, 100, 30)]; + [[navBarController navItem] setTitleView:view]; + } + } +} + +@end diff --git a/iOS/NavigationBar/README.md b/iOS/NavigationBar/README.md new file mode 100644 index 0000000..2689996 --- /dev/null +++ b/iOS/NavigationBar/README.md @@ -0,0 +1,134 @@ +Navigation bar for Cordova on iOS +================================= + +This plugin lets you create and control a native navigation bar and its buttons. + +License +------- + +[MIT license](http://www.opensource.org/licenses/mit-license.html) + +Contributors +------------ + +This plugin was put together from the incomplete NativeControls plugin and other sources. See NavigationBar.m for the history. + +Installing the plugin +--------------------- + +- Copy *.xib, *.m and *.h files to your project's "Plugins" folder (should already exist and contain a README file if you used the Cordova project template) +- They have to be added to the project as well, so drag them from the "Plugins" folder (in Finder) to the same folder (in Xcode) and select to create references +- Open "Resources/Cordova.plist" and under "Plugins", add a key with the plugin name "NavigationBar" and a string value of "NavigationBar" (I guess it's the plugin's main class name) + +Note regarding orientation changes and the tab bar plugin +--------------------------------------------------------- + +If the tab bar plugin is used together with this plugin and the tab bar is positioned on top (defaults to bottom), it's necessary to resize the navigation bar automatically: + + window.addEventListener("resize", function() { + plugins.navigationBar.resize(); + ), false); + +Using the tab bar and navigation bar plugin together +---------------------------------------------------- + +In order to use the [tab bar plugin](https://github.com/AndiDog/phonegap-plugins/tree/master/iOS/TabBar) and [navigation bar plugin](https://github.com/AndiDog/phonegap-plugins/tree/master/iOS/NavigationBar) together, you must initialize both plugins before calling any of their methods, i.e. before creating a navigation/tab bar. For example right when your application starts: + + document.addEventListener("deviceready", function() { + console.log("Cordova ready") + + plugins.navigationBar.init() + plugins.tabBar.init() + + plugins.navigationBar.create() + plugins.tabBar.create() + + // ... + +This is because both plugins are aware of each other and resize Cordova's web view accordingly, but therefore they have to know the web view's initial dimensions. If for example you only initialize the tab bar plugin, create the tab bar and later decide to also create a navigation bar, the navigation bar plugin would think the original web view size is 320x411 instead of 320x460 (on iPhone). Layouting *could* be done using the screen size as well but it's currently implemented like this. + +Example +------- + +This example shows how to use the navigation bar: + + document.addEventListener("deviceready", function() { + console.log("Cordova ready") + + plugins.navigationBar.init() + + plugins.navigationBar.create() + // or to apply a certain style (one of "Black", "BlackOpaque", "BlackTranslucent", "Default"): + plugins.navigationBar.create("BlackOpaque") + // or with a yellow tint color (note: parameters might be changed to one object in a later version) + plugins.navigationBar.create('BlackOpaque', {tintColorRgba: '255,255,0,255'}) + + plugins.navigationBar.hideLeftButton() + plugins.navigationBar.hideRightButton() + + plugins.navigationBar.setTitle("My heading") + + plugins.navigationBar.showLeftButton() + plugins.navigationBar.showRightButton() + + // Create left navigation button with a title (you can either have a title or an image, not both!) + plugins.navigationBar.setupLeftButton("Text", null, function() { + alert("left nav button tapped") + }) + + // Create right navigation button from a system-predefined button (see the full list in NativeControls.m) + // or from an image + plugins.navigationBar.setupRightButton( + null, + "barButton:Bookmarks", // or your own file like "/www/stylesheets/images/ajax-loader.png", + function() { + alert("right nav button tapped") + } + ) + + plugins.navigationBar.show() + }, false) + +How to create a custom button (such as an arrow-shaped back button) +------------------------------------------------------------------- + +There are [several ways](http://stackoverflow.com/questions/227078/creating-a-left-arrow-button-like-uinavigationbars-back-style-on-a-uitoolba) to create a back button at runtime without having to use `UINavigationController`, but only one of them seems to be okay if you want your app to be approved: A custom button background image. + +![Screenshot](http://i.imgur.com/naC96.png) + +The above screenshot has a navigation bar with two such custom buttons. The left one actually has a background image very similar to the black iOS navigation bar. A stretchable picture (such as [this](http://imgur.com/yibWD) or [that one](http://imgur.com/K2LUS) which were used above) should be used because the plugin automatically sets the button size according to the text size (but not smaller than the original picture). You can define left/right margins which shall not be stretched if the button width changes. Important: iOS 5.0 supports defining two different values for the left/right margins. In earlier iOS versions, the plugins takes the larger value (13 pixels in the example below), so please test if your background image looks fine with older versions (install and use the iPhone 4.3 simulator, for example). + +Note: Vertical margins are supported by iOS but not implemented in the plugin – tell me if you would like that feature. I think you should keep navigation bar buttons at a fixed height (30px on normal 320x480 iPhone display). + +Put the button image in the "Resources" folder of your project. Here's some example code on how to use it: + + plugins.navigationBar.setupLeftButton( + "Baaack", + "blackbutton.png", + function() { + alert('leftnavbutton tapped') + }, + { + useImageAsBackground: true + fixedMarginLeft: 13 // 13 pixels on the left side are not stretched (the left-arrow shape) + fixedMarginRight: 5 // and 5 pixels on the right side (all room between these margins is used for the text label) + } + ) + + plugins.navigationBar.setupRightButton( + null, // with a custom background image, it's possible to set no title at all + "greenbutton.png", + function() { + alert('rightnavbutton tapped') + }, + { + useImageAsBackground: true + fixedMarginLeft: 5 + fixedMarginRight: 13 + } + ) + +Reporting issues or requests for improvement +-------------------------------------------- + +Please report problems on [my GitHub fork of phonegap-plugins](https://github.com/AndiDog/phonegap-plugins). \ No newline at end of file diff --git a/iOS/NavigationBar/example.png b/iOS/NavigationBar/example.png new file mode 100644 index 0000000..aa25acf Binary files /dev/null and b/iOS/NavigationBar/example.png differ diff --git a/iOS/PDFViewer/.gitignore b/iOS/PDFViewer/.gitignore new file mode 100644 index 0000000..e0ac686 --- /dev/null +++ b/iOS/PDFViewer/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +.*.sw? +*.cso +tmp +*.mode1v3 +*.pbxuser +build +*.xcworkspace +xcuserdata +CordovaLib/javascript/cordova-*.js +CordovaLib/CordovaLibApp/www/cordova.ios.js +console.log \ No newline at end of file diff --git a/iOS/PDFViewer/Cordova.plist.png b/iOS/PDFViewer/Cordova.plist.png new file mode 100644 index 0000000..8b7f557 Binary files /dev/null and b/iOS/PDFViewer/Cordova.plist.png differ diff --git a/iOS/PDFViewer/CordovaBot.pdf b/iOS/PDFViewer/CordovaBot.pdf new file mode 100644 index 0000000..aa8a50c Binary files /dev/null and b/iOS/PDFViewer/CordovaBot.pdf differ diff --git a/iOS/PDFViewer/PDFViewer.js b/iOS/PDFViewer/PDFViewer.js new file mode 100644 index 0000000..5a8a8a0 --- /dev/null +++ b/iOS/PDFViewer/PDFViewer.js @@ -0,0 +1,68 @@ +//PDFViewer based on ChildBrowser + +/* MIT licensed */ +// (c) 2010 Jesse MacFadyen, Nitobi + + +(function() { + + var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + + function PDFViewer() { + // Does nothing + } + + + // Callback when the user chooses the 'Done' button + // called from native + PDFViewer._onClose = function() + { + window.plugins.PDFViewer.onClose(); + }; + + + +/* The interface that you will use to access functionality */ + + // Show a webpage, will result in a callback to onLocationChange + PDFViewer.prototype.showPDF = function(loc) + { + cordovaRef.exec("PDFViewerCommand.showPDF", loc); + }; + + // close the browser, will NOT result in close callback + PDFViewer.prototype.close = function() + { + cordovaRef.exec("PDFViewerCommand.close"); + }; + + // Not Implemented + PDFViewer.prototype.jsExec = function(jsString) + { + // Not Implemented!! + //PhoneGap.exec("PDFViewerCommand.jsExec",jsString); + }; + + // Note: this plugin does NOT install itself, call this method some time after deviceready to install it + // it will be returned, and also available globally from window.plugins.PDFViewer + PDFViewer.install = function() + { + if(!window.plugins) { + window.plugins = {}; + } + if ( ! window.plugins.PDFViewer ) { + window.plugins.PDFViewer = new PDFViewer(); + } + + }; + + + if (cordovaRef && cordovaRef.addConstructor) { + cordovaRef.addConstructor(PDFViewer.install); + } else { + console.log("PDFViewer Cordova Plugin could not be installed."); + return null; + } + + + })(); \ No newline at end of file diff --git a/iOS/PDFViewer/PDFViewerCommand.h b/iOS/PDFViewer/PDFViewerCommand.h new file mode 100644 index 0000000..b65db15 --- /dev/null +++ b/iOS/PDFViewer/PDFViewerCommand.h @@ -0,0 +1,16 @@ +// PDFViewer based on ChildBrowser + +// Created by Jesse MacFadyen on 10-05-29. +// Copyright 2010 Nitobi. All rights reserved. +// Copyright 2012, Randy McMillan + +#import +#import "PDFViewerViewController.h" + +@interface PDFViewerCommand : CDVPlugin {} + +@property (nonatomic, strong) PDFViewerViewController *pdfViewer; + +- (void)showPDF:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options; + +@end diff --git a/iOS/PDFViewer/PDFViewerCommand.m b/iOS/PDFViewer/PDFViewerCommand.m new file mode 100644 index 0000000..2143ac5 --- /dev/null +++ b/iOS/PDFViewer/PDFViewerCommand.m @@ -0,0 +1,54 @@ +// PDFViewer based on ChildBrowser + +// Created by Jesse MacFadyen on 10-05-29. +// Copyright 2010 Nitobi. All rights reserved. +// Copyright 2012, Randy McMillan + +#import "PDFViewerCommand.h" +#import + +@implementation PDFViewerCommand + +@synthesize pdfViewer; + +- (void)showPDF:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options // args: url +{ + if (self.pdfViewer == nil) { +#if __has_feature(objc_arc) + self.pdfViewer = [[PDFViewerViewController alloc] initWithScale:NO]; +#else + self.pdfViewer = [[[PDFViewerViewController alloc] initWithScale:NO] autorelease]; +#endif + self.pdfViewer.delegate = self; + self.pdfViewer.orientationDelegate = self.viewController; + } + + pdfViewer.modalPresentationStyle = UIModalPresentationPageSheet; + [self.viewController presentModalViewController:pdfViewer animated:YES]; + + NSString *pdfName = (NSString *)[arguments objectAtIndex:0]; + + [self.pdfViewer loadPDF:pdfName]; // @"YingYang.pdf"]; +} + +- (void)close:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options // args: url +{ + [self.pdfViewer closeViewer]; +} + +- (void)onClose +{ + [self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.PDFViewer.onClose();"]; +} + +#if !__has_feature(objc_arc) + - (void)dealloc + { + self.pdfViewer = nil; + + [super dealloc]; + } + +#endif + +@end diff --git a/iOS/PDFViewer/PDFViewerViewController.h b/iOS/PDFViewer/PDFViewerViewController.h new file mode 100644 index 0000000..7e62762 --- /dev/null +++ b/iOS/PDFViewer/PDFViewerViewController.h @@ -0,0 +1,43 @@ +// PDFViewer based on ChildBrowser + +// Created by Jesse MacFadyen on 10-05-29. +// Copyright 2010 Nitobi. All rights reserved. +// Copyright 2012, Randy McMillan + +#import + +@protocol PDFViewerDelegate + +- (void)onClose; + +@end + +@protocol CDVOrientationDelegate + +- (NSUInteger)supportedInterfaceOrientations; +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +- (BOOL)shouldAutorotate; + +@end + +@class PDFView; + +@interface PDFViewerViewController : UIViewController {} + +@property (nonatomic, strong) IBOutlet UIButton *closeBtn; + +@property (nonatomic, strong) IBOutlet PDFView *pdfView; +@property (nonatomic, strong) IBOutlet UIScrollView *scrollView; +@property (nonatomic, strong) IBOutlet UIImageView *imageView; +@property (nonatomic, strong) IBOutlet UIToolbar *toolBar; + +// unsafe_unretained is equivalent to assign - used to prevent retain cycles in the two properties below +@property (nonatomic, unsafe_unretained) id delegate; +@property (nonatomic, unsafe_unretained) id orientationDelegate; + +- (PDFViewerViewController *)initWithScale:(BOOL)enabled; +- (IBAction)onDoneButtonPress:(id)sender; +- (void)loadPDF:(NSString *)pdfName; +- (void)closeViewer; + +@end diff --git a/iOS/PDFViewer/PDFViewerViewController.m b/iOS/PDFViewer/PDFViewerViewController.m new file mode 100644 index 0000000..090af0a --- /dev/null +++ b/iOS/PDFViewer/PDFViewerViewController.m @@ -0,0 +1,182 @@ +// PDFViewer based on ChildBrowser + +// / Created by Jesse MacFadyen on 10-05-29. +// Copyright 2010 Nitobi. All rights reserved. +// Copyright 2012, Randy McMillan + +#import "PDFViewerViewController.h" +#import "UIImage+PDF.h" + +@implementation PDFViewerViewController + +@synthesize delegate, orientationDelegate; +@synthesize closeBtn; + +@synthesize pdfView; +@synthesize scrollView; +@synthesize imageView; +@synthesize toolBar; + +/* + * // The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. + * - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + * if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + * // Custom initialization + * } + * return self; + * } + */ + ++ (NSString *)resolveImageResource:(NSString *)resource +{ + NSString *systemVersion = [[UIDevice currentDevice] systemVersion]; + BOOL isLessThaniOS4 = ([systemVersion compare:@"4.0" options:NSNumericSearch] == NSOrderedAscending); + + if (isLessThaniOS4) { + return [NSString stringWithFormat:@"%@.png", resource]; + } else { + if (([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES) && ([[UIScreen mainScreen] scale] == 2.00)) { + return [NSString stringWithFormat:@"%@@2x.png", resource]; + } + } + + return resource;// if all else fails +} + +- (PDFViewerViewController *)initWithScale:(BOOL)enabled +{ + self = [super init]; + return self; +} + +// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. +- (void)viewDidLoad +{ + [super viewDidLoad]; + + // [self loadPDF:@"YingYang.pdf"]; + NSLog(@"View did load"); +} + +- (void)loadPDF:(NSString *)pdfName +{ + NSLog(@"Opening Url : %@", pdfName); + + // NSString* url = (NSString*)[arguments objectAtIndex:0]; + + UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage originalSizeImageWithPDFNamed:pdfName]]; + // imageView.center = self.view.center; + + self.imageView.image = tempImageView.image; + + // [ self.pdfView addSubview:imageView ]; + +#define VIEWBOUNDS [[UIScreen mainScreen] bounds] + + // [scrollView setBounds:self.view.superview.frame];//VIEWBOUNDS]; + // [imageView setBounds:self.view.superview.frame];//VIEWBOUNDS]; + // [pdfView setBounds:self.view.superview.frame];//VIEWBOUNDS]; + + // [scrollView setBounds:CGRectMake(scrollView.superview.center.x, scrollView.superview.center.y, 100, 100)]; + + scrollView.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth); + + imageView.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth); + + // imageView.center = self.view.superview.center; + + [tempImageView release]; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +- (void)viewDidUnload +{ + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; + NSLog(@"View did UN-load"); + pdfView = nil; +} + +- (void)dealloc +{ + self.delegate = nil; + self.orientationDelegate = nil; + +#if !__has_feature(objc_arc) + [super dealloc]; +#endif +} + +- (void)closeBrowser +{ + if (self.delegate != nil) { + [self.delegate onClose]; + } + + if ([self respondsToSelector:@selector(presentingViewController)]) { + // Reference UIViewController.h Line:179 for update to iOS 5 difference - @RandyMcMillan + [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; + } else { + [[self parentViewController] dismissModalViewControllerAnimated:YES]; + } +} + +- (IBAction)onDoneButtonPress:(id)sender +{ + [self closeBrowser]; +} + +/*- (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error { + * NSLog (@"webView:didFailLoadWithError"); + * [spinner stopAnimating]; + * addressLabel.text = @"Failed"; + * if (error != NULL) { + * UIAlertView *errorAlert = [[UIAlertView alloc] + * initWithTitle: [error localizedDescription] + * message: [error localizedFailureReason] + * delegate:nil + * cancelButtonTitle:@"OK" + * otherButtonTitles:nil]; + * [errorAlert show]; + * [errorAlert release]; + * } + * } + */ + +#pragma mark CDVOrientationDelegate + +- (BOOL)shouldAutorotate +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { + return [self.orientationDelegate shouldAutorotate]; + } + + return YES; +} + +- (NSUInteger)supportedInterfaceOrientations +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { + return [self.orientationDelegate supportedInterfaceOrientations]; + } + + return UIInterfaceOrientationMaskPortrait; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { + return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + + return YES; +} + +@end diff --git a/iOS/PDFViewer/PDFViewerViewController.xib b/iOS/PDFViewer/PDFViewerViewController.xib new file mode 100644 index 0000000..48db22c --- /dev/null +++ b/iOS/PDFViewer/PDFViewerViewController.xib @@ -0,0 +1,517 @@ + + + + 768 + 12C60 + 2843 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1929 + + + YES + IBProxyObject + IBUIBarButtonItem + IBUIImageView + IBUIScrollView + IBUIToolbar + IBUIView + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 319 + + YES + + + 319 + + YES + + + 319 + + YES + + + 319 + {500, 500} + + + + _NS:9 + NO + IBCocoaTouchFramework + + + {500, 500} + + + + _NS:9 + + 3 + MQA + + 2 + + + IBCocoaTouchFramework + + + {500, 500} + + + + _NS:9 + YES + IBCocoaTouchFramework + YES + YES + NO + NO + + + + 266 + {{0, 456}, {500, 44}} + + + + NO + NO + IBCocoaTouchFramework + 1 + + YES + + IBCocoaTouchFramework + + 5 + + + IBCocoaTouchFramework + + 5 + + + IBCocoaTouchFramework + 1 + + 0 + + + IBCocoaTouchFramework + + 5 + + + IBCocoaTouchFramework + + 5 + + + + + {500, 500} + + + + + 3 + MC41AA + + + + 3 + 3 + + IBCocoaTouchFramework + + + + + YES + + + view + + + + 35 + + + + scrollView + + + + 47 + + + + pdfView + + + + 48 + + + + imageView + + + + 50 + + + + onDoneButtonPress: + + + + 26 + + + + + YES + + 0 + + YES + + + + + + 1 + + + YES + + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + YES + + + + + + + + + + 7 + + + + + 14 + + + + + 15 + + + + + 37 + + + + + 39 + + + + + 46 + + + YES + + + + + + 44 + + + YES + + + + + + 49 + + + + + + + YES + + YES + -1.CustomClassName + -1.IBPluginDependency + -2.CustomClassName + -2.IBPluginDependency + 1.IBPluginDependency + 14.IBPluginDependency + 15.IBPluginDependency + 37.IBPluginDependency + 39.IBPluginDependency + 44.CustomClassName + 44.IBPluginDependency + 46.IBPluginDependency + 49.IBPluginDependency + 6.IBPluginDependency + 7.IBPluginDependency + + + YES + PDFViewerViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + PDFView + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + + + + YES + + + + + 51 + + + + YES + + PDFView + UIView + + IBProjectSource + ./Classes/PDFView.h + + + + PDFViewerViewController + UIViewController + + YES + + YES + onDoneButtonPress: + onSafariButtonPress: + + + YES + id + id + + + + YES + + YES + onDoneButtonPress: + onSafariButtonPress: + + + YES + + onDoneButtonPress: + id + + + onSafariButtonPress: + id + + + + + YES + + YES + addressLabel + backBtn + closeBtn + fwdBtn + imageView + pdfView + refreshBtn + safariBtn + scrollView + spinner + toolBar + webView + + + YES + UILabel + UIBarButtonItem + UIBarButtonItem + UIBarButtonItem + UIImageView + PDFView + UIBarButtonItem + UIBarButtonItem + UIScrollView + UIActivityIndicatorView + UIToolbar + UIWebView + + + + YES + + YES + addressLabel + backBtn + closeBtn + fwdBtn + imageView + pdfView + refreshBtn + safariBtn + scrollView + spinner + toolBar + webView + + + YES + + addressLabel + UILabel + + + backBtn + UIBarButtonItem + + + closeBtn + UIBarButtonItem + + + fwdBtn + UIBarButtonItem + + + imageView + UIImageView + + + pdfView + PDFView + + + refreshBtn + UIBarButtonItem + + + safariBtn + UIBarButtonItem + + + scrollView + UIScrollView + + + spinner + UIActivityIndicatorView + + + toolBar + UIToolbar + + + webView + UIWebView + + + + + IBProjectSource + ./Classes/PDFViewerViewController.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + 3 + 1929 + + diff --git a/iOS/PDFViewer/README.md b/iOS/PDFViewer/README.md new file mode 100644 index 0000000..d5a95ee --- /dev/null +++ b/iOS/PDFViewer/README.md @@ -0,0 +1,28 @@ +##PDFViewer for Cordova iOS + + +![image](https://raw.github.com/RandyMcMillan/Cordova--iOS--PDFViewer-/master/pdfview.png) + +Usage is like ChildBrowser except you send a bundled pdf file name instead of an external url + + + + + + + +Add PDFViewerCommand | PDFViewerCommand to Cordova.plist + +![image](https://raw.github.com/RandyMcMillan/Cordova--iOS--PDFViewer-/master/Cordova.plist.png) + +This code is completely dependent on the Apache Cordova (formerly PhoneGap) project, hosted on [GitHub](http://github.com/apache) + +The MIT License + +Copyright (c) 2012 Randy McMillan + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/iOS/PDFViewer/UIImage+PDF/NSString+MD5.h b/iOS/PDFViewer/UIImage+PDF/NSString+MD5.h new file mode 100644 index 0000000..ce18dbe --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/NSString+MD5.h @@ -0,0 +1,11 @@ +// +// Created by Nigel Timothy Barber (@mindbrix) on 13/04/2012. +// + +#import "NSString+MD5.h" + +@interface NSString(MD5) + +- (NSString *)MD5; + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/NSString+MD5.m b/iOS/PDFViewer/UIImage+PDF/NSString+MD5.m new file mode 100644 index 0000000..bbcc266 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/NSString+MD5.m @@ -0,0 +1,27 @@ +// +// Created by Nigel Timothy Barber (@mindbrix) on 13/04/2012. +// + +#import + +/* From: https://gist.github.com/1209911 + */ + +@implementation NSString(MD5) + +- (NSString*)MD5 +{ + const char *cStr = [self UTF8String]; + unsigned char result[16]; + CC_MD5( cStr, strlen(cStr), result ); // This is the md5 call + return [NSString stringWithFormat: + @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], + result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], + result[12], result[13], result[14], result[15] + ]; +} + + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/PDFView.h b/iOS/PDFViewer/UIImage+PDF/PDFView.h new file mode 100644 index 0000000..b80c187 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/PDFView.h @@ -0,0 +1,26 @@ +// +// PDFView.h +// +// Created by Nigel Barber on 15/10/2011. +// Copyright 2011 Mindbrix Limited. All rights reserved. +// + +#import + + +@interface PDFView : UIView +{ + +} + +@property( nonatomic, assign ) int page; +@property( nonatomic, assign ) NSString *resourceName; +@property( nonatomic, assign ) NSURL *resourceURL; + ++(CGRect) mediaRect:(NSString *)resourceName; ++(CGRect) mediaRectForURL:(NSURL *)resourceURL; ++(CGRect) mediaRectForURL:(NSURL *)resourceURL atPage:(int)page; ++(int) pageCountForURL:(NSURL *)resourceURL; ++(NSURL *)resourceURLForName:(NSString *)resourceName; + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/PDFView.m b/iOS/PDFViewer/UIImage+PDF/PDFView.m new file mode 100644 index 0000000..9b16c60 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/PDFView.m @@ -0,0 +1,140 @@ +// +// PDFView.m +// +// Created by Nigel Barber on 15/10/2011. +// Copyright 2011 Mindbrix Limited. All rights reserved. +// + +#import "PDFView.h" + + +@implementation PDFView + +@synthesize page = m_page; +@synthesize resourceName = m_resourceName; +@synthesize resourceURL = m_resourceURL; + + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) + { + // Initialization code. + m_page = 1; + } + return self; +} + + +-(void)setResourceName:(NSString *)resourceName +{ + m_resourceName = resourceName; + + self.resourceURL = [ PDFView resourceURLForName: self.resourceName ]; +} + + +-(void)setResourceURL:(NSURL *)resourceURL +{ + m_resourceURL = resourceURL; + + [ self setNeedsDisplay ]; +} + + ++(CGRect) mediaRect:(NSString *)resourceName +{ + return [ PDFView mediaRectForURL:[ PDFView resourceURLForName: resourceName ]]; +} + + ++(CGRect) mediaRectForURL:(NSURL *)resourceURL +{ + return [ self mediaRectForURL:resourceURL atPage:1 ]; +} + + ++(CGRect) mediaRectForURL:(NSURL *)resourceURL atPage:(int)page +{ + CGRect rect = CGRectNull; + + if( resourceURL ) + { + CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL( (__bridge CFURLRef) resourceURL ); + CGPDFPageRef page1 = CGPDFDocumentGetPage( pdf, page ); + + rect = CGPDFPageGetBoxRect( page1, kCGPDFCropBox ); + + CGPDFDocumentRelease( pdf ); + } + + return rect; +} + + ++(int) pageCountForURL:(NSURL *)resourceURL +{ + int pageCount = 1; + + if( resourceURL ) + { + CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL( (__bridge CFURLRef) resourceURL ); + + pageCount = CGPDFDocumentGetNumberOfPages( pdf ); + + CGPDFDocumentRelease( pdf ); + } + + return pageCount; +} + + ++(NSURL *)resourceURLForName:(NSString *)resourceName +{ + return ( resourceName ) ? [ NSURL fileURLWithPath:[[ NSBundle mainBundle ] pathForResource:resourceName ofType:nil ]] : nil; +} + + + +/**/ +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect +{ + // Drawing code. + if( self.resourceURL ) + { + /* + * Reference: http://www.cocoanetics.com/2010/06/rendering-pdf-is-easier-than-you-thought/ + */ + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + [ self.backgroundColor set ]; + CGContextFillRect( ctx, rect ); + + CGContextGetCTM( ctx ); + CGContextScaleCTM( ctx, 1, -1 ); + CGContextTranslateCTM( ctx, 0, -self.bounds.size.height ); + + CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL( (__bridge CFURLRef) self.resourceURL ); + CGPDFPageRef page1 = CGPDFDocumentGetPage( pdf, self.page ); + + CGRect mediaRect = CGPDFPageGetBoxRect( page1, kCGPDFCropBox ); + CGContextScaleCTM( ctx, rect.size.width / mediaRect.size.width, rect.size.height / mediaRect.size.height ); + CGContextTranslateCTM( ctx, -mediaRect.origin.x, -mediaRect.origin.y ); + + CGContextDrawPDFPage( ctx, page1 ); + CGPDFDocumentRelease( pdf ); + } +} + + +- (void)dealloc +{ + //[super dealloc]; +} + + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/README.md b/iOS/PDFViewer/UIImage+PDF/README.md new file mode 100644 index 0000000..ea38e9e --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/README.md @@ -0,0 +1,54 @@ +UIImage+PDF +=========== + +`UIImage+PDF` provides a `UIImage` class category method to render a `UIImage` from any PDF stored in the application bundle. The motivation for this was to enable the easy use of scaleable vector assets in `iOS` apps. + + +Usage +----- + +Add the sources files in the `UIImage+PDF` sub folder to your project. Use the following line to import the necessary methods. + + #import "UIImage+PDF.h" + +Then simply call one of the `UIImage` class methods as shown here: + + UIImage *img = [ UIImage imageWithPDFNamed:@"YingYang.pdf" atSize:CGSizeMake( 40, 40 ) ]; + UIImage *img = [ UIImage imageWithPDFNamed:@"YingYang.pdf" atWidth:60 ]; + UIImage *img = [ UIImage imageWithPDFNamed:@"YingYang.pdf" atHeight:90 ]; + UIImage *img = [ UIImage originalSizeImageWithPDFNamed:@"YingYang.pdf" ]; + +The `atWidth:` and `atHeight:` methods are particularly useful as they preserve the aspect ratio of the source PDF. + +An example project is included in this repository. The important code is in `viewDidLoad:` in `UIImage_PDF_exampleViewController.m`. + + +Cacheing layer +-------------- +`UIImage+PDF` now transparently caches all rendered PDFs in `/Library/Caches/__PDF_CACHE__`. This substantially improves application latency with large PDFs, especially on the new iPad. To disable cacheing, comment out `#define UIIMAGE_PDF_CACHEING 1` in `UIImage+PDF.h`. + + +PDF file size +------------- + +By default Adobe Illustrator saves exported PDFs very inefficiently. For best results, export in EPS format, load this into Preview and then save as a PDF. The included example file `YingYang.pdf` shrunk to 10% of its original size with this workflow. + +Other vector graphics editors which natively use the OSX Quartz renderer, such as Sketch, will create much more compact PDFs. + +The amazing devs at [Panic](http://www.panic.com/) have now released a PDF shrinking utility, [ShrinkIt](http://www.panic.com/blog/2010/02/shrinkit-1-0/), which should take a lot of the pain out of a vector asset workflow. + + +Licence +------- + +This licence is derived from one used by [Matt Gallagher](http://projectswithlove.com/about.html) + +Copyright 2012 Nigel Timothy Barber - [@mindbrix](http://twitter.com/mindbrix). All rights reserved. + +Permission is given to use this source code file without charge in any project, commercial or otherwise, entirely at your risk, with the condition that any redistribution (in part or whole) of source code must retain this copyright and permission notice. Attribution in compiled projects is appreciated but not required. + + +Further Reading +--------------- + +[Matt Gemmell](http://twitter.com/mattgemmell) has an excellent article on his blog explaining [how to use PDF images in iOS apps](http://mattgemmell.com/2012/02/10/using-pdf-images-in-ios-apps/). diff --git a/iOS/PDFViewer/UIImage+PDF/UIImage+PDF.h b/iOS/PDFViewer/UIImage+PDF/UIImage+PDF.h new file mode 100644 index 0000000..d6fe045 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/UIImage+PDF.h @@ -0,0 +1,51 @@ +// +// UIImage+PDF.h +// +// Created by Nigel Barber on 15/10/2011. +// Copyright 2011 Mindbrix Limited. All rights reserved. +// + +#import +#import "UIView+Image.h" +#import "PDFView.h" +#import "NSString+MD5.h" + + +#define UIIMAGE_PDF_CACHEING 1 + + +@interface UIImage( PDF ) + ++(UIImage *) imageOrPDFNamed:(NSString *)resourceName; ++(UIImage *) imageOrPDFWithContentsOfFile:(NSString *)path; + + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atSize:(CGSize)size atPage:(int)page; ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atSize:(CGSize)size; + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atWidth:(CGFloat)width atPage:(int)page; ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atWidth:(CGFloat)width; + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atHeight:(CGFloat)height atPage:(int)page; ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atHeight:(CGFloat)height; + ++(UIImage *) originalSizeImageWithPDFNamed:(NSString *)resourceName atPage:(int)page; ++(UIImage *) originalSizeImageWithPDFNamed:(NSString *)resourceName; + + + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atSize:(CGSize)size atPage:(int)page; ++(UIImage *) imageWithPDFURL:(NSURL *)URL atSize:(CGSize)size; + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atWidth:(CGFloat)width atPage:(int)page; ++(UIImage *) imageWithPDFURL:(NSURL *)URL atWidth:(CGFloat)width; + + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atHeight:(CGFloat)height atPage:(int)page; ++(UIImage *) imageWithPDFURL:(NSURL *)URL atHeight:(CGFloat)height; + ++(UIImage *) originalSizeImageWithPDFURL:(NSURL *)URL atPage:(int)page; ++(UIImage *) originalSizeImageWithPDFURL:(NSURL *)URL; + + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/UIImage+PDF.m b/iOS/PDFViewer/UIImage+PDF/UIImage+PDF.m new file mode 100644 index 0000000..d961f22 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/UIImage+PDF.m @@ -0,0 +1,230 @@ +// +// UIImage+PDF.m +// +// Created by Nigel Barber on 15/10/2011. +// Copyright 2011 Mindbrix Limited. All rights reserved. +// + +#import "UIImage+PDF.h" + + +@implementation UIImage( PDF ) + + +#pragma mark - Convenience methods + ++(UIImage *) imageOrPDFNamed:(NSString *)resourceName +{ + if([[ resourceName pathExtension ] isEqualToString: @"pdf" ]) + { + return [ UIImage originalSizeImageWithPDFNamed:resourceName ]; + } + else + { + return [ UIImage imageNamed:resourceName ]; + } +} + + ++(UIImage *) imageOrPDFWithContentsOfFile:(NSString *)path +{ + if([[ path pathExtension ] isEqualToString: @"pdf" ]) + { + return [ UIImage originalSizeImageWithPDFURL:[ NSURL fileURLWithPath:path ]]; + } + else + { + return [ UIImage imageWithContentsOfFile:path ]; + } +} + + +#pragma mark - Cacheing + ++(NSString *)cacheFilenameForURL:(NSURL *)resourceURL atSize:(CGSize)size atScaleFactor:(CGFloat)scaleFactor atPage:(int)page +{ + NSString *cacheFilename = nil; + +#ifdef UIIMAGE_PDF_CACHEING + + NSFileManager *fileManager = [ NSFileManager defaultManager ]; + + NSString *filePath = [ resourceURL path ]; + + //NSLog( @"filePath: %@", filePath ); + + NSDictionary *fileAttributes = [ fileManager attributesOfItemAtPath:filePath error:NULL ]; + + //NSLog( @"fileAttributes: %@", fileAttributes ); + + NSString *cacheRoot = [ NSString stringWithFormat:@"%@ - %@ - %@ - %@ - %d", [ filePath lastPathComponent ], [ fileAttributes objectForKey:NSFileSize ], [ fileAttributes objectForKey:NSFileModificationDate ], NSStringFromCGSize(CGSizeMake( size.width * scaleFactor, size.height * scaleFactor )), page ]; + + //NSLog( @"cacheRoot: %@", cacheRoot ); + + NSString *MD5 = [ cacheRoot MD5 ]; + + //NSLog( @"MD5: %@", MD5 ); + + NSString *cachesDirectory = [ NSSearchPathForDirectoriesInDomains( NSCachesDirectory, NSUserDomainMask, YES ) objectAtIndex:0 ]; + + NSString *cacheDirectory = [ NSString stringWithFormat:@"%@/__PDF_CACHE__", cachesDirectory ]; + + //NSLog( @"cacheDirectory: %@", cacheDirectory ); + + [ fileManager createDirectoryAtPath:cacheDirectory withIntermediateDirectories:YES attributes:nil error:NULL ]; + + cacheFilename = [ NSString stringWithFormat:@"%@/%@.png", cacheDirectory, MD5 ]; + + //NSLog( @"cacheFilename: %@", cacheFilename ); + +#endif + + return cacheFilename; +} + + + +#pragma mark - Resource name + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atSize:(CGSize)size atPage:(int)page +{ + return [ self imageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atSize:size atPage:page ]; +} + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atSize:(CGSize)size +{ + return [ self imageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atSize:size ]; +} + + + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atWidth:(CGFloat)width atPage:(int)page +{ + return [ self imageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atWidth:width atPage:page ]; +} + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atWidth:(CGFloat)width +{ + return [ self imageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atWidth:width ]; +} + + + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atHeight:(CGFloat)height atPage:(int)page +{ + return [ self imageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atHeight:height atPage:page ]; +} + ++(UIImage *) imageWithPDFNamed:(NSString *)resourceName atHeight:(CGFloat)height +{ + return [ self imageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atHeight:height ]; +} + + + ++(UIImage *) originalSizeImageWithPDFNamed:(NSString *)resourceName atPage:(int)page +{ + return [ self originalSizeImageWithPDFURL:[ PDFView resourceURLForName:resourceName ] atPage:page ]; +} + ++(UIImage *) originalSizeImageWithPDFNamed:(NSString *)resourceName +{ + return [ self originalSizeImageWithPDFURL:[ PDFView resourceURLForName:resourceName ]]; +} + + + +#pragma mark - Resource URLs + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atSize:(CGSize)size atPage:(int)page +{ + UIImage *pdfImage = nil; + + PDFView *pdfView = [[ PDFView alloc ] initWithFrame:CGRectMake( 0, 0, size.width, size.height ) ]; + + NSString *cacheFilename = [ self cacheFilenameForURL:URL atSize:size atScaleFactor:pdfView.contentScaleFactor atPage:page ]; + + if([[ NSFileManager defaultManager ] fileExistsAtPath:cacheFilename ]) + { + //NSLog( @"Cache hit" ); + + pdfImage = [ UIImage imageWithCGImage:[[ UIImage imageWithContentsOfFile:cacheFilename ] CGImage ] scale:pdfView.contentScaleFactor orientation:UIImageOrientationUp ]; + } + else + { + //NSLog( @"Cache miss" ); + + pdfView.backgroundColor = [ UIColor clearColor ]; + pdfView.page = page; + pdfView.resourceURL = URL; + + pdfImage = [ pdfView image ]; + + if( cacheFilename ) + { + [ UIImagePNGRepresentation( pdfImage ) writeToFile:cacheFilename atomically:NO ]; + } + } + + // [ pdfView release ]; + + return pdfImage; +} + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atSize:(CGSize)size +{ + return [ self imageWithPDFURL:URL atSize:size atPage:1 ]; +} + + + + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atWidth:(CGFloat)width atPage:(int)page +{ + CGRect mediaRect = [ PDFView mediaRectForURL:URL ]; + CGFloat aspectRatio = mediaRect.size.width / mediaRect.size.height; + + CGSize size = CGSizeMake( width, ceilf( width / aspectRatio )); + + return [ UIImage imageWithPDFURL:URL atSize:size atPage:page ]; +} + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atWidth:(CGFloat)width +{ + return [ UIImage imageWithPDFURL:URL atWidth:width atPage:1 ]; +} + + + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atHeight:(CGFloat)height atPage:(int)page +{ + CGRect mediaRect = [ PDFView mediaRectForURL:URL ]; + CGFloat aspectRatio = mediaRect.size.width / mediaRect.size.height; + + CGSize size = CGSizeMake( ceilf( height * aspectRatio ), height ); + + return [ UIImage imageWithPDFURL:URL atSize:size atPage:page ]; +} + ++(UIImage *) imageWithPDFURL:(NSURL *)URL atHeight:(CGFloat)height +{ + return [ UIImage imageWithPDFURL:URL atHeight:height atPage:1 ]; +} + + + ++(UIImage *) originalSizeImageWithPDFURL:(NSURL *)URL atPage:(int)page +{ + CGRect mediaRect = [ PDFView mediaRectForURL:URL ]; + + return [ UIImage imageWithPDFURL:URL atSize:mediaRect.size atPage:page ]; +} + ++(UIImage *) originalSizeImageWithPDFURL:(NSURL *)URL +{ + return [ UIImage originalSizeImageWithPDFURL:URL atPage:1 ]; +} + + + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/UIView+Image.h b/iOS/PDFViewer/UIImage+PDF/UIView+Image.h new file mode 100644 index 0000000..be1f194 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/UIView+Image.h @@ -0,0 +1,17 @@ +// +// UIView+Image.h +// +// Created by Nigel Barber on 21/07/2011. +// Copyright 2011 Mindbrix Limited. All rights reserved. +// + +#import +#import + +@interface UIView( Image ) + +-(UIImage *) image; +-(void) savePNG:(NSString *)filePath; +-(void) saveJPEG:(NSString *)filePath :(float)quality; + +@end diff --git a/iOS/PDFViewer/UIImage+PDF/UIView+Image.m b/iOS/PDFViewer/UIImage+PDF/UIView+Image.m new file mode 100644 index 0000000..c1a9e60 --- /dev/null +++ b/iOS/PDFViewer/UIImage+PDF/UIView+Image.m @@ -0,0 +1,44 @@ +// +// UIView+Image.m +// +// Created by Nigel Barber on 21/07/2011. +// Copyright 2011 Mindbrix Limited. All rights reserved. +// + +#import "UIView+Image.h" + + +@implementation UIView( Image ) + +-(UIImage *)image +{ + if( [ self.layer respondsToSelector:@selector(setShouldRasterize:)]) + { + UIGraphicsBeginImageContextWithOptions( self.bounds.size, NO, self.contentScaleFactor ); + } + else + { + UIGraphicsBeginImageContext( self.bounds.size ); + } + + [ self.layer renderInContext:UIGraphicsGetCurrentContext() ]; + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + + + +-(void) savePNG:(NSString *)filePath +{ + [ UIImagePNGRepresentation(self.image) writeToFile:filePath atomically:NO ]; +} + + +-(void) saveJPEG:(NSString *)filePath :(float)quality +{ + [ UIImageJPEGRepresentation(self.image, quality) writeToFile:filePath atomically:NO ]; +} + +@end diff --git a/iOS/PDFViewer/pdfview.PNG b/iOS/PDFViewer/pdfview.PNG new file mode 100644 index 0000000..4ff21fc Binary files /dev/null and b/iOS/PDFViewer/pdfview.PNG differ diff --git a/iOS/PDFViewer/readme.pdf b/iOS/PDFViewer/readme.pdf new file mode 100644 index 0000000..c69f527 Binary files /dev/null and b/iOS/PDFViewer/readme.pdf differ diff --git a/iOS/PDFViewer/www/PDFViewer.js b/iOS/PDFViewer/www/PDFViewer.js new file mode 100644 index 0000000..8fb3756 --- /dev/null +++ b/iOS/PDFViewer/www/PDFViewer.js @@ -0,0 +1,68 @@ +//PDFViewer based on ChildBrowser + +/* MIT licensed */ +// (c) 2010 Jesse MacFadyen, Nitobi + + +(function() { + + var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + + function PDFViewer() { + // Does nothing + } + + + // Callback when the user chooses the 'Done' button + // called from native + PDFViewer._onClose = function() + { + window.plugins.PDFViewer.onClose(); + }; + + + +/* The interface that you will use to access functionality */ + + // Show a webpage, will result in a callback to onLocationChange + PDFViewer.prototype.showPDF = function(loc) + { + cordovaRef.exec("PDFViewerCommand.showPDF", loc); + }; + + // close the browser, will NOT result in close callback + PDFViewer.prototype.close = function() + { + cordovaRef.exec("PDFViewerCommand.close"); + }; + + // Not Implemented + PDFViewer.prototype.jsExec = function(jsString) + { + // Not Implemented!! + //PhoneGap.exec("PDFViewerCommand.jsExec",jsString); + }; + + // Note: this plugin does NOT install itself, call this method some time after deviceready to install it + // it will be returned, and also available globally from window.plugins.PDFViewer + PDFViewer.install = function() + { + if(!window.plugins) { + window.plugins = {}; + } + if ( ! window.plugins.PDFViewer ) { + window.plugins.PDFViewer = new PDFViewer(); + } + + }; + + + if (cordovaRef && cordovaRef.addConstructor) { + cordovaRef.addConstructor(PDFViewer.install); + } else { + console.log("PDFViewer Cordova Plugin could not be installed."); + return null; + } + + + })(); \ No newline at end of file diff --git a/iOS/PDFViewer/www/cordova-2.1.0.js b/iOS/PDFViewer/www/cordova-2.1.0.js new file mode 100755 index 0000000..db81edf --- /dev/null +++ b/iOS/PDFViewer/www/cordova-2.1.0.js @@ -0,0 +1,5305 @@ +// commit 143f5221a6251c9cbccdedc57005c61551b97f12 + +// File generated at :: Wed Sep 12 2012 15:26:58 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code + */ + fireDocumentEvent: function(type, data, bNoDetach) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + if( bNoDetach ) { + documentEventHandlers[type].fire(evt); + } + else { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'), + nextGuid = 1; + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + var headers = null; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + headers = options.headers; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + * @param headers {Object} Keys are header names, values are header values. Multiple + * headers of the same name are not supported. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; + this.headers = headers || null; +}; + +module.exports = FileUploadOptions; + +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger than file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(null, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param msgType The 'type' of update this is + * @param value Use of value is determined by the msgType + */ +Media.onStatus = function(id, msgType, value) { + + var media = mediaObjects[id]; + + if(media) { + switch(msgType) { + case Media.MEDIA_STATE : + media.statusCallback && media.statusCallback(value); + if(value == Media.MEDIA_STOPPED) { + media.successCallback && media.successCallback(); + } + break; + case Media.MEDIA_DURATION : + media._duration = value; + break; + case Media.MEDIA_ERROR : + media.errorCallback && media.errorCallback(value); + break; + case Media.MEDIA_POSITION : + media._position = Number(value); + break; + default : + console && console.error && console.error("Unhandled Media.onStatus :: " + msgType); + break; + } + } + else { + console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id); + } + +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. +*/ +/* + According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror + We should never be creating these objects, we should just implement the interface + which has 1 property for an instance, 'code' + + instead of doing : + errorCallbackFunction( new MediaError(3,'msg') ); +we should simply use a literal : + errorCallbackFunction( {'code':3} ); + */ + +if(!MediaError) { + var MediaError = function(code, msg) { + this.code = (typeof code != 'undefined') ? code : null; + this.message = msg || ""; // message is NON-standard! do not use! + }; +} + +MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0; +MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1; +MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2; +MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4; +// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below. +// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes +MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4; + +module.exports = MediaError; + +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +module.exports = MediaFile; + +}); + +// file: lib/common/plugin/MediaFileData.js +define("cordova/plugin/MediaFileData", function(require, exports, module) { +/** + * MediaFileData encapsulates format information of a media file. + * + * @param {DOMString} codecs + * @param {long} bitrate + * @param {long} height + * @param {long} width + * @param {float} duration + */ +var MediaFileData = function(codecs, bitrate, height, width, duration){ + this.codecs = codecs || null; + this.bitrate = bitrate || 0; + this.height = height || 0; + this.width = width || 0; + this.duration = duration || 0; +}; + +module.exports = MediaFileData; +}); + +// file: lib/common/plugin/Metadata.js +define("cordova/plugin/Metadata", function(require, exports, module) { +/** + * Information about the state of the file or directory + * + * {Date} modificationTime (readonly) + */ +var Metadata = function(time) { + this.modificationTime = (typeof time != 'undefined'?new Date(time):null); +}; + +module.exports = Metadata; +}); + +// file: lib/common/plugin/Position.js +define("cordova/plugin/Position", function(require, exports, module) { +var Coordinates = require('cordova/plugin/Coordinates'); + +var Position = function(coords, timestamp) { + if (coords) { + this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy); + } else { + this.coords = new Coordinates(); + } + this.timestamp = (timestamp !== undefined) ? timestamp : new Date(); +}; + +module.exports = Position; + +}); + +// file: lib/common/plugin/PositionError.js +define("cordova/plugin/PositionError", function(require, exports, module) { +/** + * Position error object + * + * @constructor + * @param code + * @param message + */ +var PositionError = function(code, message) { + this.code = code || null; + this.message = message || ''; +}; + +PositionError.PERMISSION_DENIED = 1; +PositionError.POSITION_UNAVAILABLE = 2; +PositionError.TIMEOUT = 3; + +module.exports = PositionError; +}); + +// file: lib/common/plugin/ProgressEvent.js +define("cordova/plugin/ProgressEvent", function(require, exports, module) { +// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill +// Feature test: See if we can instantiate a native ProgressEvent; +// if so, use that approach, +// otherwise fill-in with our own implementation. +// +// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview. +var ProgressEvent = (function() { + /* + var createEvent = function(data) { + var event = document.createEvent('Events'); + event.initEvent('ProgressEvent', false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + if (data.target) { + // TODO: cannot call .dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retrieved a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object whose properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/echo.js +define("cordova/plugin/echo", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback. + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + * @param message The string to be echoed. + * @param forceAsync Whether to force an async return value (for testing native->js bridge). + */ +module.exports = function(successCallback, errorCallback, message, forceAsync) { + var action = forceAsync ? 'echoAsync' : 'echo'; + exec(successCallback, errorCallback, "Echo", action, [message]); +}; + + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/ios/plugin/ios/Contact.js +define("cordova/plugin/ios/Contact", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'); + +/** + * Provides iOS Contact.display API. + */ +module.exports = { + display : function(errorCB, options) { + /* + * Display a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + */ + + if (this.id === null) { + if (typeof errorCB === "function") { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + } + else { + exec(null, errorCB, "Contacts","displayContact", [this.id, options]); + } + } +}; +}); + +// file: lib/ios/plugin/ios/Entry.js +define("cordova/plugin/ios/Entry", function(require, exports, module) { +module.exports = { + toURL:function() { + // TODO: refactor path in a cross-platform way so we can eliminate + // these kinds of platform-specific hacks. + return "file://localhost" + this.fullPath; + }, + toURI: function() { + console.log("DEPRECATED: Update your code to use 'toURL'"); + return "file://localhost" + this.fullPath; + } +}; +}); + +// file: lib/ios/plugin/ios/FileReader.js +define("cordova/plugin/ios/FileReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + FileReader = require('cordova/plugin/FileReader'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +module.exports = { + readAsText:function(file, encoding) { + // Figure out pathing + this.fileName = ''; + if (typeof file.fullPath === 'undefined') { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + // Default encoding is UTF-8 + var enc = encoding ? encoding : "UTF-8"; + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save result + me.result = decodeURIComponent(r); + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // null result + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + "File", "readAsText", [this.fileName, enc]); + } +}; +}); + +// file: lib/ios/plugin/ios/console.js +define("cordova/plugin/ios/console", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * This class provides access to the debugging console. + * @constructor + */ +var DebugConsole = function() { + this.winConsole = window.console; + this.logLevel = DebugConsole.INFO_LEVEL; +}; + +// from most verbose, to least verbose +DebugConsole.ALL_LEVEL = 1; // same as first level +DebugConsole.INFO_LEVEL = 1; +DebugConsole.WARN_LEVEL = 2; +DebugConsole.ERROR_LEVEL = 4; +DebugConsole.NONE_LEVEL = 8; + +DebugConsole.prototype.setLevel = function(level) { + this.logLevel = level; +}; + +var stringify = function(message) { + try { + if (typeof message === "object" && JSON && JSON.stringify) { + try { + return JSON.stringify(message); + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + } else { + return message.toString(); + } + } catch (e) { + return e.toString(); + } +}; + +/** + * Print a normal log message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.log = function(message) { + if (this.logLevel <= DebugConsole.INFO_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'INFO' } ]); + } + else if (this.winConsole && this.winConsole.log) { + this.winConsole.log(message); + } +}; + +/** + * Print a warning message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.warn = function(message) { + if (this.logLevel <= DebugConsole.WARN_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'WARN' } ]); + } + else if (this.winConsole && this.winConsole.warn) { + this.winConsole.warn(message); + } +}; + +/** + * Print an error message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.error = function(message) { + if (this.logLevel <= DebugConsole.ERROR_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'ERROR' } ]); + } + else if (this.winConsole && this.winConsole.error){ + this.winConsole.error(message); + } +}; + +module.exports = new DebugConsole(); +}); + +// file: lib/ios/plugin/ios/contacts.js +define("cordova/plugin/ios/contacts", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides iOS enhanced contacts API. + */ +module.exports = { + newContactUI : function(successCallback) { + /* + * Create a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * returns: the id of the created contact as param to successCallback + */ + exec(successCallback, null, "Contacts","newContact", []); + }, + chooseContact : function(successCallback, options) { + /* + * Select a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + * fields: array of fields to return in contact object (see ContactOptions.fields) + * + * @returns + * id of contact selected + * ContactObject + * if no fields provided contact contains just id information + * if fields provided contact object contains information for the specified fields + * + */ + var win = function(result) { + var fullContact = require('cordova/plugin/contacts').create(result); + successCallback(fullContact.id, fullContact); + }; + exec(win, null, "Contacts","chooseContact", [options]); + } +}; +}); + +// file: lib/ios/plugin/ios/nativecomm.js +define("cordova/plugin/ios/nativecomm", function(require, exports, module) { +var cordova = require('cordova'); + +/** + * Called by native code to retrieve all queued commands and clear the queue. + */ +module.exports = function() { + // Each entry in commandQueue is a JSON string already. + var json = '[' + cordova.commandQueue.join(',') + ']'; + cordova.commandQueue.length = 0; + return json; +}; + +}); + +// file: lib/ios/plugin/ios/notification.js +define("cordova/plugin/ios/notification", function(require, exports, module) { +var Media = require('cordova/plugin/Media'); + +module.exports = { + beep:function(count) { + (new Media('beep.wav')).play(); + } +}; +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + + Hello World + + +
+

Apache Cordova

+ + + + + + + +
+ + + + + + + diff --git a/iOS/PDFViewer/www/js/index.js b/iOS/PDFViewer/www/js/index.js new file mode 100755 index 0000000..0b33dc8 --- /dev/null +++ b/iOS/PDFViewer/www/js/index.js @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +var app = { + // Application Constructor + initialize: function() { + this.bindEvents(); + }, + // Bind Event Listeners + // + // Bind any events that are required on startup. Common events are: + // `load`, `deviceready`, `offline`, and `online`. + bindEvents: function() { + document.addEventListener('deviceready', this.onDeviceReady, false); + }, + // deviceready Event Handler + // + // The scope of `this` is the event. In order to call the `receivedEvent` + // function, we must explicity call `app.receivedEvent(...);` + onDeviceReady: function() { + app.receivedEvent('deviceready'); + + }, + // Update DOM on a Received Event + receivedEvent: function(id) { + var parentElement = document.getElementById(id); + var listeningElement = parentElement.querySelector('.listening'); + var receivedElement = parentElement.querySelector('.received'); + + listeningElement.setAttribute('style', 'display:none;'); + receivedElement.setAttribute('style', 'display:block;'); + + console.log('Received Event: ' + id); + + + + cordova.exec("PDFViewerCommand.showPDF", loc); + + + + } +}; diff --git a/iOS/PDFViewer/www/res/icon/ios/icon-57-2x.png b/iOS/PDFViewer/www/res/icon/ios/icon-57-2x.png new file mode 100755 index 0000000..efd9c37 Binary files /dev/null and b/iOS/PDFViewer/www/res/icon/ios/icon-57-2x.png differ diff --git a/iOS/PDFViewer/www/res/icon/ios/icon-57.png b/iOS/PDFViewer/www/res/icon/ios/icon-57.png new file mode 100755 index 0000000..c795fc4 Binary files /dev/null and b/iOS/PDFViewer/www/res/icon/ios/icon-57.png differ diff --git a/iOS/PDFViewer/www/res/icon/ios/icon-72-2x.png b/iOS/PDFViewer/www/res/icon/ios/icon-72-2x.png new file mode 100755 index 0000000..dd819da Binary files /dev/null and b/iOS/PDFViewer/www/res/icon/ios/icon-72-2x.png differ diff --git a/iOS/PDFViewer/www/res/icon/ios/icon-72.png b/iOS/PDFViewer/www/res/icon/ios/icon-72.png new file mode 100755 index 0000000..b1cfde7 Binary files /dev/null and b/iOS/PDFViewer/www/res/icon/ios/icon-72.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-ipad-landscape-2x.png b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-landscape-2x.png new file mode 100755 index 0000000..95c542d Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-landscape-2x.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-ipad-landscape.png b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-landscape.png new file mode 100755 index 0000000..04be5ac Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-landscape.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-ipad-portrait-2x.png b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-portrait-2x.png new file mode 100755 index 0000000..aae1862 Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-portrait-2x.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-ipad-portrait.png b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-portrait.png new file mode 100755 index 0000000..41e839d Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-ipad-portrait.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-iphone-landscape-2x.png b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-landscape-2x.png new file mode 100755 index 0000000..0165669 Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-landscape-2x.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-iphone-landscape.png b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-landscape.png new file mode 100755 index 0000000..d154883 Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-landscape.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-iphone-portrait-2x.png b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-portrait-2x.png new file mode 100755 index 0000000..bd24886 Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-portrait-2x.png differ diff --git a/iOS/PDFViewer/www/res/screen/ios/screen-iphone-portrait.png b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-portrait.png new file mode 100755 index 0000000..6fcba56 Binary files /dev/null and b/iOS/PDFViewer/www/res/screen/ios/screen-iphone-portrait.png differ diff --git a/iOS/PDFViewer/www/spec.html b/iOS/PDFViewer/www/spec.html new file mode 100755 index 0000000..71f00de --- /dev/null +++ b/iOS/PDFViewer/www/spec.html @@ -0,0 +1,68 @@ + + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/PDFViewer/www/spec/helper.js b/iOS/PDFViewer/www/spec/helper.js new file mode 100755 index 0000000..929f776 --- /dev/null +++ b/iOS/PDFViewer/www/spec/helper.js @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +afterEach(function() { + document.getElementById('stage').innerHTML = ''; +}); + +var helper = { + trigger: function(obj, name) { + var e = document.createEvent('Event'); + e.initEvent(name, true, true); + obj.dispatchEvent(e); + }, + getComputedStyle: function(querySelector, property) { + var element = document.querySelector(querySelector); + return window.getComputedStyle(element).getPropertyValue(property); + } +}; diff --git a/iOS/PDFViewer/www/spec/index.js b/iOS/PDFViewer/www/spec/index.js new file mode 100755 index 0000000..20f8be5 --- /dev/null +++ b/iOS/PDFViewer/www/spec/index.js @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +describe('app', function() { + describe('initialize', function() { + it('should bind deviceready', function() { + runs(function() { + spyOn(app, 'onDeviceReady'); + app.initialize(); + helper.trigger(window.document, 'deviceready'); + }); + + waitsFor(function() { + return (app.onDeviceReady.calls.length > 0); + }, 'onDeviceReady should be called once', 500); + + runs(function() { + expect(app.onDeviceReady).toHaveBeenCalled(); + }); + }); + }); + + describe('onDeviceReady', function() { + it('should report that it fired', function() { + spyOn(app, 'receivedEvent'); + app.onDeviceReady(); + expect(app.receivedEvent).toHaveBeenCalledWith('deviceready'); + }); + }); + + describe('receivedEvent', function() { + beforeEach(function() { + var el = document.getElementById('stage'); + el.innerHTML = ['
', + '

Listening

', + '

Received

', + '
'].join('\n'); + }); + + it('should hide the listening element', function() { + app.receivedEvent('deviceready'); + var displayStyle = helper.getComputedStyle('#deviceready .listening', 'display'); + expect(displayStyle).toEqual('none'); + }); + + it('should show the received element', function() { + app.receivedEvent('deviceready'); + var displayStyle = helper.getComputedStyle('#deviceready .received', 'display'); + expect(displayStyle).toEqual('block'); + }); + }); +}); diff --git a/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/MIT.LICENSE b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/MIT.LICENSE new file mode 100755 index 0000000..7c435ba --- /dev/null +++ b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008-2011 Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine-html.js b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine-html.js new file mode 100755 index 0000000..a0b0639 --- /dev/null +++ b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine-html.js @@ -0,0 +1,616 @@ +jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + +jasmine.HtmlReporter = function(_doc) { + var self = this; + var doc = _doc || window.document; + + var reporterView; + + var dom = {}; + + // Jasmine Reporter Public Interface + self.logRunningSpecs = false; + + self.reportRunnerStarting = function(runner) { + var specs = runner.specs() || []; + + if (specs.length == 0) { + return; + } + + createReporterDom(runner.env.versionString()); + doc.body.appendChild(dom.reporter); + + reporterView = new jasmine.HtmlReporter.ReporterView(dom); + reporterView.addSpecs(specs, self.specFilter); + }; + + self.reportRunnerResults = function(runner) { + reporterView && reporterView.complete(); + }; + + self.reportSuiteResults = function(suite) { + reporterView.suiteComplete(suite); + }; + + self.reportSpecStarting = function(spec) { + if (self.logRunningSpecs) { + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } + }; + + self.reportSpecResults = function(spec) { + reporterView.specComplete(spec); + }; + + self.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } + }; + + self.specFilter = function(spec) { + if (!focusedSpecName()) { + return true; + } + + return spec.getFullName().indexOf(focusedSpecName()) === 0; + }; + + return self; + + function focusedSpecName() { + var specName; + + (function memoizeFocusedSpec() { + if (specName) { + return; + } + + var paramMap = []; + var params = doc.location.search.substring(1).split('&'); + + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + specName = paramMap.spec; + })(); + + return specName; + } + + function createReporterDom(version) { + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, + dom.banner = self.createDom('div', { className: 'banner' }, + self.createDom('span', { className: 'title' }, "Jasmine "), + self.createDom('span', { className: 'version' }, version)), + + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), + dom.alert = self.createDom('div', {className: 'alert'}), + dom.results = self.createDom('div', {className: 'results'}, + dom.summary = self.createDom('div', { className: 'summary' }), + dom.details = self.createDom('div', { id: 'details' })) + ); + } +}; +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) { + this.startedAt = new Date(); + this.runningSpecCount = 0; + this.completeSpecCount = 0; + this.passedCount = 0; + this.failedCount = 0; + this.skippedCount = 0; + + this.createResultsMenu = function() { + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), + ' | ', + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); + + this.summaryMenuItem.onclick = function() { + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); + }; + + this.detailsMenuItem.onclick = function() { + showDetails(); + }; + }; + + this.addSpecs = function(specs, specFilter) { + this.totalSpecCount = specs.length; + + this.views = { + specs: {}, + suites: {} + }; + + for (var i = 0; i < specs.length; i++) { + var spec = specs[i]; + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); + if (specFilter(spec)) { + this.runningSpecCount++; + } + } + }; + + this.specComplete = function(spec) { + this.completeSpecCount++; + + if (isUndefined(this.views.specs[spec.id])) { + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); + } + + var specView = this.views.specs[spec.id]; + + switch (specView.status()) { + case 'passed': + this.passedCount++; + break; + + case 'failed': + this.failedCount++; + break; + + case 'skipped': + this.skippedCount++; + break; + } + + specView.refresh(); + this.refresh(); + }; + + this.suiteComplete = function(suite) { + var suiteView = this.views.suites[suite.id]; + if (isUndefined(suiteView)) { + return; + } + suiteView.refresh(); + }; + + this.refresh = function() { + + if (isUndefined(this.resultsMenu)) { + this.createResultsMenu(); + } + + // currently running UI + if (isUndefined(this.runningAlert)) { + this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); + dom.alert.appendChild(this.runningAlert); + } + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); + + // skipped specs UI + if (isUndefined(this.skippedAlert)) { + this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); + } + + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.skippedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.skippedAlert); + } + + // passing specs UI + if (isUndefined(this.passedAlert)) { + this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); + } + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); + + // failing specs UI + if (isUndefined(this.failedAlert)) { + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); + } + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); + + if (this.failedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.failedAlert); + dom.alert.appendChild(this.resultsMenu); + } + + // summary info + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + }; + + this.complete = function() { + dom.alert.removeChild(this.runningAlert); + + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.failedCount === 0) { + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); + } else { + showDetails(); + } + + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + }; + + return this; + + function showDetails() { + if (dom.reporter.className.search(/showDetails/) === -1) { + dom.reporter.className += " showDetails"; + } + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDefined(obj) { + return !isUndefined(obj); + } + + function specPluralizedFor(count) { + var str = count + " spec"; + if (count > 1) { + str += "s" + } + return str; + } + +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); + + +jasmine.HtmlReporter.SpecView = function(spec, dom, views) { + this.spec = spec; + this.dom = dom; + this.views = views; + + this.symbol = this.createDom('li', { className: 'pending' }); + this.dom.symbolSummary.appendChild(this.symbol); + + this.summary = this.createDom('div', { className: 'specSummary' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.description) + ); + + this.detail = this.createDom('div', { className: 'specDetail' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.getFullName()) + ); +}; + +jasmine.HtmlReporter.SpecView.prototype.status = function() { + return this.getSpecStatus(this.spec); +}; + +jasmine.HtmlReporter.SpecView.prototype.refresh = function() { + this.symbol.className = this.status(); + + switch (this.status()) { + case 'skipped': + break; + + case 'passed': + this.appendSummaryToSuiteDiv(); + break; + + case 'failed': + this.appendSummaryToSuiteDiv(); + this.appendFailureDetail(); + break; + } +}; + +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { + this.summary.className += ' ' + this.status(); + this.appendToSummary(this.spec, this.summary); +}; + +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { + this.detail.className += ' ' + this.status(); + + var resultItems = this.spec.results().getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + this.detail.appendChild(messagesDiv); + this.dom.details.appendChild(this.detail); + } +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { + this.suite = suite; + this.dom = dom; + this.views = views; + + this.element = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) + ); + + this.appendToSummary(this.suite, this.element); +}; + +jasmine.HtmlReporter.SuiteView.prototype.status = function() { + return this.getSpecStatus(this.suite); +}; + +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { + this.element.className += " " + this.status(); +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); + +/* @deprecated Use jasmine.HtmlReporter instead + */ +jasmine.TrivialReporter = function(doc) { + this.document = doc || document; + this.suiteDivs = {}; + this.logRunningSpecs = false; +}; + +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { el.appendChild(child); } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + this.createDom('span', { className: 'title' }, "Jasmine"), + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); + + var suites = runner.suites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + var suiteDiv = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); + this.suiteDivs[suite.id] = suiteDiv; + var parentDiv = this.outerDiv; + if (suite.parentSuite) { + parentDiv = this.suiteDivs[suite.parentSuite.id]; + } + parentDiv.appendChild(suiteDiv); + } + + this.startedAt = new Date(); + + var self = this; + showPassed.onclick = function(evt) { + if (showPassed.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onclick = function(evt) { + if (showSkipped.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; +}; + +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { + var results = runner.results(); + var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; + this.runnerDiv.setAttribute("class", className); + //do it twice for IE + this.runnerDiv.setAttribute("className", className); + var specs = runner.specs(); + var specCount = 0; + for (var i = 0; i < specs.length; i++) { + if (this.specFilter(specs[i])) { + specCount++; + } + } + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); +}; + +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { + var results = suite.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.totalCount === 0) { // todo: change this to check results.skipped + status = 'skipped'; + } + this.suiteDivs[suite.id].className += " " + status; +}; + +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { + if (this.logRunningSpecs) { + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } +}; + +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { + var results = spec.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + var specDiv = this.createDom('div', { className: 'spec ' + status }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); + + + var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + + this.suiteDivs[spec.suite.id].appendChild(specDiv); +}; + +jasmine.TrivialReporter.prototype.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } +}; + +jasmine.TrivialReporter.prototype.getLocation = function() { + return this.document.location; +}; + +jasmine.TrivialReporter.prototype.specFilter = function(spec) { + var paramMap = {}; + var params = this.getLocation().search.substring(1).split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + if (!paramMap.spec) { + return true; + } + return spec.getFullName().indexOf(paramMap.spec) === 0; +}; diff --git a/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine.css b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine.css new file mode 100755 index 0000000..826e575 --- /dev/null +++ b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine.css @@ -0,0 +1,81 @@ +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } + +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +#HTMLReporter a { text-decoration: none; } +#HTMLReporter a:hover { text-decoration: underline; } +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } +#HTMLReporter #jasmine_content { position: fixed; right: 100%; } +#HTMLReporter .version { color: #aaaaaa; } +#HTMLReporter .banner { margin-top: 14px; } +#HTMLReporter .duration { color: #aaaaaa; float: right; } +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +#HTMLReporter .symbolSummary li.passed { font-size: 14px; } +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } +#HTMLReporter .symbolSummary li.failed { line-height: 9px; } +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } +#HTMLReporter .symbolSummary li.pending { line-height: 11px; } +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +#HTMLReporter .runningAlert { background-color: #666666; } +#HTMLReporter .skippedAlert { background-color: #aaaaaa; } +#HTMLReporter .skippedAlert:first-child { background-color: #333333; } +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } +#HTMLReporter .passingAlert { background-color: #a6b779; } +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } +#HTMLReporter .failingAlert { background-color: #cf867e; } +#HTMLReporter .failingAlert:first-child { background-color: #b03911; } +#HTMLReporter .results { margin-top: 14px; } +#HTMLReporter #details { display: none; } +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter.showDetails .summary { display: none; } +#HTMLReporter.showDetails #details { display: block; } +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter .summary { margin-top: 14px; } +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } +#HTMLReporter .summary .specSummary.failed a { color: #b03911; } +#HTMLReporter .description + .suite { margin-top: 0; } +#HTMLReporter .suite { margin-top: 14px; } +#HTMLReporter .suite a { color: #333333; } +#HTMLReporter #details .specDetail { margin-bottom: 28px; } +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } +#HTMLReporter .resultMessage span.result { display: block; } +#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } + +#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } +#TrivialReporter a:visited, #TrivialReporter a { color: #303; } +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } +#TrivialReporter .runner.running { background-color: yellow; } +#TrivialReporter .options { text-align: right; font-size: .8em; } +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } +#TrivialReporter .suite .suite { margin: 5px; } +#TrivialReporter .suite.passed { background-color: #dfd; } +#TrivialReporter .suite.failed { background-color: #fdd; } +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } +#TrivialReporter .spec.skipped { background-color: #bbb; } +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } +#TrivialReporter .passed { background-color: #cfc; display: none; } +#TrivialReporter .failed { background-color: #fbb; } +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } +#TrivialReporter .resultMessage .mismatch { color: black; } +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } +#TrivialReporter #jasmine_content { position: fixed; right: 100%; } +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine.js b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine.js new file mode 100755 index 0000000..03bf89a --- /dev/null +++ b/iOS/PDFViewer/www/spec/lib/jasmine-1.2.0/jasmine.js @@ -0,0 +1,2529 @@ +var isCommonJS = typeof window == "undefined"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS) exports.jasmine = jasmine; +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0) text += " "; + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the + * attributes on the object. + * + * @example + * // don't care about any other attributes than foo. + * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); + * + * @param sample {Object} sample + * @returns matchable object for the sample + */ +jasmine.objectContaining = function (sample) { + return new jasmine.Matchers.ObjectContaining(sample); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS) exports.spyOn = spyOn; + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS) exports.it = it; + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS) exports.xit = xit; + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS) exports.expect = expect; + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS) exports.runs = runs; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS) exports.waits = waits; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS) exports.waitsFor = waitsFor; + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS) exports.beforeEach = beforeEach; + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS) exports.afterEach = afterEach; + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS) exports.describe = describe; + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS) exports.xdescribe = xdescribe; + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) return str; + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + + if (a === b) return true; + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a.jasmineMatches) { + return a.jasmineMatches(b); + } + + if (b.jasmineMatches) { + return b.jasmineMatches(a); + } + + if (a instanceof jasmine.Matchers.ObjectContaining) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.ObjectContaining) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') continue; + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) return result; + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0) message += ","; + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + if (this.actual.callCount === 0) { + // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." + ]; + } else { + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) + ]; + } + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + var multiplier = Math.pow(10, precision); + var actual = Math.round(this.actual * multiplier); + expected = Math.round(expected * multiplier); + return expected == actual; +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} expected + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineToString = function() { + return ''; +}; + +jasmine.Matchers.ObjectContaining = function (sample) { + this.sample = sample; +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var env = jasmine.getEnv(); + + var hasKey = function(obj, keyName) { + return obj != null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { + return ""; +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block) { + this.blocks.unshift(block); +}; + +jasmine.Queue.prototype.add = function(block) { + this.blocks.push(block); +}; + +jasmine.Queue.prototype.insertNext = function(block) { + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + + if (self.index < self.blocks.length && !this.abort) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this)); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 2, + "build": 0, + "revision": 1337005947 +}; diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.pbxproj b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.pbxproj deleted file mode 100644 index 5aa747f..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.pbxproj +++ /dev/null @@ -1,603 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 48BA2DCD15129ACE00F58A27 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DCC15129ACE00F58A27 /* Foundation.framework */; }; - 48BA2DCF15129ACE00F58A27 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DCE15129ACE00F58A27 /* UIKit.framework */; }; - 48BA2DD115129ACE00F58A27 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DD015129ACE00F58A27 /* CoreGraphics.framework */; }; - 48BA2DD315129ACE00F58A27 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DD215129ACE00F58A27 /* AddressBook.framework */; }; - 48BA2DD515129ACE00F58A27 /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DD415129ACE00F58A27 /* AddressBookUI.framework */; }; - 48BA2DD715129ACE00F58A27 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DD615129ACE00F58A27 /* AudioToolbox.framework */; }; - 48BA2DD915129ACE00F58A27 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DD815129ACE00F58A27 /* AVFoundation.framework */; }; - 48BA2DDB15129ACE00F58A27 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DDA15129ACE00F58A27 /* CoreLocation.framework */; }; - 48BA2DDD15129ACE00F58A27 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DDC15129ACE00F58A27 /* MediaPlayer.framework */; }; - 48BA2DDF15129ACE00F58A27 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DDE15129ACE00F58A27 /* QuartzCore.framework */; }; - 48BA2DE115129ACE00F58A27 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DE015129ACE00F58A27 /* SystemConfiguration.framework */; }; - 48BA2DE315129ACE00F58A27 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DE215129ACE00F58A27 /* MobileCoreServices.framework */; }; - 48BA2DE515129ACE00F58A27 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DE415129ACE00F58A27 /* CoreMedia.framework */; }; - 48BA2DEB15129ACE00F58A27 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2DE915129ACE00F58A27 /* InfoPlist.strings */; }; - 48BA2DED15129ACE00F58A27 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 48BA2DEC15129ACE00F58A27 /* main.m */; }; - 48BA2DF015129ACE00F58A27 /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2DEF15129ACE00F58A27 /* Cordova.framework */; }; - 48BA2DF515129ACE00F58A27 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2DF315129ACE00F58A27 /* Localizable.strings */; }; - 48BA2DF915129ACE00F58A27 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2DF715129ACE00F58A27 /* Localizable.strings */; }; - 48BA2DFD15129ACE00F58A27 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2DFB15129ACE00F58A27 /* Localizable.strings */; }; - 48BA2E0115129ACE00F58A27 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2DFF15129ACE00F58A27 /* Localizable.strings */; }; - 48BA2E0415129ACE00F58A27 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E0315129ACE00F58A27 /* icon.png */; }; - 48BA2E0615129ACE00F58A27 /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E0515129ACE00F58A27 /* icon@2x.png */; }; - 48BA2E0815129ACE00F58A27 /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E0715129ACE00F58A27 /* icon-72.png */; }; - 48BA2E0B15129ACE00F58A27 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E0A15129ACE00F58A27 /* Default.png */; }; - 48BA2E0D15129ACE00F58A27 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E0C15129ACE00F58A27 /* Default@2x.png */; }; - 48BA2E0F15129ACE00F58A27 /* Capture.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E0E15129ACE00F58A27 /* Capture.bundle */; }; - 48BA2E1115129ACE00F58A27 /* Cordova.plist in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E1015129ACE00F58A27 /* Cordova.plist */; }; - 48BA2E1515129ACE00F58A27 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 48BA2E1415129ACE00F58A27 /* AppDelegate.m */; }; - 48BA2E1815129ACE00F58A27 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 48BA2E1715129ACE00F58A27 /* MainViewController.m */; }; - 48BA2E1A15129ACE00F58A27 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E1915129ACE00F58A27 /* MainViewController.xib */; }; - 48BA2E1E15129ACE00F58A27 /* verify.sh in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E1D15129ACE00F58A27 /* verify.sh */; }; - 48BA2E2515129AEB00F58A27 /* www in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E2415129AEB00F58A27 /* www */; }; - 48BA2E3315129B0800F58A27 /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E2615129B0800F58A27 /* index.html */; }; - 48BA2E3415129B0800F58A27 /* master.css in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E2715129B0800F58A27 /* master.css */; }; - 48BA2E3515129B0800F58A27 /* libPayPalMEP.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 48BA2E2915129B0800F58A27 /* libPayPalMEP.a */; }; - 48BA2E3615129B0800F58A27 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 48BA2E2F15129B0800F58A27 /* README.md */; }; - 48BA2E3815129B0800F58A27 /* SAiOSPaypalPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 48BA2E3215129B0800F58A27 /* SAiOSPaypalPlugin.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 48BA2DC815129ACE00F58A27 /* PayPalExampleCDV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PayPalExampleCDV.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 48BA2DCC15129ACE00F58A27 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - 48BA2DCE15129ACE00F58A27 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; - 48BA2DD015129ACE00F58A27 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 48BA2DD215129ACE00F58A27 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; - 48BA2DD415129ACE00F58A27 /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; - 48BA2DD615129ACE00F58A27 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; - 48BA2DD815129ACE00F58A27 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; - 48BA2DDA15129ACE00F58A27 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; - 48BA2DDC15129ACE00F58A27 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; - 48BA2DDE15129ACE00F58A27 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; - 48BA2DE015129ACE00F58A27 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; - 48BA2DE215129ACE00F58A27 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; - 48BA2DE415129ACE00F58A27 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; - 48BA2DE815129ACE00F58A27 /* PayPalExampleCDV-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PayPalExampleCDV-Info.plist"; sourceTree = ""; }; - 48BA2DEA15129ACE00F58A27 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 48BA2DEC15129ACE00F58A27 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 48BA2DEE15129ACE00F58A27 /* PayPalExampleCDV-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PayPalExampleCDV-Prefix.pch"; sourceTree = ""; }; - 48BA2DEF15129ACE00F58A27 /* Cordova.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cordova.framework; path = /Users/Shared/Cordova/Frameworks/Cordova.framework; sourceTree = ""; }; - 48BA2DF415129ACE00F58A27 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Resources/en.lproj/Localizable.strings; sourceTree = ""; }; - 48BA2DF815129ACE00F58A27 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Resources/es.lproj/Localizable.strings; sourceTree = ""; }; - 48BA2DFC15129ACE00F58A27 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Resources/de.lproj/Localizable.strings; sourceTree = ""; }; - 48BA2E0015129ACE00F58A27 /* se */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = se; path = Resources/se.lproj/Localizable.strings; sourceTree = ""; }; - 48BA2E0315129ACE00F58A27 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = Resources/icons/icon.png; sourceTree = ""; }; - 48BA2E0515129ACE00F58A27 /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon@2x.png"; path = "Resources/icons/icon@2x.png"; sourceTree = ""; }; - 48BA2E0715129ACE00F58A27 /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-72.png"; path = "Resources/icons/icon-72.png"; sourceTree = ""; }; - 48BA2E0A15129ACE00F58A27 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Resources/splash/Default.png; sourceTree = ""; }; - 48BA2E0C15129ACE00F58A27 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "Resources/splash/Default@2x.png"; sourceTree = ""; }; - 48BA2E0E15129ACE00F58A27 /* Capture.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Capture.bundle; path = Resources/Capture.bundle; sourceTree = ""; }; - 48BA2E1015129ACE00F58A27 /* Cordova.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Cordova.plist; sourceTree = ""; }; - 48BA2E1315129ACE00F58A27 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Classes/AppDelegate.h; sourceTree = ""; }; - 48BA2E1415129ACE00F58A27 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Classes/AppDelegate.m; sourceTree = ""; }; - 48BA2E1615129ACE00F58A27 /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MainViewController.h; path = Classes/MainViewController.h; sourceTree = ""; }; - 48BA2E1715129ACE00F58A27 /* MainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MainViewController.m; path = Classes/MainViewController.m; sourceTree = ""; }; - 48BA2E1915129ACE00F58A27 /* MainViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainViewController.xib; path = Classes/MainViewController.xib; sourceTree = ""; }; - 48BA2E1C15129ACE00F58A27 /* README */ = {isa = PBXFileReference; lastKnownFileType = text; name = README; path = Plugins/README; sourceTree = ""; }; - 48BA2E1D15129ACE00F58A27 /* verify.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = verify.sh; sourceTree = ""; }; - 48BA2E2415129AEB00F58A27 /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = ""; }; - 48BA2E2615129B0800F58A27 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = index.html; path = ../../../index.html; sourceTree = ""; }; - 48BA2E2715129B0800F58A27 /* master.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = master.css; path = ../../../master.css; sourceTree = ""; }; - 48BA2E2915129B0800F58A27 /* libPayPalMEP.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libPayPalMEP.a; sourceTree = ""; }; - 48BA2E2A15129B0800F58A27 /* MEPAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MEPAddress.h; sourceTree = ""; }; - 48BA2E2B15129B0800F58A27 /* MEPAmounts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MEPAmounts.h; sourceTree = ""; }; - 48BA2E2C15129B0800F58A27 /* PayPal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PayPal.h; sourceTree = ""; }; - 48BA2E2D15129B0800F58A27 /* PayPalContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PayPalContext.h; sourceTree = ""; }; - 48BA2E2E15129B0800F58A27 /* PayPalMEPPayment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PayPalMEPPayment.h; sourceTree = ""; }; - 48BA2E2F15129B0800F58A27 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README.md; path = ../../../README.md; sourceTree = ""; }; - 48BA2E3015129B0800F58A27 /* SAiOSPaypalPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SAiOSPaypalPlugin.h; path = ../../../SAiOSPaypalPlugin.h; sourceTree = ""; }; - 48BA2E3215129B0800F58A27 /* SAiOSPaypalPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SAiOSPaypalPlugin.m; path = ../../../SAiOSPaypalPlugin.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 48BA2DC215129ACE00F58A27 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 48BA2DCD15129ACE00F58A27 /* Foundation.framework in Frameworks */, - 48BA2DCF15129ACE00F58A27 /* UIKit.framework in Frameworks */, - 48BA2DD115129ACE00F58A27 /* CoreGraphics.framework in Frameworks */, - 48BA2DD315129ACE00F58A27 /* AddressBook.framework in Frameworks */, - 48BA2DD515129ACE00F58A27 /* AddressBookUI.framework in Frameworks */, - 48BA2DD715129ACE00F58A27 /* AudioToolbox.framework in Frameworks */, - 48BA2DD915129ACE00F58A27 /* AVFoundation.framework in Frameworks */, - 48BA2DDB15129ACE00F58A27 /* CoreLocation.framework in Frameworks */, - 48BA2DDD15129ACE00F58A27 /* MediaPlayer.framework in Frameworks */, - 48BA2DDF15129ACE00F58A27 /* QuartzCore.framework in Frameworks */, - 48BA2DE115129ACE00F58A27 /* SystemConfiguration.framework in Frameworks */, - 48BA2DE315129ACE00F58A27 /* MobileCoreServices.framework in Frameworks */, - 48BA2DE515129ACE00F58A27 /* CoreMedia.framework in Frameworks */, - 48BA2DF015129ACE00F58A27 /* Cordova.framework in Frameworks */, - 48BA2E3515129B0800F58A27 /* libPayPalMEP.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 48BA2DC515129ACE00F58A27 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 48BA2DBA15129ACE00F58A27 = { - isa = PBXGroup; - children = ( - 48BA2E2415129AEB00F58A27 /* www */, - 48BA2DE615129ACE00F58A27 /* PayPalExampleCDV */, - 48BA2DCB15129ACE00F58A27 /* Frameworks */, - 48BA2DC915129ACE00F58A27 /* Products */, - ); - sourceTree = ""; - }; - 48BA2DC915129ACE00F58A27 /* Products */ = { - isa = PBXGroup; - children = ( - 48BA2DC815129ACE00F58A27 /* PayPalExampleCDV.app */, - ); - name = Products; - sourceTree = ""; - }; - 48BA2DCB15129ACE00F58A27 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 48BA2DCC15129ACE00F58A27 /* Foundation.framework */, - 48BA2DCE15129ACE00F58A27 /* UIKit.framework */, - 48BA2DD015129ACE00F58A27 /* CoreGraphics.framework */, - 48BA2DD215129ACE00F58A27 /* AddressBook.framework */, - 48BA2DD415129ACE00F58A27 /* AddressBookUI.framework */, - 48BA2DD615129ACE00F58A27 /* AudioToolbox.framework */, - 48BA2DD815129ACE00F58A27 /* AVFoundation.framework */, - 48BA2DDA15129ACE00F58A27 /* CoreLocation.framework */, - 48BA2DDC15129ACE00F58A27 /* MediaPlayer.framework */, - 48BA2DDE15129ACE00F58A27 /* QuartzCore.framework */, - 48BA2DE015129ACE00F58A27 /* SystemConfiguration.framework */, - 48BA2DE215129ACE00F58A27 /* MobileCoreServices.framework */, - 48BA2DE415129ACE00F58A27 /* CoreMedia.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 48BA2DE615129ACE00F58A27 /* PayPalExampleCDV */ = { - isa = PBXGroup; - children = ( - 48BA2DEF15129ACE00F58A27 /* Cordova.framework */, - 48BA2DF115129ACE00F58A27 /* Resources */, - 48BA2E1215129ACE00F58A27 /* Classes */, - 48BA2E1B15129ACE00F58A27 /* Plugins */, - 48BA2DE715129ACE00F58A27 /* Supporting Files */, - ); - path = PayPalExampleCDV; - sourceTree = ""; - }; - 48BA2DE715129ACE00F58A27 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 48BA2DE815129ACE00F58A27 /* PayPalExampleCDV-Info.plist */, - 48BA2DE915129ACE00F58A27 /* InfoPlist.strings */, - 48BA2DEC15129ACE00F58A27 /* main.m */, - 48BA2DEE15129ACE00F58A27 /* PayPalExampleCDV-Prefix.pch */, - 48BA2E1015129ACE00F58A27 /* Cordova.plist */, - 48BA2E1915129ACE00F58A27 /* MainViewController.xib */, - 48BA2E1D15129ACE00F58A27 /* verify.sh */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 48BA2DF115129ACE00F58A27 /* Resources */ = { - isa = PBXGroup; - children = ( - 48BA2E0E15129ACE00F58A27 /* Capture.bundle */, - 48BA2DF215129ACE00F58A27 /* en.lproj */, - 48BA2DF615129ACE00F58A27 /* es.lproj */, - 48BA2DFA15129ACE00F58A27 /* de.lproj */, - 48BA2DFE15129ACE00F58A27 /* se.lproj */, - 48BA2E0215129ACE00F58A27 /* icons */, - 48BA2E0915129ACE00F58A27 /* splash */, - ); - name = Resources; - sourceTree = ""; - }; - 48BA2DF215129ACE00F58A27 /* en.lproj */ = { - isa = PBXGroup; - children = ( - 48BA2DF315129ACE00F58A27 /* Localizable.strings */, - ); - name = en.lproj; - sourceTree = ""; - }; - 48BA2DF615129ACE00F58A27 /* es.lproj */ = { - isa = PBXGroup; - children = ( - 48BA2DF715129ACE00F58A27 /* Localizable.strings */, - ); - name = es.lproj; - sourceTree = ""; - }; - 48BA2DFA15129ACE00F58A27 /* de.lproj */ = { - isa = PBXGroup; - children = ( - 48BA2DFB15129ACE00F58A27 /* Localizable.strings */, - ); - name = de.lproj; - sourceTree = ""; - }; - 48BA2DFE15129ACE00F58A27 /* se.lproj */ = { - isa = PBXGroup; - children = ( - 48BA2DFF15129ACE00F58A27 /* Localizable.strings */, - ); - name = se.lproj; - sourceTree = ""; - }; - 48BA2E0215129ACE00F58A27 /* icons */ = { - isa = PBXGroup; - children = ( - 48BA2E0315129ACE00F58A27 /* icon.png */, - 48BA2E0515129ACE00F58A27 /* icon@2x.png */, - 48BA2E0715129ACE00F58A27 /* icon-72.png */, - ); - name = icons; - sourceTree = ""; - }; - 48BA2E0915129ACE00F58A27 /* splash */ = { - isa = PBXGroup; - children = ( - 48BA2E0A15129ACE00F58A27 /* Default.png */, - 48BA2E0C15129ACE00F58A27 /* Default@2x.png */, - ); - name = splash; - sourceTree = ""; - }; - 48BA2E1215129ACE00F58A27 /* Classes */ = { - isa = PBXGroup; - children = ( - 48BA2E1315129ACE00F58A27 /* AppDelegate.h */, - 48BA2E1415129ACE00F58A27 /* AppDelegate.m */, - 48BA2E1615129ACE00F58A27 /* MainViewController.h */, - 48BA2E1715129ACE00F58A27 /* MainViewController.m */, - ); - name = Classes; - sourceTree = ""; - }; - 48BA2E1B15129ACE00F58A27 /* Plugins */ = { - isa = PBXGroup; - children = ( - 48BA2E2615129B0800F58A27 /* index.html */, - 48BA2E2715129B0800F58A27 /* master.css */, - 48BA2E2815129B0800F58A27 /* PayPal_MobilePayments_Library */, - 48BA2E2F15129B0800F58A27 /* README.md */, - 48BA2E3015129B0800F58A27 /* SAiOSPaypalPlugin.h */, - 48BA2E3215129B0800F58A27 /* SAiOSPaypalPlugin.m */, - 48BA2E1C15129ACE00F58A27 /* README */, - ); - name = Plugins; - sourceTree = ""; - }; - 48BA2E2815129B0800F58A27 /* PayPal_MobilePayments_Library */ = { - isa = PBXGroup; - children = ( - 48BA2E2915129B0800F58A27 /* libPayPalMEP.a */, - 48BA2E2A15129B0800F58A27 /* MEPAddress.h */, - 48BA2E2B15129B0800F58A27 /* MEPAmounts.h */, - 48BA2E2C15129B0800F58A27 /* PayPal.h */, - 48BA2E2D15129B0800F58A27 /* PayPalContext.h */, - 48BA2E2E15129B0800F58A27 /* PayPalMEPPayment.h */, - ); - name = PayPal_MobilePayments_Library; - path = ../../../PayPal_MobilePayments_Library; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 48BA2DC715129ACE00F58A27 /* PayPalExampleCDV */ = { - isa = PBXNativeTarget; - buildConfigurationList = 48BA2E2115129ACE00F58A27 /* Build configuration list for PBXNativeTarget "PayPalExampleCDV" */; - buildPhases = ( - 48BA2DC115129ACE00F58A27 /* Sources */, - 48BA2DC215129ACE00F58A27 /* Frameworks */, - 48BA2DC315129ACE00F58A27 /* Resources */, - 48BA2DC415129ACE00F58A27 /* Sources */, - 48BA2DC515129ACE00F58A27 /* Frameworks */, - 48BA2DC615129ACE00F58A27 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = PayPalExampleCDV; - productName = PayPalExampleCDV; - productReference = 48BA2DC815129ACE00F58A27 /* PayPalExampleCDV.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 48BA2DBC15129ACE00F58A27 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0430; - ORGANIZATIONNAME = OpenOSX.org; - }; - buildConfigurationList = 48BA2DBF15129ACE00F58A27 /* Build configuration list for PBXProject "PayPalExampleCDV" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - es, - de, - se, - ); - mainGroup = 48BA2DBA15129ACE00F58A27; - productRefGroup = 48BA2DC915129ACE00F58A27 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 48BA2DC715129ACE00F58A27 /* PayPalExampleCDV */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 48BA2DC315129ACE00F58A27 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 48BA2DEB15129ACE00F58A27 /* InfoPlist.strings in Resources */, - 48BA2DF515129ACE00F58A27 /* Localizable.strings in Resources */, - 48BA2DF915129ACE00F58A27 /* Localizable.strings in Resources */, - 48BA2DFD15129ACE00F58A27 /* Localizable.strings in Resources */, - 48BA2E0115129ACE00F58A27 /* Localizable.strings in Resources */, - 48BA2E0415129ACE00F58A27 /* icon.png in Resources */, - 48BA2E0615129ACE00F58A27 /* icon@2x.png in Resources */, - 48BA2E0815129ACE00F58A27 /* icon-72.png in Resources */, - 48BA2E0B15129ACE00F58A27 /* Default.png in Resources */, - 48BA2E0D15129ACE00F58A27 /* Default@2x.png in Resources */, - 48BA2E0F15129ACE00F58A27 /* Capture.bundle in Resources */, - 48BA2E1115129ACE00F58A27 /* Cordova.plist in Resources */, - 48BA2E1A15129ACE00F58A27 /* MainViewController.xib in Resources */, - 48BA2E1E15129ACE00F58A27 /* verify.sh in Resources */, - 48BA2E2515129AEB00F58A27 /* www in Resources */, - 48BA2E3315129B0800F58A27 /* index.html in Resources */, - 48BA2E3415129B0800F58A27 /* master.css in Resources */, - 48BA2E3615129B0800F58A27 /* README.md in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 48BA2DC615129ACE00F58A27 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "\n\t\t\t\t\t\t\t\tchmod 755 $PROJECT_DIR/$PROJECT_NAME/verify.sh\n\t\t\t\t\t\t\t\t$PROJECT_DIR/$PROJECT_NAME/verify.sh\n\t\t\t\t\t"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 48BA2DC115129ACE00F58A27 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 48BA2DED15129ACE00F58A27 /* main.m in Sources */, - 48BA2E1515129ACE00F58A27 /* AppDelegate.m in Sources */, - 48BA2E1815129ACE00F58A27 /* MainViewController.m in Sources */, - 48BA2E3815129B0800F58A27 /* SAiOSPaypalPlugin.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 48BA2DC415129ACE00F58A27 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 48BA2DE915129ACE00F58A27 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 48BA2DEA15129ACE00F58A27 /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - 48BA2DF315129ACE00F58A27 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 48BA2DF415129ACE00F58A27 /* en */, - ); - name = Localizable.strings; - sourceTree = ""; - }; - 48BA2DF715129ACE00F58A27 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 48BA2DF815129ACE00F58A27 /* es */, - ); - name = Localizable.strings; - sourceTree = ""; - }; - 48BA2DFB15129ACE00F58A27 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 48BA2DFC15129ACE00F58A27 /* de */, - ); - name = Localizable.strings; - sourceTree = ""; - }; - 48BA2DFF15129ACE00F58A27 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 48BA2E0015129ACE00F58A27 /* se */, - ); - name = Localizable.strings; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 48BA2E1F15129ACE00F58A27 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = ( - armv6, - "$(ARCHS_STANDARD_32_BIT)", - ); - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 3.0; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 48BA2E2015129ACE00F58A27 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = ( - armv6, - "$(ARCHS_STANDARD_32_BIT)", - ); - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 3.0; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 48BA2E2215129ACE00F58A27 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = /Users/Shared/Cordova/Frameworks; - GCC_DYNAMIC_NO_PIC = NO; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "PayPalExampleCDV/PayPalExampleCDV-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1,", - "CORDOVA_FRAMEWORK=1", - ); - INFOPLIST_FILE = "PayPalExampleCDV/PayPalExampleCDV-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/../../PayPal_MobilePayments_Library\"", - ); - OTHER_LDFLAGS = ( - "-weak_framework", - UIKit, - "-weak_framework", - AVFoundation, - "-weak_framework", - CoreMedia, - "-weak_library", - /usr/lib/libSystem.B.dylib, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - 48BA2E2315129ACE00F58A27 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - FRAMEWORK_SEARCH_PATHS = /Users/Shared/Cordova/Frameworks; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "PayPalExampleCDV/PayPalExampleCDV-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "NDEBUG=1,", - "CORDOVA_FRAMEWORK=1", - ); - INFOPLIST_FILE = "PayPalExampleCDV/PayPalExampleCDV-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/../../PayPal_MobilePayments_Library\"", - ); - OTHER_LDFLAGS = ( - "-weak_framework", - UIKit, - "-weak_framework", - AVFoundation, - "-weak_framework", - CoreMedia, - "-weak_library", - /usr/lib/libSystem.B.dylib, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 48BA2DBF15129ACE00F58A27 /* Build configuration list for PBXProject "PayPalExampleCDV" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 48BA2E1F15129ACE00F58A27 /* Debug */, - 48BA2E2015129ACE00F58A27 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 48BA2E2115129ACE00F58A27 /* Build configuration list for PBXNativeTarget "PayPalExampleCDV" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 48BA2E2215129ACE00F58A27 /* Debug */, - 48BA2E2315129ACE00F58A27 /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; -/* End XCConfigurationList section */ - }; - rootObject = 48BA2DBC15129ACE00F58A27 /* Project object */; -} diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.xcworkspace/xcuserdata/randymcmillan.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.xcworkspace/xcuserdata/randymcmillan.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 756bd57..0000000 Binary files a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV.xcodeproj/project.xcworkspace/xcuserdata/randymcmillan.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.m b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.m deleted file mode 100644 index a1c4368..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Classes/MainViewController.m +++ /dev/null @@ -1,70 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. - */ - -// -// MainViewController.h -// PayPalExampleCDV -// -// Created by Randy McMillan on 3/15/12. -// Copyright OpenOSX.org 2012. All rights reserved. -// - -#import "MainViewController.h" - -@implementation MainViewController - -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil -{ - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - // Custom initialization - } - return self; -} - -- (void)didReceiveMemoryWarning -{ - // Releases the view if it doesn't have a superview. - [super didReceiveMemoryWarning]; - - // Release any cached data, images, etc that aren't in use. -} - -#pragma mark - View lifecycle - -- (void)viewDidLoad -{ - [super viewDidLoad]; - // Do any additional setup after loading the view from its nib. -} - -- (void)viewDidUnload -{ - [super viewDidUnload]; - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Return YES for supported orientations - return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; -} - -@end diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/PayPalExampleCDV-Prefix.pch b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/PayPalExampleCDV-Prefix.pch deleted file mode 100644 index 45ac70b..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/PayPalExampleCDV-Prefix.pch +++ /dev/null @@ -1,7 +0,0 @@ -// -// Prefix header for all source files of the 'PayPalExampleCDV' target in the 'PayPalExampleCDV' project -// - -#ifdef __OBJC__ - #import -#endif diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Plugins/README b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Plugins/README deleted file mode 100644 index 438840d..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Plugins/README +++ /dev/null @@ -1 +0,0 @@ -Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder. \ No newline at end of file diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/splash/Default.png b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/splash/Default.png deleted file mode 100644 index 644f380..0000000 Binary files a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/splash/Default.png and /dev/null differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/splash/Default@2x.png b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/splash/Default@2x.png deleted file mode 100644 index 02d6e35..0000000 Binary files a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/Resources/splash/Default@2x.png and /dev/null differ diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/en.lproj/InfoPlist.strings b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/en.lproj/InfoPlist.strings deleted file mode 100644 index 477b28f..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/verify.sh b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/verify.sh deleted file mode 100755 index 0f6f037..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/PayPalExampleCDV/verify.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - - -if [ ! -d "$PROJECT_DIR/www" ] ; then - cp -R /Users/Shared/Cordova/Frameworks/Cordova.framework/www "$PROJECT_DIR" -fi -# detect www folder reference in project, if missing, print warning -grep "{isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = \"\"; };" "$PROJECT_DIR/$PROJECT_NAME.xcodeproj/project.pbxproj" -rc=$? -if [ $rc != 0 ] ; then -echo -e "warning: Missing - Add $PROJECT_DIR/www as a folder reference in your project. Just drag and drop the folder into your project, into the Project Navigator of Xcode 4. Make sure you select the second radio-button: 'Create folder references for any added folders' (which will create a blue folder)" 1>&2 -fi \ No newline at end of file diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/SAiOSPaypalPlugin.js b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/SAiOSPaypalPlugin.js deleted file mode 100644 index 4f883e5..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/SAiOSPaypalPlugin.js +++ /dev/null @@ -1,121 +0,0 @@ -// ////////////////////////////////////// -// Paypal PhoneGap Plugin -// by Shazron Abdullah -// Oct 8th 2010 -// - -/* - * buttonType (unused currently) - */ -var PayPalButtonType = { - 'BUTTON_68x24' : 0, - 'BUTTON_68x33' : 1, - 'BUTTON_118x24': 2, - 'BUTTON_152x33': 3, - 'BUTTON_194x37': 4, - 'BUTTON_278x43': 5, - 'BUTTON_294x43': 6, - 'BUTTON_TYPE_COUNT': 7 -}; - -/* - * PaymentType for window.plugins.prepare - */ -var PayPalPaymentType = -{ - 'HARD_GOODS': 0, - 'SERVICE' : 1, - 'PERSONAL' : 2, - 'DONATION' : 3 -}; - -/* - * errorType for PaypalPaymentEvent.Failed - */ -var PayPalFailureType = -{ - 'SYSTEM_ERROR' : 0, - 'RECIPIENT_ERROR' : 1, - 'APPLICATION_ERROR' : 2, - 'CONSUMER_ERROR' : 3 -}; - -/* - * Events to listen to after a user touches the payment button - */ -var PaypalPaymentEvent = -{ - /** - * Listen for this event to signify Paypal payment success. The event object will have these properties: - * transactionID - a string value - */ - Success : "PaypalPaymentEvent.Success", - /** - * Listen for this event to signify Paypal payment canceled. The event object will have these properties: - * [no properties available] - */ - Canceled : "PaypalPaymentEvent.Canceled", - /** - * Listen for this event to signify Paypal payment failed. The event object will have these properties: - * errorType - an integer value - */ - Failed : "PaypalPaymentEvent.Failed" -}; - -/** - * Constructor - */ -function SAiOSPaypalPlugin() -{ -} - -/** - * Prepare payment type - * paymentType is from the PayPalPaymentType enum - */ -SAiOSPaypalPlugin.prototype.prepare = function(paymentType) -{ - Cordova.exec("SAiOSPaypalPlugin.prepare", paymentType); -} - -/** - * Initiate payment - */ -SAiOSPaypalPlugin.prototype.pay = function() -{ - Cordova.exec("SAiOSPaypalPlugin.pay"); -} - -/** - * Sets the payment information for when the Paypal button is clicked. - * Takes in an object (paymentProperties). Properties available to be set: - * paymentCurrency - a string value (required) - * paymentAmount - a double value (required) - * itemDesc - a string value (required) - * recipient - a string value (e-mail address, required) - * merchantName - a string value (required) - */ -SAiOSPaypalPlugin.prototype.setPaymentInfo = function(paymentProperties) -{ - Cordova.exec("SAiOSPaypalPlugin.setPaymentInfo", paymentProperties); -} - - - -/** - * Install function - */ -SAiOSPaypalPlugin.install = function() -{ - if ( !window.plugins ) { - window.plugins = {}; - } - if ( !window.plugins.paypal ) { - window.plugins.paypal = new SAiOSPaypalPlugin(); - } -} - -/** - * Add to PhoneGap constructor - */ -Cordova.addConstructor(SAiOSPaypalPlugin.install); \ No newline at end of file diff --git a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/cordova-1.5.0.js b/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/cordova-1.5.0.js deleted file mode 100644 index 36c2383..0000000 --- a/iOS/PayPalPlugin/.EXAMPLE/PayPalExampleCDV/www/cordova-1.5.0.js +++ /dev/null @@ -1,4135 +0,0 @@ -/* Cordova v1.5.0 */ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ - - -/* - * Some base contributions - * Copyright (c) 2011, Proyectos Equis Ka, S.L. - */ - -if (typeof Cordova === "undefined") { - -if (typeof(DeviceInfo) !== 'object'){ - DeviceInfo = {}; -} -/** - * This represents the Cordova API itself, and provides a global namespace for accessing - * information about the state of Cordova. - * @class - */ -Cordova = { - // This queue holds the currently executing command and all pending - // commands executed with Cordova.exec(). - commandQueue: [], - // Indicates if we're currently in the middle of flushing the command - // queue on the native side. - commandQueueFlushing: false, - _constructors: [], - documentEventHandler: {}, // Collection of custom document event handlers - windowEventHandler: {} -}; - -/** - * List of resource files loaded by Cordova. - * This is used to ensure JS and other files are loaded only once. - */ -Cordova.resources = {base: true}; - -/** - * Determine if resource has been loaded by Cordova - * - * @param name - * @return - */ -Cordova.hasResource = function(name) { - return Cordova.resources[name]; -}; - -/** - * Add a resource to list of loaded resources by Cordova - * - * @param name - */ -Cordova.addResource = function(name) { - Cordova.resources[name] = true; -}; - -/** - * Boolean flag indicating if the Cordova API is available and initialized. - */ // TODO: Remove this, it is unused here ... -jm -Cordova.available = DeviceInfo.uuid != undefined; - -/** - * Add an initialization function to a queue that ensures it will run and initialize - * application constructors only once Cordova has been initialized. - * @param {Function} func The function callback you want run once Cordova is initialized - */ -Cordova.addConstructor = function(func) { - var state = document.readyState; - if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null ) - { - func(); - } - else - { - Cordova._constructors.push(func); - } -}; - -(function() - { - var timer = setInterval(function() - { - - var state = document.readyState; - - if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null ) - { - clearInterval(timer); // stop looking - // run our constructors list - while (Cordova._constructors.length > 0) - { - var constructor = Cordova._constructors.shift(); - try - { - constructor(); - } - catch(e) - { - if (typeof(console['log']) == 'function') - { - console.log("Failed to run constructor: " + console.processMessage(e)); - } - else - { - alert("Failed to run constructor: " + e.message); - } - } - } - // all constructors run, now fire the deviceready event - var e = document.createEvent('Events'); - e.initEvent('deviceready'); - document.dispatchEvent(e); - } - }, 1); -})(); - -// session id for calls -Cordova.sessionKey = 0; - -// centralized callbacks -Cordova.callbackId = 0; -Cordova.callbacks = {}; -Cordova.callbackStatus = { - NO_RESULT: 0, - OK: 1, - CLASS_NOT_FOUND_EXCEPTION: 2, - ILLEGAL_ACCESS_EXCEPTION: 3, - INSTANTIATION_EXCEPTION: 4, - MALFORMED_URL_EXCEPTION: 5, - IO_EXCEPTION: 6, - INVALID_ACTION: 7, - JSON_EXCEPTION: 8, - ERROR: 9 - }; - -/** - * Creates a gap bridge iframe used to notify the native code about queued - * commands. - * - * @private - */ -Cordova.createGapBridge = function() { - gapBridge = document.createElement("iframe"); - gapBridge.setAttribute("style", "display:none;"); - gapBridge.setAttribute("height","0px"); - gapBridge.setAttribute("width","0px"); - gapBridge.setAttribute("frameborder","0"); - document.documentElement.appendChild(gapBridge); - return gapBridge; -} - -/** - * Execute a Cordova command by queuing it and letting the native side know - * there are queued commands. The native side will then request all of the - * queued commands and execute them. - * - * Arguments may be in one of two formats: - * - * FORMAT ONE (preferable) - * The native side will call Cordova.callbackSuccess or - * Cordova.callbackError, depending upon the result of the action. - * - * @param {Function} success The success callback - * @param {Function} fail The fail callback - * @param {String} service The name of the service to use - * @param {String} action The name of the action to use - * @param {String[]} [args] Zero or more arguments to pass to the method - * - * FORMAT TWO - * @param {String} command Command to be run in Cordova, e.g. - * "ClassName.method" - * @param {String[]} [args] Zero or more arguments to pass to the method - * object parameters are passed as an array object - * [object1, object2] each object will be passed as - * JSON strings - */ -Cordova.exec = function() { - if (!Cordova.available) { - alert("ERROR: Attempting to call Cordova.exec()" - +" before 'deviceready'. Ignoring."); - return; - } - - var successCallback, failCallback, service, action, actionArgs; - var callbackId = null; - if (typeof arguments[0] !== "string") { - // FORMAT ONE - successCallback = arguments[0]; - failCallback = arguments[1]; - service = arguments[2]; - action = arguments[3]; - actionArgs = arguments[4]; - - // Since we need to maintain backwards compatibility, we have to pass - // an invalid callbackId even if no callback was provided since plugins - // will be expecting it. The Cordova.exec() implementation allocates - // an invalid callbackId and passes it even if no callbacks were given. - callbackId = 'INVALID'; - } else { - // FORMAT TWO - splitCommand = arguments[0].split("."); - action = splitCommand.pop(); - service = splitCommand.join("."); - actionArgs = Array.prototype.splice.call(arguments, 1); - } - - // Start building the command object. - var command = { - className: service, - methodName: action, - arguments: [] - }; - - // Register the callbacks and add the callbackId to the positional - // arguments if given. - if (successCallback || failCallback) { - callbackId = service + Cordova.callbackId++; - Cordova.callbacks[callbackId] = - {success:successCallback, fail:failCallback}; - } - if (callbackId != null) { - command.arguments.push(callbackId); - } - - for (var i = 0; i < actionArgs.length; ++i) { - var arg = actionArgs[i]; - if (arg == undefined || arg == null) { - continue; - } else if (typeof(arg) == 'object') { - command.options = arg; - } else { - command.arguments.push(arg); - } - } - - // Stringify and queue the command. We stringify to command now to - // effectively clone the command arguments in case they are mutated before - // the command is executed. - Cordova.commandQueue.push(JSON.stringify(command)); - - // If the queue length is 1, then that means it was empty before we queued - // the given command, so let the native side know that we have some - // commands to execute, unless the queue is currently being flushed, in - // which case the command will be picked up without notification. - if (Cordova.commandQueue.length == 1 && !Cordova.commandQueueFlushing) { - if (!Cordova.gapBridge) { - Cordova.gapBridge = Cordova.createGapBridge(); - } - - Cordova.gapBridge.src = "gap://ready"; - } -} - -/** - * Called by native code to retrieve all queued commands and clear the queue. - */ -Cordova.getAndClearQueuedCommands = function() { - json = JSON.stringify(Cordova.commandQueue); - Cordova.commandQueue = []; - return json; -} - -/** - * Called by native code when returning successful result from an action. - * - * @param callbackId - * @param args - * args.status - Cordova.callbackStatus - * args.message - return value - * args.keepCallback - 0 to remove callback, 1 to keep callback in Cordova.callbacks[] - */ -Cordova.callbackSuccess = function(callbackId, args) { - if (Cordova.callbacks[callbackId]) { - - // If result is to be sent to callback - if (args.status == Cordova.callbackStatus.OK) { - try { - if (Cordova.callbacks[callbackId].success) { - Cordova.callbacks[callbackId].success(args.message); - } - } - catch (e) { - console.log("Error in success callback: "+callbackId+" = "+e); - } - } - - // Clear callback if not expecting any more results - if (!args.keepCallback) { - delete Cordova.callbacks[callbackId]; - } - } -}; - -/** - * Called by native code when returning error result from an action. - * - * @param callbackId - * @param args - */ -Cordova.callbackError = function(callbackId, args) { - if (Cordova.callbacks[callbackId]) { - try { - if (Cordova.callbacks[callbackId].fail) { - Cordova.callbacks[callbackId].fail(args.message); - } - } - catch (e) { - console.log("Error in error callback: "+callbackId+" = "+e); - } - - // Clear callback if not expecting any more results - if (!args.keepCallback) { - delete Cordova.callbacks[callbackId]; - } - } -}; - - -/** - * Does a deep clone of the object. - * - * @param obj - * @return - */ -Cordova.clone = function(obj) { - if(!obj) { - return obj; - } - - if(obj instanceof Array){ - var retVal = new Array(); - for(var i = 0; i < obj.length; ++i){ - retVal.push(Cordova.clone(obj[i])); - } - return retVal; - } - - if (obj instanceof Function) { - return obj; - } - - if(!(obj instanceof Object)){ - return obj; - } - - if (obj instanceof Date) { - return obj; - } - - retVal = new Object(); - for(i in obj){ - if(!(i in retVal) || retVal[i] != obj[i]) { - retVal[i] = Cordova.clone(obj[i]); - } - } - return retVal; -}; - -// Intercept calls to document.addEventListener -Cordova.m_document_addEventListener = document.addEventListener; - -// Intercept calls to window.addEventListener -Cordova.m_window_addEventListener = window.addEventListener; - -/** - * Add a custom window event handler. - * - * @param {String} event The event name that callback handles - * @param {Function} callback The event handler - */ -Cordova.addWindowEventHandler = function(event, callback) { - Cordova.windowEventHandler[event] = callback; -} - -/** - * Add a custom document event handler. - * - * @param {String} event The event name that callback handles - * @param {Function} callback The event handler - */ -Cordova.addDocumentEventHandler = function(event, callback) { - Cordova.documentEventHandler[event] = callback; -} - -/** - * Intercept adding document event listeners and handle our own - * - * @param {Object} evt - * @param {Function} handler - * @param capture - */ -document.addEventListener = function(evt, handler, capture) { - var e = evt.toLowerCase(); - - // If subscribing to an event that is handled by a plugin - if (typeof Cordova.documentEventHandler[e] !== "undefined") { - if (Cordova.documentEventHandler[e](e, handler, true)) { - return; // Stop default behavior - } - } - - Cordova.m_document_addEventListener.call(document, evt, handler, capture); -}; - -/** - * Intercept adding window event listeners and handle our own - * - * @param {Object} evt - * @param {Function} handler - * @param capture - */ -window.addEventListener = function(evt, handler, capture) { - var e = evt.toLowerCase(); - - // If subscribing to an event that is handled by a plugin - if (typeof Cordova.windowEventHandler[e] !== "undefined") { - if (Cordova.windowEventHandler[e](e, handler, true)) { - return; // Stop default behavior - } - } - - Cordova.m_window_addEventListener.call(window, evt, handler, capture); -}; - -// Intercept calls to document.removeEventListener and watch for events that -// are generated by Cordova native code -Cordova.m_document_removeEventListener = document.removeEventListener; - -// Intercept calls to window.removeEventListener -Cordova.m_window_removeEventListener = window.removeEventListener; - -/** - * Intercept removing document event listeners and handle our own - * - * @param {Object} evt - * @param {Function} handler - * @param capture - */ -document.removeEventListener = function(evt, handler, capture) { - var e = evt.toLowerCase(); - - // If unsubcribing from an event that is handled by a plugin - if (typeof Cordova.documentEventHandler[e] !== "undefined") { - if (Cordova.documentEventHandler[e](e, handler, false)) { - return; // Stop default behavior - } - } - - Cordova.m_document_removeEventListener.call(document, evt, handler, capture); -}; - -/** - * Intercept removing window event listeners and handle our own - * - * @param {Object} evt - * @param {Function} handler - * @param capture - */ -window.removeEventListener = function(evt, handler, capture) { - var e = evt.toLowerCase(); - - // If unsubcribing from an event that is handled by a plugin - if (typeof Cordova.windowEventHandler[e] !== "undefined") { - if (Cordova.windowEventHandler[e](e, handler, false)) { - return; // Stop default behavior - } - } - - Cordova.m_window_removeEventListener.call(window, evt, handler, capture); -}; - -/** - * Method to fire document event - * - * @param {String} type The event type to fire - * @param {Object} data Data to send with event - */ -Cordova.fireDocumentEvent = function(type, data) { - var e = document.createEvent('Events'); - e.initEvent(type); - if (data) { - for (var i in data) { - e[i] = data[i]; - } - } - document.dispatchEvent(e); -}; - -/** - * Method to fire window event - * - * @param {String} type The event type to fire - * @param {Object} data Data to send with event - */ -Cordova.fireWindowEvent = function(type, data) { - var e = document.createEvent('Events'); - e.initEvent(type); - if (data) { - for (var i in data) { - e[i] = data[i]; - } - } - window.dispatchEvent(e); -}; - -/** - * Method to fire event from native code - * Leaving this generic version to handle problems with iOS 3.x. Is currently used by orientation and battery events - * Remove when iOS 3.x no longer supported and call fireWindowEvent or fireDocumentEvent directly - */ -Cordova.fireEvent = function(type, target, data) { - var e = document.createEvent('Events'); - e.initEvent(type); - if (data) { - for (var i in data) { - e[i] = data[i]; - } - } - target = target || document; - if (target.dispatchEvent === undefined) { // ie window.dispatchEvent is undefined in iOS 3.x - target = document; - } - - target.dispatchEvent(e); -}; -/** - * Create a UUID - * - * @return - */ -Cordova.createUUID = function() { - return Cordova.UUIDcreatePart(4) + '-' + - Cordova.UUIDcreatePart(2) + '-' + - Cordova.UUIDcreatePart(2) + '-' + - Cordova.UUIDcreatePart(2) + '-' + - Cordova.UUIDcreatePart(6); -}; - -Cordova.UUIDcreatePart = function(length) { - var uuidpart = ""; - for (var i=0; i -1) { - me._batteryListener.splice(pos, 1); - } - } else if (eventType === "batterylow") { - var pos = me._lowListener.indexOf(handler); - if (pos > -1) { - me._lowListener.splice(pos, 1); - } - } else if (eventType === "batterycritical") { - var pos = me._criticalListener.indexOf(handler); - if (pos > -1) { - me._criticalListener.splice(pos, 1); - } - } - - // If there are no more registered event listeners stop the battery listener on native side. - if (me._batteryListener.length === 0 && me._lowListener.length === 0 && me._criticalListener.length === 0) { - Cordova.exec(null, null, "org.apache.cordova.battery", "stop", []); - } - } -}; - -/** - * Callback for battery status - * - * @param {Object} info keys: level, isPlugged - */ -Battery.prototype._status = function(info) { - if (info) { - var me = this; - if (me._level != info.level || me._isPlugged != info.isPlugged) { - // Fire batterystatus event - //Cordova.fireWindowEvent("batterystatus", info); - // use this workaround since iOS 3.x does have window.dispatchEvent - Cordova.fireEvent("batterystatus", window, info); - - // Fire low battery event - if (info.level == 20 || info.level == 5) { - if (info.level == 20) { - //Cordova.fireWindowEvent("batterylow", info); - // use this workaround since iOS 3.x does not have window.dispatchEvent - Cordova.fireEvent("batterylow", window, info); - } - else { - //Cordova.fireWindowEvent("batterycritical", info); - // use this workaround since iOS 3.x does not have window.dispatchEvent - Cordova.fireEvent("batterycritical", window, info); - } - } - } - me._level = info.level; - me._isPlugged = info.isPlugged; - } -}; - -/** - * Error callback for battery start - */ -Battery.prototype._error = function(e) { - console.log("Error initializing Battery: " + e); -}; - -Cordova.addConstructor(function() { - if (typeof navigator.battery === "undefined") { - navigator.battery = new Battery(); - Cordova.addWindowEventHandler("batterystatus", navigator.battery.eventHandler); - Cordova.addWindowEventHandler("batterylow", navigator.battery.eventHandler); - Cordova.addWindowEventHandler("batterycritical", navigator.battery.eventHandler); - } -}); -}if (!Cordova.hasResource("camera")) { - Cordova.addResource("camera"); - - -/** - * This class provides access to the device camera. - * @constructor - */ -Camera = function() { - -} -/** - * Available Camera Options - * {boolean} allowEdit - true to allow editing image, default = false - * {number} quality 0-100 (low to high) default = 100 - * {Camera.DestinationType} destinationType default = DATA_URL - * {Camera.PictureSourceType} sourceType default = CAMERA - * {number} targetWidth - width in pixels to scale image default = 0 (no scaling) - * {number} targetHeight - height in pixels to scale image default = 0 (no scaling) - * {Camera.EncodingType} - encodingType default = JPEG - * {boolean} correctOrientation - Rotate the image to correct for the orientation of the device during capture (iOS only) - * {boolean} saveToPhotoAlbum - Save the image to the photo album on the device after capture (iOS only) - */ -/** - * Format of image that is returned from getPicture. - * - * Example: navigator.camera.getPicture(success, fail, - * { quality: 80, - * destinationType: Camera.DestinationType.DATA_URL, - * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) - */ -Camera.DestinationType = { - DATA_URL: 0, // Return base64 encoded string - FILE_URI: 1 // Return file uri -}; -Camera.prototype.DestinationType = Camera.DestinationType; - -/** - * Source to getPicture from. - * - * Example: navigator.camera.getPicture(success, fail, - * { quality: 80, - * destinationType: Camera.DestinationType.DATA_URL, - * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) - */ -Camera.PictureSourceType = { - PHOTOLIBRARY : 0, // Choose image from picture library - CAMERA : 1, // Take picture from camera - SAVEDPHOTOALBUM : 2 // Choose image from picture library -}; -Camera.prototype.PictureSourceType = Camera.PictureSourceType; - -/** - * Encoding of image returned from getPicture. - * - * Example: navigator.camera.getPicture(success, fail, - * { quality: 80, - * destinationType: Camera.DestinationType.DATA_URL, - * sourceType: Camera.PictureSourceType.CAMERA, - * encodingType: Camera.EncodingType.PNG}) - */ -Camera.EncodingType = { - JPEG: 0, // Return JPEG encoded image - PNG: 1 // Return PNG encoded image -}; -Camera.prototype.EncodingType = Camera.EncodingType; - -/** - * Type of pictures to select from. Only applicable when - * PictureSourceType is PHOTOLIBRARY or SAVEDPHOTOALBUM - * - * Example: navigator.camera.getPicture(success, fail, - * { quality: 80, - * destinationType: Camera.DestinationType.DATA_URL, - * sourceType: Camera.PictureSourceType.PHOTOLIBRARY, - * mediaType: Camera.MediaType.PICTURE}) - */ -Camera.MediaType = { - PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType - VIDEO: 1, // allow selection of video only, ONLY RETURNS URL - ALLMEDIA : 2 // allow selection from all media types -}; -Camera.prototype.MediaType = Camera.MediaType; - -/** - * Gets a picture from source defined by "options.sourceType", and returns the - * image as defined by the "options.destinationType" option. - - * The defaults are sourceType=CAMERA and destinationType=DATA_URL. - * - * @param {Function} successCallback - * @param {Function} errorCallback - * @param {Object} options - */ -Camera.prototype.getPicture = function(successCallback, errorCallback, options) { - // successCallback required - if (typeof successCallback != "function") { - console.log("Camera Error: successCallback is not a function"); - return; - } - - // errorCallback optional - if (errorCallback && (typeof errorCallback != "function")) { - console.log("Camera Error: errorCallback is not a function"); - return; - } - - Cordova.exec(successCallback, errorCallback, "org.apache.cordova.camera","getPicture",[options]); -}; - - - -Cordova.addConstructor(function() { - if (typeof navigator.camera == "undefined") navigator.camera = new Camera(); -}); -}; - -if (!Cordova.hasResource("device")) { - Cordova.addResource("device"); - -/** - * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the - * phone, etc. - * @constructor - */ -Device = function() -{ - this.platform = null; - this.version = null; - this.name = null; - this.cordova = null; - this.uuid = null; - try - { - this.platform = DeviceInfo.platform; - this.version = DeviceInfo.version; - this.name = DeviceInfo.name; - this.cordova = DeviceInfo.gap; - this.uuid = DeviceInfo.uuid; - - } - catch(e) - { - // TODO: - } - this.available = Cordova.available = this.uuid != null; -} - -Cordova.addConstructor(function() { - if (typeof navigator.device === "undefined") { - navigator.device = window.device = new Device(); - } -}); -}; -if (!Cordova.hasResource("capture")) { - Cordova.addResource("capture"); -/** - * The CaptureError interface encapsulates all errors in the Capture API. - */ -function CaptureError() { - this.code = null; -}; - -// Capture error codes -CaptureError.CAPTURE_INTERNAL_ERR = 0; -CaptureError.CAPTURE_APPLICATION_BUSY = 1; -CaptureError.CAPTURE_INVALID_ARGUMENT = 2; -CaptureError.CAPTURE_NO_MEDIA_FILES = 3; -CaptureError.CAPTURE_NOT_SUPPORTED = 20; - -/** - * The Capture interface exposes an interface to the camera and microphone of the hosting device. - */ -function Capture() { - this.supportedAudioModes = []; - this.supportedImageModes = []; - this.supportedVideoModes = []; -}; - -/** - * Launch audio recorder application for recording audio clip(s). - * - * @param {Function} successCB - * @param {Function} errorCB - * @param {CaptureAudioOptions} options - * - * No audio recorder to launch for iOS - return CAPTURE_NOT_SUPPORTED - */ -Capture.prototype.captureAudio = function(successCallback, errorCallback, options) { - /*if (errorCallback && typeof errorCallback === "function") { - errorCallback({ - "code": CaptureError.CAPTURE_NOT_SUPPORTED - }); - }*/ - Cordova.exec(successCallback, errorCallback, "org.apache.cordova.mediacapture", "captureAudio", [options]); -}; - -/** - * Launch camera application for taking image(s). - * - * @param {Function} successCB - * @param {Function} errorCB - * @param {CaptureImageOptions} options - */ -Capture.prototype.captureImage = function(successCallback, errorCallback, options) { - Cordova.exec(successCallback, errorCallback, "org.apache.cordova.mediacapture", "captureImage", [options]); -}; - -/** - * Casts a PluginResult message property (array of objects) to an array of MediaFile objects - * (used in Objective-C) - * - * @param {PluginResult} pluginResult - */ -Capture.prototype._castMediaFile = function(pluginResult) { - var mediaFiles = []; - var i; - for (i=0; i} categories -* @param {ContactField[]} urls contact's web sites -*/ -var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, addresses, - ims, organizations, birthday, note, photos, categories, urls) { - this.id = id || null; - this.displayName = displayName || null; - this.name = name || null; // ContactName - this.nickname = nickname || null; - this.phoneNumbers = phoneNumbers || null; // ContactField[] - this.emails = emails || null; // ContactField[] - this.addresses = addresses || null; // ContactAddress[] - this.ims = ims || null; // ContactField[] - this.organizations = organizations || null; // ContactOrganization[] - this.birthday = birthday || null; // JS Date - this.note = note || null; - this.photos = photos || null; // ContactField[] - this.categories = categories || null; - this.urls = urls || null; // ContactField[] -}; - -/** -* Converts Dates to milliseconds before sending to iOS -*/ -Contact.prototype.convertDatesOut = function() -{ - var dates = new Array("birthday"); - for (var i=0; i][;base64], - * - * @param file {File} File object containing file properties - */ -FileReader.prototype.readAsDataURL = function(file) { - this.fileName = ""; - - if (typeof file.fullPath === "undefined") { - this.fileName = file; - } else { - this.fileName = file.fullPath; - } - - // LOADING state - this.readyState = FileReader.LOADING; - - // If loadstart callback - if (typeof this.onloadstart === "function") { - var evt = File._createEvent("loadstart", this); - this.onloadstart(evt); - } - - var me = this; - - // Read file - navigator.fileMgr.readAsDataURL(this.fileName, - - // Success callback - function(r) { - var evt; - - // If DONE (cancelled), then don't do anything - if (me.readyState === FileReader.DONE) { - return; - } - - // Save result - me.result = r; - - // If onload callback - if (typeof me.onload === "function") { - evt = File._createEvent("load", me); - me.onload(evt); - } - - // DONE state - me.readyState = FileReader.DONE; - - // If onloadend callback - if (typeof me.onloadend === "function") { - evt = File._createEvent("loadend", me); - me.onloadend(evt); - } - }, - - // Error callback - function(e) { - var evt; - // If DONE (cancelled), then don't do anything - if (me.readyState === FileReader.DONE) { - return; - } - - // Save error - me.error = e; - - // If onerror callback - if (typeof me.onerror === "function") { - evt = File._createEvent("error", me); - me.onerror(evt); - } - - // DONE state - me.readyState = FileReader.DONE; - - // If onloadend callback - if (typeof me.onloadend === "function") { - evt = File._createEvent("loadend", me); - me.onloadend(evt); - } - } - ); -}; - -/** - * Read file and return data as a binary data. - * - * @param file The name of the file - */ -FileReader.prototype.readAsBinaryString = function(file) { - // TODO - Can't return binary data to browser. - this.fileName = file; -}; - -/** - * Read file and return data as a binary data. - * - * @param file The name of the file - */ -FileReader.prototype.readAsArrayBuffer = function(file) { - // TODO - Can't return binary data to browser. - this.fileName = file; -}; - -//----------------------------------------------------------------------------- -// File Writer -//----------------------------------------------------------------------------- - -/** - * This class writes to the mobile device file system. - * - @param file {File} a File object representing a file on the file system -*/ -FileWriter = function(file) { - this.fileName = ""; - this.length = 0; - if (file) { - this.fileName = file.fullPath || file; - this.length = file.size || 0; - } - - // default is to write at the beginning of the file - this.position = 0; - - this.readyState = 0; // EMPTY - - this.result = null; - - // Error - this.error = null; - - // Event handlers - this.onwritestart = null; // When writing starts - this.onprogress = null; // While writing the file, and reporting partial file data - this.onwrite = null; // When the write has successfully completed. - this.onwriteend = null; // When the request has completed (either in success or failure). - this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. - this.onerror = null; // When the write has failed (see errors). -} - -// States -FileWriter.INIT = 0; -FileWriter.WRITING = 1; -FileWriter.DONE = 2; - -/** - * Abort writing file. - */ -FileWriter.prototype.abort = function() { - // check for invalid state - if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { - throw FileError.INVALID_STATE_ERR; - } - - // set error - var error = new FileError(), evt; - error.code = error.ABORT_ERR; - this.error = error; - - // If error callback - if (typeof this.onerror === "function") { - evt = File._createEvent("error", this); - this.onerror(evt); - } - // If abort callback - if (typeof this.onabort === "function") { - evt = File._createEvent("abort", this); - this.onabort(evt); - } - - this.readyState = FileWriter.DONE; - - // If write end callback - if (typeof this.onwriteend == "function") { - evt = File._createEvent("writeend", this); - this.onwriteend(evt); - } -}; - -/** - * @Deprecated: use write instead - * - * @param file to write the data to - * @param text to be written - * @param bAppend if true write to end of file, otherwise overwrite the file - */ -FileWriter.prototype.writeAsText = function(file, text, bAppend) { - // Throw an exception if we are already writing a file - if (this.readyState === FileWriter.WRITING) { - throw FileError.INVALID_STATE_ERR; - } - - if (bAppend !== true) { - bAppend = false; // for null values - } - - this.fileName = file; - - // WRITING state - this.readyState = FileWriter.WRITING; - - var me = this; - - // If onwritestart callback - if (typeof me.onwritestart === "function") { - var evt = File._createEvent("writestart", me); - me.onwritestart(evt); - } - - - // Write file - navigator.fileMgr.writeAsText(file, text, bAppend, - // Success callback - function(r) { - var evt; - - // If DONE (cancelled), then don't do anything - if (me.readyState === FileWriter.DONE) { - return; - } - - // Save result - me.result = r; - - // If onwrite callback - if (typeof me.onwrite === "function") { - evt = File._createEvent("write", me); - me.onwrite(evt); - } - - // DONE state - me.readyState = FileWriter.DONE; - - // If onwriteend callback - if (typeof me.onwriteend === "function") { - evt = File._createEvent("writeend", me); - me.onwriteend(evt); - } - }, - - // Error callback - function(e) { - var evt; - - // If DONE (cancelled), then don't do anything - if (me.readyState === FileWriter.DONE) { - return; - } - - // Save error - me.error = e; - - // If onerror callback - if (typeof me.onerror === "function") { - evt = File._createEvent("error", me); - me.onerror(evt); - } - - // DONE state - me.readyState = FileWriter.DONE; - - // If onwriteend callback - if (typeof me.onwriteend === "function") { - evt = File._createEvent("writeend", me); - me.onwriteend(evt); - } - } - ); -}; - -/** - * Writes data to the file - * - * @param text to be written - */ -FileWriter.prototype.write = function(text) { - // Throw an exception if we are already writing a file - if (this.readyState === FileWriter.WRITING) { - throw FileError.INVALID_STATE_ERR; - } - - // WRITING state - this.readyState = FileWriter.WRITING; - - var me = this; - - // If onwritestart callback - if (typeof me.onwritestart === "function") { - var evt = File._createEvent("writestart", me); - me.onwritestart(evt); - } - - // Write file - navigator.fileMgr.write(this.fileName, text, this.position, - - // Success callback - function(r) { - var evt; - // If DONE (cancelled), then don't do anything - if (me.readyState === FileWriter.DONE) { - return; - } - - - // position always increases by bytes written because file would be extended - me.position += r; - // The length of the file is now where we are done writing. - me.length = me.position; - - // If onwrite callback - if (typeof me.onwrite === "function") { - evt = File._createEvent("write", me); - me.onwrite(evt); - } - - // DONE state - me.readyState = FileWriter.DONE; - - // If onwriteend callback - if (typeof me.onwriteend === "function") { - evt = File._createEvent("writeend", me); - me.onwriteend(evt); - } - }, - - // Error callback - function(e) { - var evt; - - // If DONE (cancelled), then don't do anything - if (me.readyState === FileWriter.DONE) { - return; - } - - // Save error - me.error = e; - - // If onerror callback - if (typeof me.onerror === "function") { - evt = File._createEvent("error", me); - me.onerror(evt); - } - - // DONE state - me.readyState = FileWriter.DONE; - - // If onwriteend callback - if (typeof me.onwriteend === "function") { - evt = File._createEvent("writeend", me); - me.onwriteend(evt); - } - } - ); - -}; - -/** - * Moves the file pointer to the location specified. - * - * If the offset is a negative number the position of the file - * pointer is rewound. If the offset is greater than the file - * size the position is set to the end of the file. - * - * @param offset is the location to move the file pointer to. - */ -FileWriter.prototype.seek = function(offset) { - // Throw an exception if we are already writing a file - if (this.readyState === FileWriter.WRITING) { - throw FileError.INVALID_STATE_ERR; - } - - if (!offset) { - return; - } - - // See back from end of file. - if (offset < 0) { - this.position = Math.max(offset + this.length, 0); - } - // Offset is bigger then file size so set position - // to the end of the file. - else if (offset > this.length) { - this.position = this.length; - } - // Offset is between 0 and file size so set the position - // to start writing. - else { - this.position = offset; - } -}; - -/** - * Truncates the file to the size specified. - * - * @param size to chop the file at. - */ -FileWriter.prototype.truncate = function(size) { - // Throw an exception if we are already writing a file - if (this.readyState === FileWriter.WRITING) { - throw FileError.INVALID_STATE_ERR; - } - // what if no size specified? - - // WRITING state - this.readyState = FileWriter.WRITING; - - var me = this; - - // If onwritestart callback - if (typeof me.onwritestart === "function") { - var evt = File._createEvent("writestart", me); - me.onwritestart(evt); - } - - // Write file - navigator.fileMgr.truncate(this.fileName, size, - - // Success callback - function(r) { - var evt; - // If DONE (cancelled), then don't do anything - if (me.readyState === FileWriter.DONE) { - return; - } - - // Update the length of the file - me.length = r; - me.position = Math.min(me.position, r); - - // If onwrite callback - if (typeof me.onwrite === "function") { - evt = File._createEvent("write", me); - me.onwrite(evt); - } - - // DONE state - me.readyState = FileWriter.DONE; - - // If onwriteend callback - if (typeof me.onwriteend === "function") { - evt = File._createEvent("writeend", me); - me.onwriteend(evt); - } - }, - - // Error callback - function(e) { - var evt; - // If DONE (cancelled), then don't do anything - if (me.readyState === FileWriter.DONE) { - return; - } - - // Save error - me.error = e; - - // If onerror callback - if (typeof me.onerror === "function") { - evt = File._createEvent("error", me); - me.onerror(evt); - } - - // DONE state - me.readyState = FileWriter.DONE; - - // If onwriteend callback - if (typeof me.onwriteend === "function") { - evt = File._createEvent("writeend", me); - me.onwriteend(evt); - } - } - ); -}; - -LocalFileSystem = function() { -}; - -// File error codes -LocalFileSystem.TEMPORARY = 0; -LocalFileSystem.PERSISTENT = 1; -LocalFileSystem.RESOURCE = 2; -LocalFileSystem.APPLICATION = 3; - -/** - * Requests a filesystem in which to store application data. - * - * @param {int} type of file system being requested - * @param {Function} successCallback is called with the new FileSystem - * @param {Function} errorCallback is called with a FileError - */ -LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) { - if (type < 0 || type > 3) { - if (typeof errorCallback == "function") { - errorCallback({ - "code": FileError.SYNTAX_ERR - }); - } - } - else { - Cordova.exec(successCallback, errorCallback, "org.apache.cordova.file", "requestFileSystem", [type, size]); - } -}; - -/** - * - * @param {DOMString} uri referring to a local file in a filesystem - * @param {Function} successCallback is called with the new entry - * @param {Function} errorCallback is called with a FileError - */ -LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) { - Cordova.exec(successCallback, errorCallback, "org.apache.cordova.file", "resolveLocalFileSystemURI", [uri]); -}; - -/** -* This function is required as we need to convert raw -* JSON objects into concrete File and Directory objects. -* -* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects. -* @returns an entry -*/ -LocalFileSystem.prototype._castFS = function(pluginResult) { - var entry = null; - entry = new DirectoryEntry(); - entry.isDirectory = pluginResult.message.root.isDirectory; - entry.isFile = pluginResult.message.root.isFile; - entry.name = pluginResult.message.root.name; - entry.fullPath = pluginResult.message.root.fullPath; - pluginResult.message.root = entry; - return pluginResult; -} - -LocalFileSystem.prototype._castEntry = function(pluginResult) { - var entry = null; - if (pluginResult.message.isDirectory) { - entry = new DirectoryEntry(); - } - else if (pluginResult.message.isFile) { - entry = new FileEntry(); - } - entry.isDirectory = pluginResult.message.isDirectory; - entry.isFile = pluginResult.message.isFile; - entry.name = pluginResult.message.name; - entry.fullPath = pluginResult.message.fullPath; - pluginResult.message = entry; - return pluginResult; -} - -LocalFileSystem.prototype._castEntries = function(pluginResult) { - var entries = pluginResult.message; - var retVal = []; - for (i=0; i - - - - - - - - paypal-plugin-host - - - - - - - - - - diff --git a/iOS/PayPalPlugin/.EXAMPLES/DO_NOT_ADD_PayPalPlugin-Hosts.md b/iOS/PayPalPlugin/.EXAMPLES/DO_NOT_ADD_PayPalPlugin-Hosts.md deleted file mode 100644 index ca917a2..0000000 --- a/iOS/PayPalPlugin/.EXAMPLES/DO_NOT_ADD_PayPalPlugin-Hosts.md +++ /dev/null @@ -1 +0,0 @@ -Do not add the PayPalPlugin-Hosts to your application \ No newline at end of file diff --git a/iOS/PayPalPlugin/SAiOSPaypalPlugin.h b/iOS/PayPalPlugin/SAiOSPaypalPlugin.h index 6109cc9..e25b2d9 100644 --- a/iOS/PayPalPlugin/SAiOSPaypalPlugin.h +++ b/iOS/PayPalPlugin/SAiOSPaypalPlugin.h @@ -6,11 +6,7 @@ // Copyright 2010 Shazron Abdullah. All rights reserved. #import -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "CDVPlugin.h" -#endif #import "PayPal.h" @interface PaypalPaymentInfo : NSObject diff --git a/iOS/PayPalPlugin/wwwCDV20.zip b/iOS/PayPalPlugin/wwwCDV20.zip new file mode 100644 index 0000000..d1cf560 Binary files /dev/null and b/iOS/PayPalPlugin/wwwCDV20.zip differ diff --git a/iOS/PushNotification/PushNotification.h b/iOS/PushNotification/PushNotification.h index 3a298ef..a81c86a 100644 --- a/iOS/PushNotification/PushNotification.h +++ b/iOS/PushNotification/PushNotification.h @@ -8,11 +8,7 @@ // MIT Licensed #import -#ifdef CORDOVA_FRAMEWORK - #import -#else - #import "CDVPlugin.h" -#endif +#import @interface PushNotification : CDVPlugin { diff --git a/iOS/PushNotification/PushNotification.m b/iOS/PushNotification/PushNotification.m index 7a28bf5..371f5d3 100644 --- a/iOS/PushNotification/PushNotification.m +++ b/iOS/PushNotification/PushNotification.m @@ -8,11 +8,7 @@ // MIT Licensed #import "PushNotification.h" -#ifdef CORDOVA_FRAMEWORK - #import -#else - #import "JSONKit.h" -#endif +#import @implementation PushNotification @@ -84,7 +80,7 @@ - (void)didReceiveRemoteNotification:(NSDictionary*)userInfo { DLog(@"didReceiveRemoteNotification:%@", userInfo); - NSString *jsStatement = [NSString stringWithFormat:@"window.plugins.pushNotification.notificationCallback(%@);", [userInfo JSONString]]; + NSString *jsStatement = [NSString stringWithFormat:@"window.plugins.pushNotification.notificationCallback(%@);", [userInfo cdvjk_JSONString]]; [self writeJavascript:jsStatement]; } diff --git a/iOS/PushNotification/README.md b/iOS/PushNotification/README.md index f2cafc4..a097d7b 100644 --- a/iOS/PushNotification/README.md +++ b/iOS/PushNotification/README.md @@ -107,6 +107,8 @@ In order to support launch notifications (app starting from a remote notificatio pushNotification.registerDevice({alert:true, badge:true, sound:true}, function(status) { + // if successful status is an object that looks like this: + // {"type":"7","pushBadge":"1","pushSound":"1","enabled":"1","deviceToken":"blablahblah","pushAlert":"1"} console.warn('registerDevice:%o', status); navigator.notification.alert(JSON.stringify(['registerDevice', status])); }); diff --git a/iOS/README.md b/iOS/README.md index 1f64514..293b230 100644 --- a/iOS/README.md +++ b/iOS/README.md @@ -1,89 +1,70 @@ -New iOS plugins should be submitted here. +#Cordova (iOS) plugins should be submitted here. -As existing iPhone/iOS plugins are patched and COMPLETELY abandon the PhoneGap naming convention they will be placed here. -The present https://github.com/phonegap/phonegap-plugins/tree/master/iPhone should be preserved for use with projects still using PhoneGap 1.4.1 and before. +The [/iPhone](https://github.com/phonegap/phonegap-plugins/tree/master/iPhone) folder should be preserved for use with projects still using [PhoneGap 1.4.1](https://github.com/phonegap/phonegap/tags) and before. -* Added a ActionSheet (iOS) plugin with Cordova support. -* Added a AdPlugin (iOS) plugin with Cordova support. -* Added a ApplicationPreferences (iOS) plugin with Cordova support. -* Added a Badge (iOS) plugin with Cordova support. -* Added a BarCodeScanner (iOS) plugin with Cordova support. -* Added a card.io (iOS) plugin with Cordova support. -* Added a ChildBrowser (iOS) plugin with Cordova support. -* Added a DatePicker (iOS) plugin with Cordova support. -* Added a EmailComposer (iOS) plugin with Cordova support. -* Added a NativeControls (iOS) plugin with Cordova support. -* Added a PayPalPlugin (iOS) plugin with Cordova support. -* Added a PrintPlugin (iOS) plugin with Cordova support. -* Added a Screenshot (iOS) plugin with Cordova support. -* Added a SMSComposer (iOS) plugin with Cordova support. -* Added a Twitter (iOS) plugin with Cordova support. -* Added a VolumeSlider (iOS) plugin with Cordova support. +* Added ActionSheet (iOS) plugin with Cordova support. +* Added AdPlugin (iOS) plugin with Cordova support. +* Added AppBlade (iOS) plugin with Cordova support. +* Added AppiraterPlugin (iOS) plugin with Cordova support. +* Added ApplicationPreferences (iOS) plugin with Cordova support. +* Added AudioRecord (iOS) plugin with Cordova support. +* Added Badge (iOS) plugin with Cordova support. +* Added BarcodeScanner (iOS) plugin with Cordova support. +* Added CalendarPlugin (iOS) plugin with Cordova support. +* Added ChildBrowser (iOS) plugin with Cordova support. +* Added DatePicker (iOS) plugin with Cordova support. +* Added Diagnostic (iOS) plugin with Cordova support. +* Added EmailComposer (iOS) plugin with Cordova support. +* Added FileUploader (iOS) plugin with Cordova support. +* Added Globalization (iOS) plugin with Cordova support. +* Added GoogleAnalytics (iOS) plugin with Cordova support. +* Added InAppPurchaseManager (iOS) plugin with Cordova support. +* Added Keychain (iOS) plugin with Cordova support. +* Added LocalNotifications (iOS) plugin with Cordova support. +* Added LowLatencyAudio (iOS) plugin with Cordova support. +* Added MapKit (iOS) plugin with Cordova support. +* Added MessageBox (iOS) plugin with Cordova support. +* Added NativeControls (iOS) plugin with Cordova support. +* Added NavigationBar (iOS) plugin with Cordova support. +* Added NotificationEx (iOS) plugin with Cordova support. +* Added OCRPlugin (iOS) plugin with Cordova support. +* Added PayPalPlugin (iOS) plugin with Cordova support. +* Added PickerView (iOS) plugin with Cordova support. +* Added PowerManagement (iOS) plugin with Cordova support. +* Added PrintPlugin (iOS) plugin with Cordova support. +* Added ProgressHud (iOS) plugin with Cordova support. +* Added PushNotification (iOS) plugin with Cordova support. +* Added SMSComposer (iOS) plugin with Cordova support. +* Added Screenshot (iOS) plugin with Cordova support. +* Added SecureDeviceIdentifier (iOS) plugin with Cordova support. +* Added ShareKitPlugin (iOS) plugin with Cordova support. +* Added TabBar (iOS) plugin with Cordova support. +* Added Testflight (iOS) plugin with Cordova support. +* Added Twitter (iOS) plugin with Cordova support. +* Added UAPushNotifications (iOS) plugin with Cordova support. +* Added UniqueIdentifier (iOS) plugin with Cordova support. +* Added VolumeSlider (iOS) plugin with Cordova support. +* Added WebInspector (iOS) plugin with Cordova support. +* Added card.io (iOS) plugin with Cordova support. +* Added iCloudKV (iOS) plugin with Cordova support. * More added regularly. -Please refer to the Plugin Upgrade Guides distributed in the [download](http://phonegap.com/download/) for the most current version. - -# Cordova Plugin Upgrade Guide # - -This document is for developers who need to upgrade their Cordova plugins to a newer Cordova version. Starting with Cordova 1.5.0, some classes have been renamed, which will require the plugin to be upgraded. Make sure your project itself has been upgraded using the "Cordova Upgrade Guide" document. +Please refer to the Plugin Upgrade Guides distributed in the [download](http://phonegap.com/download/) for the most current version. -## Upgrading older Cordova plugins to 1.6.0 ## +#Cordova Plugin Upgrade Guide + This document is for developers who need to upgrade their Cordova plugins to a newer Cordova version. Starting with Cordova 1.5.0, some classes have been renamed, which will require the plugin to be upgraded. Make sure your project itself has been upgraded using the "Cordova Upgrade Guide" document. Upgrading older Cordova plugins to 2.0.0 1. Install Cordova 2.0.0 2. Follow the "Upgrading older Cordova plugins to 1.9.0" section, if necessary 3. No changes in plugin structure from 1.9.x 4. Change in import header use: in 2.0.0, Cordova projects use the CordovaLib project as a subproject, it now uses the CORDOVA_FRAMEWORK styled import like this:
#import + instead of like this:
+ #import "CDV.h" + So now in 2.0.0, Cordova import headers are unified. NOTE: The deprecated for 2.0.0 CDVPlugin methods verifyArguments and appViewController have been removed. + ##Upgrading older Cordova plugins to 1.9.0 1. Install Cordova 1.9.0 2. Follow the "Upgrading older Cordova plugins to 1.8.0" section, if necessary 3. No changes in plugin structure from 1.8.x + ##Upgrading older Cordova plugins to 1.8.0 1. Install Cordova 1.8.0 2. Follow the "Upgrading older Cordova plugins to 1.7.0" section, if necessary 3. No changes in plugin structure from 1.7.x ##Upgrading older Cordova plugins to 1.7.0 1. Install Cordova 1.7.0 2. Follow the "Upgrading older Cordova plugins to 1.6.0" section, if necessary 3. No changes in plugin structure from 1.6.x ##Upgrading older Cordova plugins to 1.6.x +1. Install Cordova 1.6.x 2. Follow the "Upgrading older Cordova plugins to 1.5.0" section, if necessary 3. See the 1.6.0 Plugin Notes section for new functionality available to plugins 4. The global "Cordova" (upper-case C) was renamed to "cordova" (lower-case c) to match the cordova-js Android implementation in 1.5.0 that is now common to Android, Blackberry and iOS. Please rename your calls to reflect the new lower-case c, or you can add a shim (which will support older versions) like so: 5. Wrap your plugin JavaScript in a temporary scope (self-executing function) - see "Temporary Scope" or this b. Inside your temporary scope, set a local var to the global PhoneGap/Cordova/cordova object, for the exec function var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks 6. Replace any PhoneGap or Cordova or cordova in your plugin JavaScript (within the temporary scope), with cordovaRef above. + ##Upgrading older Cordova plugins to 1.5.0 1. Install Cordova 1.5.0 2. Replacemacrooccurrencesof"PHONEGAP_FRAMEWORK"with"CORDOVA_FRAMEWORK" 3. Replace import occurrences of " -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "CDVPlugin.h" -#endif #import #import diff --git a/iOS/SMSComposer/index.html b/iOS/SMSComposer/index.html index 8cbcbbe..5d0c2c7 100755 --- a/iOS/SMSComposer/index.html +++ b/iOS/SMSComposer/index.html @@ -1,68 +1,60 @@ - + - - - - - - - - - - - - -
-
-
- - - - - + + + + + + + Hello Cordova + + +
+

Apache Cordovaâ„¢

+
+ + +
+
+ + + + + + + + +
+ + + + - diff --git a/iOS/ScreenOrientation/README.md b/iOS/ScreenOrientation/README.md new file mode 100644 index 0000000..d5e1e71 --- /dev/null +++ b/iOS/ScreenOrientation/README.md @@ -0,0 +1,50 @@ +# Cordova ScreenOrientation Plugin # +by `Simon Cruise` + +## DESCRIPTION ## + +The status bar and screen will be rotated with animation to the desired orientation 'portrait' or 'landscape'. + + +## SETUP ## + +To use the AppDelegate must have the main view controller that extends CDVViewController as a member variable called viewController. +This main view controller header file should have the below allowed orientations variable introduced. + + @interface MainViewController : CDVViewController { + NSMutableArray *allowedOrientations; + } + + @property (nonatomic, retain) NSMutableArray *allowedOrientations; + +Source file should have the allowedOrientations synthesized. + + @synthesize allowedOrientations; + +Then perform setup in viewDidLoad method. UIDeviceOrientationPortrait can be replaced with UIDeviceOrientationLandscapeRight depending on desired start up orientation. + + - (void) viewDidLoad + { + [super viewDidLoad]; + self.allowedOrientations = [NSMutableArray array]; + [self.allowedOrientations addObject:[NSNumber numberWithInt:UIDeviceOrientationPortrait]]; + } + +Finally implement shouldAutorotateToInterfaceOrientation method with +the following. + + - (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation + { + return [allowedOrientations containsObject:[NSNumber numberWithInt:interfaceOrientation]]; + } +## LICENSE ## + +Copyright 2012 Simon Cruise. All rights reserved. + +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/iOS/ScreenOrientation/ScreenOrientation.h b/iOS/ScreenOrientation/ScreenOrientation.h new file mode 100644 index 0000000..d63b81a --- /dev/null +++ b/iOS/ScreenOrientation/ScreenOrientation.h @@ -0,0 +1,16 @@ +// +// ScreenOrientation.h +// +// Created by Simon Cruise on 30/08/2012. +// + +#import +#import "AppDelegate.h" + +#import + +@interface ScreenOrientation : CDVPlugin + +- (void) set:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + +@end diff --git a/iOS/ScreenOrientation/ScreenOrientation.js b/iOS/ScreenOrientation/ScreenOrientation.js new file mode 100755 index 0000000..72406a2 --- /dev/null +++ b/iOS/ScreenOrientation/ScreenOrientation.js @@ -0,0 +1,8 @@ +var screenOrientation = function() {} + +screenOrientation.prototype.set = function(str, success, fail) { + var args = {}; + args.key = str; + PhoneGap.exec(success, fail, "ScreenOrientation", "set", [args]); +}; +navigator.screenOrientation = new screenOrientation(); diff --git a/iOS/ScreenOrientation/ScreenOrientation.m b/iOS/ScreenOrientation/ScreenOrientation.m new file mode 100644 index 0000000..660d97e --- /dev/null +++ b/iOS/ScreenOrientation/ScreenOrientation.m @@ -0,0 +1,44 @@ +// +// ScreenOrientation.m +// +// Created by Simon Cruise on 30/08/2012. +// + +#import "ScreenOrientation.h" + +@implementation ScreenOrientation + +- (void)set:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options +{ + AppDelegate* appDelegate = (AppDelegate*) [UIApplication sharedApplication].delegate; + + NSMutableArray *allowed = [NSMutableArray array]; + NSString *targetOrientation = [options objectForKey:@"key"]; + int statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; + int statusBarWidth = [[UIApplication sharedApplication] statusBarFrame].size.width; + if([targetOrientation isEqualToString:@"landscape"]) { + [allowed addObject:[NSNumber numberWithInt:UIDeviceOrientationLandscapeRight]]; + appDelegate.viewController.allowedOrientations = allowed; + [[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeRight animated:YES]; + [appDelegate.viewController.view setTransform: CGAffineTransformMakeRotation(M_PI * 1.5)]; + [appDelegate.viewController.view setFrame:CGRectMake(statusBarHeight, 0, appDelegate.viewController.view.frame.size.height-statusBarHeight, appDelegate.viewController.view.frame.size.width+statusBarHeight)]; + + [UIView commitAnimations]; + } + if([targetOrientation isEqualToString:@"portrait"]) { + if (![appDelegate.viewController.allowedOrientations containsObject:[NSNumber numberWithInt:UIDeviceOrientationPortrait]]) { + [allowed addObject:[NSNumber numberWithInt:UIDeviceOrientationPortrait]]; + appDelegate.viewController.allowedOrientations = allowed; + [[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationPortrait animated:YES]; + [appDelegate.viewController.view setTransform: CGAffineTransformMakeRotation(0)]; + [appDelegate.viewController.view setFrame:CGRectMake(0, statusBarWidth, appDelegate.viewController.view.frame.size.height+statusBarWidth, appDelegate.viewController.view.frame.size.width-statusBarWidth)]; + + [UIView commitAnimations]; + + } + } + +} + + +@end diff --git a/iOS/Screenshot/README.md b/iOS/Screenshot/README.md new file mode 100644 index 0000000..c0ff635 --- /dev/null +++ b/iOS/Screenshot/README.md @@ -0,0 +1,36 @@ +Screenshot +=============== +Plugin for Cordova (1.6+) + +The Screenshot plugin allows your application to take screenshots of the current screen and save them into the phone. + +Platforms +--------- +Currently available on: + +### Android +Copy the content of *src* folder to your *src* folder. + +Copy the content of *www* folder to you *www* folder. + +Edit your *AndroidManifest.xml* and add the following permission: +`` + +In addition you have to edit your *res/xml/plugins.xml* file to let Cordova know about the plugin: +`` + +### iOS +Copy the *Screenshot.h* and *Screenshot.m* files to your projects "Plugins" folder. + +Copy the *Screenshot.js* file to you *www* folder. + +Add the Screenshot plugin to the *Cordova.plist* file (to the Plugins list). Both Key and Value are "Screenshot". + + +License +======= +Copyright (C) 2012 30ideas (http://30ide.as) +MIT licensed + +Created by Josemando Sobral +Jul 2nd, 2012 diff --git a/iOS/Screenshot/Screenshot.js b/iOS/Screenshot/Screenshot.js index eab8b61..e418c42 100644 --- a/iOS/Screenshot/Screenshot.js +++ b/iOS/Screenshot/Screenshot.js @@ -2,7 +2,7 @@ * This code is adapted from the work of Michael Nachbaur * by Simon Madine of The Angry Robot Zombie Factory * - Converted to Cordova 1.6.1 by Josemando Sobral. - * 2012-06-03 + * 2012-07-03 * MIT licensed */ @@ -25,7 +25,7 @@ * Save the screenshot to the user's Photo Library */ Screenshot.prototype.saveScreenshot = function() { - cordovaRef.exec("Screenshot.saveScreenshot"); + cordovaRef.exec(null, null, "Screenshot", "saveScreenshot", []); }; cordovaRef.addConstructor(function() { diff --git a/iOS/ShareKitPlugin/ShareKitPlugin.m b/iOS/ShareKitPlugin/ShareKitPlugin.m index 180fde3..43146ca 100644 --- a/iOS/ShareKitPlugin/ShareKitPlugin.m +++ b/iOS/ShareKitPlugin/ShareKitPlugin.m @@ -25,7 +25,8 @@ NSString *message = [arguments objectAtIndex:1]; SHKItem *item; - if ([arguments objectAtIndex:2]==NULL) { + + if ([arguments count] == 3) { NSURL *itemUrl = [NSURL URLWithString:[arguments objectAtIndex:2]]; item = [SHKItem URL:itemUrl title:message contentType:SHKURLContentTypeWebpage]; } else { @@ -112,14 +113,12 @@ SHKItem *item; - NSString *message = [arguments objectAtIndex:1]; - if ([arguments objectAtIndex:2]==NULL) { - NSURL *itemUrl = [NSURL URLWithString:[arguments objectAtIndex:2]]; - item = [SHKItem URL:itemUrl title:message contentType:SHKURLContentTypeWebpage]; - } else { - item = [SHKItem text:message]; - } - + NSString *subject = [arguments objectAtIndex:1]; + NSString *body = [arguments objectAtIndex:2]; + + item = [SHKItem text:body]; + item.title = subject; + [SHKMail shareItem:item]; } diff --git a/iOS/StatusBarNotifier/FDStatusBarNotifierView.h b/iOS/StatusBarNotifier/FDStatusBarNotifierView.h new file mode 100644 index 0000000..9690ad8 --- /dev/null +++ b/iOS/StatusBarNotifier/FDStatusBarNotifierView.h @@ -0,0 +1,38 @@ +// +// StatusBarNotifierView.h +// StatusBarNotifier +// +// Created by Francesco Di Lorenzo on 05/09/12. +// Copyright (c) 2012 Francesco Di Lorenzo. All rights reserved. +// + +#import + + + +@interface FDStatusBarNotifierView : UIView + +@property (strong, nonatomic) NSString *message; +@property NSTimeInterval timeOnScreen; // seconds, default: 2s +@property id delegate; + + +- (id)initWithMessage:(NSString *)message; +- (id)initWithMessage:(NSString *)message delegate:(id /**/)delegate; + +- (void)showInWindow:(UIWindow *)window; + +@end + + +@protocol StatusBarNotifierViewDelegate +@optional + +- (void)willPresentNotifierView:(FDStatusBarNotifierView *)notifierView; // before animation and showing view +- (void)didPresentNotifierView:(FDStatusBarNotifierView *)notifierView; // after animation +- (void)willHideNotifierView:(FDStatusBarNotifierView *)notifierView; // before hiding animation +- (void)didHideNotifierView:(FDStatusBarNotifierView *)notifierView; // after animation + +- (void)notifierViewTapped:(FDStatusBarNotifierView *)notifierView; // user tap the status bar message + +@end \ No newline at end of file diff --git a/iOS/StatusBarNotifier/FDStatusBarNotifierView.m b/iOS/StatusBarNotifier/FDStatusBarNotifierView.m new file mode 100644 index 0000000..60dcfc0 --- /dev/null +++ b/iOS/StatusBarNotifier/FDStatusBarNotifierView.m @@ -0,0 +1,133 @@ +// +// StatusBarNotifierView.m +// StatusBarNotifier +// +// Created by Francesco Di Lorenzo on 05/09/12. +// Copyright (c) 2012 Francesco Di Lorenzo. All rights reserved. +// + +#import "FDStatusBarNotifierView.h" + +@interface FDStatusBarNotifierView () + +@property (strong) UILabel *messageLabel; + +@end + +@implementation FDStatusBarNotifierView + +#define kNotifierViewInitialFramePortrait CGRectMake(0, 20, 320, 20) +#define kNotifierViewFinalFramePortrait CGRectMake(0, 0, 320, 20) +//#define kNotifierViewInitialFrameLandscape CGRectMake(0, 20, 480, 20) +//#define kNotifierViewFinalFrameLandscape CGRectMake(0, 0, 480, 20) + +#define kMessageLabelInitialFramePortrait CGRectMake(10, 0, 300, 20) +#define kMessageLabelInitialFrameLandscape CGRectMake(10, 0, 460, 20) + +- (id)init { + self = [super init]; + if (self) { + self.frame = kNotifierViewInitialFramePortrait; + + self.messageLabel = [[UILabel alloc] initWithFrame:kMessageLabelInitialFramePortrait]; + self.messageLabel.textColor = [UIColor whiteColor]; + self.messageLabel.backgroundColor = [UIColor blackColor]; + self.messageLabel.textAlignment = UITextAlignmentCenter; + self.messageLabel.font = [UIFont boldSystemFontOfSize:12]; + [self addSubview:self.messageLabel]; + + self.timeOnScreen = 2.0; + } + return self; +} + +- (id)initWithMessage:(NSString *)message { + self = [super init]; + if (self) { + self.frame = kNotifierViewInitialFramePortrait; + self.message = message; + self.backgroundColor = [UIColor blackColor]; + + self.messageLabel = [[UILabel alloc] initWithFrame:kMessageLabelInitialFramePortrait]; + self.messageLabel.textColor = [UIColor whiteColor]; + self.messageLabel.text = message; + self.messageLabel.backgroundColor = [UIColor blackColor]; + self.messageLabel.textAlignment = UITextAlignmentCenter; + self.messageLabel.font = [UIFont boldSystemFontOfSize:12]; + [self addSubview:self.messageLabel]; + + self.timeOnScreen = 2.0; + } + return self; + +} + +- (id)initWithMessage:(NSString *)message delegate:(id /**/)delegate { + self = [super init]; + if (self) { + self.frame = kNotifierViewInitialFramePortrait; + self.delegate = delegate; + self.message = message; + self.backgroundColor = [UIColor blackColor]; + + self.messageLabel = [[UILabel alloc] initWithFrame:kMessageLabelInitialFramePortrait]; + self.messageLabel.textColor = [UIColor whiteColor]; + self.messageLabel.text = message; + self.messageLabel.backgroundColor = [UIColor blackColor]; + self.messageLabel.textAlignment = UITextAlignmentCenter; + self.messageLabel.font = [UIFont boldSystemFontOfSize:12]; + [self addSubview:self.messageLabel]; + + self.timeOnScreen = 2.0; + } + return self; +} + +- (void)showInWindow:(UIWindow *)window { + if (self.delegate && [self.delegate respondsToSelector:@selector(willPresentNotifierView:)]) + [self.delegate willPresentNotifierView:self]; + + [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide]; + [window insertSubview:self atIndex:0]; + + [UIView animateWithDuration:.4 animations:^{ + self.frame = kNotifierViewFinalFramePortrait; + } completion:^(BOOL finished){ + + if (self.delegate && [self.delegate respondsToSelector:@selector(didPresentNotifierView:)]) + [self.delegate didPresentNotifierView:self]; + + [NSTimer scheduledTimerWithTimeInterval:self.timeOnScreen target:self selector:@selector(hide) userInfo:nil repeats:NO]; + + }]; +} + +- (void)hide { + + if (self.delegate && [self.delegate respondsToSelector:@selector(willHideNotifierView:)]) + [self.delegate willHideNotifierView:self]; + + [UIView animateWithDuration:.4 animations:^{ + self.frame = kNotifierViewInitialFramePortrait; + [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide]; + } completion:^(BOOL finished){ + if (finished) { + + if (self.delegate && [self.delegate respondsToSelector:@selector(didHideNotifierView:)]) + [self.delegate didHideNotifierView:self]; + + [self removeFromSuperview]; + } + }]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [self.delegate notifierViewTapped:self]; +} + +- (void)setMessage:(NSString *)message { + _message = message; + self.messageLabel.text = message; +} + +@end diff --git a/iOS/StatusBarNotifier/README.md b/iOS/StatusBarNotifier/README.md new file mode 100644 index 0000000..d0f0bfa --- /dev/null +++ b/iOS/StatusBarNotifier/README.md @@ -0,0 +1,8 @@ +cordova status bar notifier plugin + +depends on this: http://github.com/frankdilo/FDStatusBarNotifierView + + window.plugins.statusBarNotifier.show('hello yes this is dog') + window.plugins.statusBarNotifier.show('this will display for 5 seconds', 5.0) + +ios6+ only (I think) diff --git a/iOS/StatusBarNotifier/StatusBarNotifier.h b/iOS/StatusBarNotifier/StatusBarNotifier.h new file mode 100644 index 0000000..6225a2d --- /dev/null +++ b/iOS/StatusBarNotifier/StatusBarNotifier.h @@ -0,0 +1,17 @@ +#import +#import "AppDelegate.h" +#import "FDStatusBarNotifierView.h" + +#import + +@interface StatusBarNotifier: CDVPlugin { + NSMutableDictionary* callbackIds; + NSString* messageField; +} + +@property (nonatomic, retain) NSMutableDictionary* callbackIds; +@property (nonatomic, retain) NSString* messageField; + +- (void) show:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + +@end diff --git a/iOS/StatusBarNotifier/StatusBarNotifier.js b/iOS/StatusBarNotifier/StatusBarNotifier.js new file mode 100755 index 0000000..3254367 --- /dev/null +++ b/iOS/StatusBarNotifier/StatusBarNotifier.js @@ -0,0 +1,14 @@ +;(function(cordova) { + + function StatusBarNotifier() {} + + StatusBarNotifier.prototype.show = function(text, timeOnScreen, callback) { + if (typeof timeOnScreen === "function") callback = timeOnScreen + if (!callback) callback = function() {} + cordova.exec(callback, callback, "StatusBarNotifier", "show", [{text: text, timeOnScreen: timeOnScreen}]) + } + + if (!window.plugins) window.plugins = {} + window.plugins.StatusBarNotifier = new StatusBarNotifier() + +})(window.cordova || window.Cordova || window.PhoneGap); diff --git a/iOS/StatusBarNotifier/StatusBarNotifier.m b/iOS/StatusBarNotifier/StatusBarNotifier.m new file mode 100644 index 0000000..ea866bc --- /dev/null +++ b/iOS/StatusBarNotifier/StatusBarNotifier.m @@ -0,0 +1,32 @@ +#import "StatusBarNotifier.h" + +@implementation StatusBarNotifier + +@synthesize callbackIds = _callbackIds; +@synthesize messageField; + +- (NSMutableDictionary*)callbackIds { + if(_callbackIds == nil) { + _callbackIds = [[NSMutableDictionary alloc] init]; + } + return _callbackIds; +} + +- (void)show:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { + [self.callbackIds setValue:[arguments pop] forKey:@"show"]; + AppDelegate* appDelegate = (AppDelegate*) [UIApplication sharedApplication].delegate; + [self setMessageField:[options objectForKey:@"text"] ?: @""]; + float timeOnScreen = [[options objectForKey:@"timeOnScreen"] floatValue] ?: 3.0; + FDStatusBarNotifierView *notifierView = [[FDStatusBarNotifierView alloc] initWithMessage:self.messageField]; + notifierView.timeOnScreen = timeOnScreen; + [notifierView showInWindow:appDelegate.window]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self writeJavascript:[pluginResult toSuccessCallbackString:[self.callbackIds valueForKey:@"show"]]]; +} + +- (void) dealloc { + [_callbackIds dealloc]; + [super dealloc]; +} + +@end diff --git a/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.h b/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.h new file mode 100644 index 0000000..c52fde7 --- /dev/null +++ b/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.h @@ -0,0 +1,48 @@ +/* + TabBar.h + + Created by Jesse MacFadyen on 10-02-03. + MIT Licensed + + Originally this code was developed my Michael Nachbaur + Formerly -> PhoneGap :: UIControls.h + Created by Michael Nachbaur on 13/04/09. + Copyright 2009 Decaf Ninja Software. All rights reserved. + + API cleaned up and improved by Andreas Sommer (https://github.com/AndiDog/phonegap-plugins). + */ +#import +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVPlugin.h" +#import + +@interface TabBar : CDVPlugin { + UITabBar* tabBar; + + NSMutableDictionary* tabBarItems; + + // Represents bounds as if started in portrait mode! + CGRect originalWebViewBounds; + + CGFloat navBarHeight; + CGFloat tabBarHeight; + bool tabBarAtBottom; +} + +- (void)create:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)show:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)resize:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)hide:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)init:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)showItems:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)createItem:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)updateItem:(NSArray*)arguments withDict:(NSDictionary*)options; +- (void)selectItem:(NSArray*)arguments withDict:(NSDictionary*)options; + +@end + +@interface UITabBar (NavBarCompat) +@property (nonatomic) bool tabBarAtBottom; +@end \ No newline at end of file diff --git a/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.js b/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.js new file mode 100644 index 0000000..e24b7c9 --- /dev/null +++ b/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.js @@ -0,0 +1,159 @@ +/* + This code is adapted from the work of: + Created by Michael Nachbaur on 13/04/09. + Copyright 2009 Decaf Ninja Software. All rights reserved. + MIT licensed + + API cleaned up and improved by Andreas Sommer (https://github.com/AndiDog/phonegap-plugins). +*/ + +function TabBar() { + this.tag = 0; + this.callbacks = {}; + this.selectedItem = null; +} + +/** + * Create a native tab bar that can have tab buttons added to it which can respond to events. + * + * @param options Additional options: + * - selectedImageTintColorRgba: Tint color for selected items (defaults to standard light blue), must define the + * color as string e.g. '255,0,0,128' for 50% transparent red. This is only supported on iOS 5 or newer. + */ +TabBar.prototype.create = function(options) { + cordova.exec("TabBar.create", options || {}); +}; + +/** + * Create a new tab bar item for use on a previously created tab bar. Use ::showTabBarItems to show the new item on the tab bar. + * + * If the supplied image name is one of the labels listed below, then this method will construct a tab button + * using the standard system buttons. Note that if you use one of the system images, that the \c title you supply will be ignored. + * + * Tab Buttons + * - tabButton:More + * - tabButton:Favorites + * - tabButton:Featured + * - tabButton:TopRated + * - tabButton:Recents + * - tabButton:Contacts + * - tabButton:History + * - tabButton:Bookmarks + * - tabButton:Search + * - tabButton:Downloads + * - tabButton:MostRecent + * - tabButton:MostViewed + * @param {String} name internal name to refer to this tab by + * @param {String} [title] title text to show on the tab, or null if no text should be shown + * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown + * @param {Object} [options] Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden + */ +TabBar.prototype.createItem = function(name, label, image, options) { + + var tag = this.tag++; + if (options && 'onSelect' in options && typeof(options['onSelect']) == 'function') { + this.callbacks[tag] = {'onSelect':options.onSelect,'name':name}; + //delete options.onSelect; + } + + cordova.exec("TabBar.createItem", name, label, image, tag, options); +}; + +/** + * Function to detect currently selected tab bar item + * @see createItem + * @see showItems + */ +TabBar.prototype.getSelectedItem = function() { + return this.selectedItem; +}; + +/** + * Hide a tab bar. The tab bar has to be created first. + */ +TabBar.prototype.hide = function(animate) { + if (animate === undefined || animate === null) + animate = true; + cordova.exec("TabBar.hide", {animate: animate}); +}; + +/** + * Must be called before any other method in order to initialize the plugin. + */ +TabBar.prototype.init = function() +{ + cordova.exec("TabBar.init"); +}; + +/** + * Internal function called when a tab bar item has been selected. + * @param {Number} tag the tag number for the item that has been selected + */ +TabBar.prototype.onItemSelected = function(tag) +{ + this.selectedItem = tag; + if (typeof(this.callbacks[tag].onSelect) == 'function') + this.callbacks[tag].onSelect(this.callbacks[tag].name); +}; + +TabBar.prototype.resize = function() { + cordova.exec("TabBar.resize"); +}; + +/** + * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item. + * @param {String} tabName the name of the tab to select, or null if all tabs should be deselected + * @see createItem + * @see showItems + */ +TabBar.prototype.selectItem = function(tab) { + cordova.exec("TabBar.selectItem", tab); +}; + +/** + * Show a tab bar. The tab bar has to be created first. + * @param {Object} [options] Options indicating how the tab bar should be shown: + * - \c height integer indicating the height of the tab bar (default: \c 49) + * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom) + */ +TabBar.prototype.show = function(options) { + if(!options) + options = {position: 'bottom'}; + cordova.exec("TabBar.show", options); +}; + +/** + * Show previously created items on the tab bar + * @param {String} arguments... the item names to be shown + * @param {Object} [options] dictionary of options, notable options including: + * - \c animate indicates that the items should animate onto the tab bar + * @see createItem + * @see create + */ +TabBar.prototype.showItems = function() { + var parameters = [ "TabBar.showItems" ]; + for (var i = 0; i < arguments.length; i++) { + parameters.push(arguments[i]); + } + cordova.exec.apply(this, parameters); +}; + +/** + * Update an existing tab bar item to change its badge value. + * @param {String} name internal name used to represent this item when it was created + * @param {Object} options Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden + */ +TabBar.prototype.updateItem = function(name, options) { + if (!options) options = {}; + cordova.exec("TabBar.updateItem", name, options); +}; + +cordova.addConstructor(function() +{ + if(!window.plugins) + window.plugins = {}; + + window.plugins.tabBar = new TabBar(); +}); diff --git a/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.m b/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.m new file mode 100644 index 0000000..4fff920 --- /dev/null +++ b/iOS/TabBar/2.0.0 (not developed anymore)/TabBar.m @@ -0,0 +1,404 @@ +/* + TabBar.m + + Created by Jesse MacFadyen on 10-02-03. + MIT Licensed + + Originally this code was developed my Michael Nachbaur + Formerly -> PhoneGap :: UIControls.h + Created by Michael Nachbaur on 13/04/09. + Copyright 2009 Decaf Ninja Software. All rights reserved. + + API cleaned up and improved by Andreas Sommer (https://github.com/AndiDog/phonegap-plugins). + */ + +#import +#import "TabBar.h" +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVDebug.h" +#import + +@implementation TabBar +#ifndef __IPHONE_3_0 +@synthesize webView; +#endif + +-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (TabBar*)[super initWithWebView:theWebView]; + if (self) + { + // The original web view bounds must be retrieved here. On iPhone, it would be 0,0,320,460 for example. Since + // Cordova seems to initialize plugins on the first call, there is a plugin method init() that has to be called + // in order to make Cordova call *this* method. If someone forgets the init() call and uses the navigation bar + // and tab bar plugins together, these values won't be the original web view bounds and layout will be wrong. + tabBarItems = [[NSMutableDictionary alloc] initWithCapacity:5]; + + originalWebViewBounds = theWebView.bounds; + + // This code block is the same for both the navigation and tab bar plugin! + UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation]; + if(UIInterfaceOrientationIsLandscape(interfaceOrientation)) + { + // In this case, the app started in landscape mode and the web view is sized accordingly. The frame + // (0,0,480,300) is expected on the non-Retina display. Since the originalWebViewBounds variable represents + // the original frame as if the app was started in portrait mode, the values are corrected here: + + UIApplication *app = [UIApplication sharedApplication]; + + if(app.statusBarHidden) + { + originalWebViewBounds.size.width = theWebView.bounds.size.height; + originalWebViewBounds.size.height = theWebView.bounds.size.width; + } + else + { + float statusBarHeight = MIN(app.statusBarFrame.size.width, app.statusBarFrame.size.height); + originalWebViewBounds.size.width = theWebView.bounds.size.height + statusBarHeight; + originalWebViewBounds.size.height = theWebView.bounds.size.width - statusBarHeight; + } + } + + tabBarHeight = 49.0f; + navBarHeight = 44.0f; + tabBarAtBottom = true; + } + return self; +} + +- (void)dealloc +{ + if (tabBar) + [tabBar release]; + + [super dealloc]; +} + +-(void)correctWebViewBounds +{ + if(!tabBar) + return; + + const bool tabBarShown = !tabBar.hidden; + bool navBarShown = false; + + UIView *parent = [tabBar superview]; + for(UIView *view in parent.subviews) + if([view isMemberOfClass:[UINavigationBar class]]) + { + navBarShown = !view.hidden; + break; + } + + // IMPORTANT: Below code is the same in both the navigation and tab bar plugins! + + CGFloat left = originalWebViewBounds.origin.x; + CGFloat right = left + originalWebViewBounds.size.width; + CGFloat top = originalWebViewBounds.origin.y; + CGFloat bottom = top + originalWebViewBounds.size.height; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + switch (orientation) + { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + // No need to change width/height from original bounds + break; + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + right = left + originalWebViewBounds.size.height + 20.0f; + bottom = top + originalWebViewBounds.size.width - 20.0f; + break; + default: + NSLog(@"Unknown orientation: %d", orientation); + break; + } + + if(navBarShown) + top += navBarHeight; + + if(tabBarShown) + { + if(tabBarAtBottom) + bottom -= tabBarHeight; + else + top += tabBarHeight; + } + + CGRect webViewBounds = CGRectMake(left, top, right - left, bottom - top); + + [self.webView setFrame:webViewBounds]; + + // NOTE: Following part again for tab bar plugin only + + if(tabBarShown) + { + if(tabBarAtBottom) + [tabBar setFrame:CGRectMake(left, bottom, right - left, tabBarHeight)]; + else + [tabBar setFrame:CGRectMake(left, originalWebViewBounds.origin.y, right - left, tabBarHeight)]; + } +} + +/** + * Create a native tab bar at either the top or the bottom of the display. + * @brief creates a tab bar + * @param arguments unused + * @param options unused + */ +- (void)create:(NSArray*)arguments withDict:(NSDictionary*)options +{ + tabBar = [UITabBar new]; + [tabBar sizeToFit]; + tabBar.delegate = self; + tabBar.multipleTouchEnabled = NO; + tabBar.autoresizesSubviews = YES; + tabBar.hidden = YES; + tabBar.userInteractionEnabled = YES; + tabBar.opaque = YES; + + NSString *iconTint = [options valueForKey:@"selectedImageTintColorRgba"]; + + if(iconTint && [tabBar respondsToSelector:@selector(setSelectedImageTintColor:)]) + { + NSArray *rgba = [iconTint componentsSeparatedByString:@","]; + tabBar.selectedImageTintColor = [UIColor colorWithRed:[[rgba objectAtIndex:0] intValue]/255.0f + green:[[rgba objectAtIndex:1] intValue]/255.0f + blue:[[rgba objectAtIndex:2] intValue]/255.0f + alpha:[[rgba objectAtIndex:3] intValue]/255.0f]; + } + + self.webView.superview.autoresizesSubviews = YES; + + [self.webView.superview addSubview:tabBar]; +} + +-(void) init:(NSMutableArray *)arguments withDict:(NSMutableDictionary *)options +{ + // Dummy function, see initWithWebView +} + +/** + * Show the tab bar after its been created. + * @brief show the tab bar + * @param arguments unused + * @param options used to indicate options for where and how the tab bar should be placed + * - \c height integer indicating the height of the tab bar (default: \c 49) + * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom) + */ +- (void)show:(NSArray*)arguments withDict:(NSDictionary*)options +{ + if (!tabBar) + [self create:nil withDict:nil]; + + // if we are calling this again when its shown, reset + if (!tabBar.hidden) + return; + + // CGRect offsetRect = [ [UIApplication sharedApplication] statusBarFrame]; + + if(options) + { + tabBarHeight = [[options objectForKey:@"height"] floatValue]; + tabBarAtBottom = [[options objectForKey:@"position"] isEqualToString:@"bottom"]; + } + + tabBar.tabBarAtBottom = tabBarAtBottom; + + if(tabBarHeight == 0) + tabBarHeight = 49.0f; + + tabBar.hidden = NO; + + [self correctWebViewBounds]; +} + +/** + * Resize the tab bar (this should be called on orientation change) + * @brief resize the tab bar on rotation + * @param arguments unused + * @param options unused + */ +- (void)resize:(NSArray*)arguments withDict:(NSDictionary*)options +{ + [self correctWebViewBounds]; +} + +/** + * Hide the tab bar + * @brief hide the tab bar + * @param arguments unused + * @param options unused + */ +- (void)hide:(NSArray*)arguments withDict:(NSDictionary*)options +{ + if (!tabBar) + [self create:nil withDict:nil]; + + tabBar.hidden = YES; + + [self correctWebViewBounds]; +} + +/** + * Create a new tab bar item for use on a previously created tab bar. Use ::showTabBarItems to show the new item on the tab bar. + * + * If the supplied image name is one of the labels listed below, then this method will construct a tab button + * using the standard system buttons. Note that if you use one of the system images, that the \c title you supply will be ignored. + * - Tab Buttons + * - tabButton:More + * - tabButton:Favorites + * - tabButton:Featured + * - tabButton:TopRated + * - tabButton:Recents + * - tabButton:Contacts + * - tabButton:History + * - tabButton:Bookmarks + * - tabButton:Search + * - tabButton:Downloads + * - tabButton:MostRecent + * - tabButton:MostViewed + * @brief create a tab bar item + * @param arguments Parameters used to create the tab bar + * -# \c name internal name to refer to this tab by + * -# \c title title text to show on the tab, or null if no text should be shown + * -# \c image image filename or internal identifier to show, or null if now image should be shown + * -# \c tag unique number to be used as an internal reference to this button + * @param options Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden + */ +- (void)createItem:(NSArray*)arguments withDict:(NSDictionary*)options +{ + if (!tabBar) + [self create:nil withDict:nil]; + + NSString *name = [arguments objectAtIndex:0]; + NSString *title = [arguments objectAtIndex:1]; + NSString *imageName = [arguments objectAtIndex:2]; + int tag = [[arguments objectAtIndex:3] intValue]; + + UITabBarItem *item = nil; + if ([imageName length] > 0) + { + UITabBarSystemItem systemItem = -1; + if ([imageName isEqualToString:@"tabButton:More"]) systemItem = UITabBarSystemItemMore; + else if ([imageName isEqualToString:@"tabButton:Favorites"]) systemItem = UITabBarSystemItemFavorites; + else if ([imageName isEqualToString:@"tabButton:Featured"]) systemItem = UITabBarSystemItemFeatured; + else if ([imageName isEqualToString:@"tabButton:TopRated"]) systemItem = UITabBarSystemItemTopRated; + else if ([imageName isEqualToString:@"tabButton:Recents"]) systemItem = UITabBarSystemItemRecents; + else if ([imageName isEqualToString:@"tabButton:Contacts"]) systemItem = UITabBarSystemItemContacts; + else if ([imageName isEqualToString:@"tabButton:History"]) systemItem = UITabBarSystemItemHistory; + else if ([imageName isEqualToString:@"tabButton:Bookmarks"]) systemItem = UITabBarSystemItemBookmarks; + else if ([imageName isEqualToString:@"tabButton:Search"]) systemItem = UITabBarSystemItemSearch; + else if ([imageName isEqualToString:@"tabButton:Downloads"]) systemItem = UITabBarSystemItemDownloads; + else if ([imageName isEqualToString:@"tabButton:MostRecent"]) systemItem = UITabBarSystemItemMostRecent; + else if ([imageName isEqualToString:@"tabButton:MostViewed"]) systemItem = UITabBarSystemItemMostViewed; + if (systemItem != -1) + item = [[UITabBarItem alloc] initWithTabBarSystemItem:systemItem tag:tag]; + } + + if (item == nil) + item = [[UITabBarItem alloc] initWithTitle:title image:[UIImage imageNamed:imageName] tag:tag]; + + if ([options objectForKey:@"badge"]) + item.badgeValue = [options objectForKey:@"badge"]; + + [tabBarItems setObject:item forKey:name]; + [item release]; +} + + +/** + * Update an existing tab bar item to change its badge value. + * @brief update the badge value on an existing tab bar item + * @param arguments Parameters used to identify the tab bar item to update + * -# \c name internal name used to represent this item when it was created + * @param options Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden + */ +- (void)updateItem:(NSArray*)arguments withDict:(NSDictionary*)options +{ + if (!tabBar) + [self create:nil withDict:nil]; + + NSString *name = [arguments objectAtIndex:0]; + UITabBarItem *item = [tabBarItems objectForKey:name]; + if (item) + item.badgeValue = [options objectForKey:@"badge"]; +} + + +/** + * Show previously created items on the tab bar + * @brief show a list of tab bar items + * @param arguments the item names to be shown + * @param options dictionary of options, notable options including: + * - \c animate indicates that the items should animate onto the tab bar + * @see createItem + * @see create + */ +- (void)showItems:(NSArray*)arguments withDict:(NSDictionary*)options +{ + if (!tabBar) + [self create:nil withDict:nil]; + + int i, count = [arguments count]; + NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:count]; + for (i = 0; i < count; i++) { + NSString *itemName = [arguments objectAtIndex:i]; + UITabBarItem *item = [tabBarItems objectForKey:itemName]; + if (item) + [items addObject:item]; + } + + BOOL animateItems = NO; + if ([options objectForKey:@"animate"]) + animateItems = [(NSString*)[options objectForKey:@"animate"] boolValue]; + [tabBar setItems:items animated:animateItems]; + [items release]; +} + +/** + * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item. + * @brief manually select a tab bar item + * @param arguments the name of the tab bar item to select + * @see createItem + * @see showItems + */ +- (void)selectItem:(NSArray*)arguments withDict:(NSDictionary*)options +{ + if (!tabBar) + [self create:nil withDict:nil]; + + NSString *itemName = [arguments objectAtIndex:0]; + UITabBarItem *item = [tabBarItems objectForKey:itemName]; + if (item) + tabBar.selectedItem = item; + else + tabBar.selectedItem = nil; +} + +- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item +{ + NSString * jsCallBack = [NSString stringWithFormat:@"window.plugins.tabBar.onItemSelected(%d);", item.tag]; + [self.webView stringByEvaluatingJavaScriptFromString:jsCallBack]; +} + +@end + + +@implementation UIView (NavBarCompat) + +- (void)setTabBarAtBottom:(bool)tabBarAtBottom +{ + objc_setAssociatedObject(self, @"NavBarCompat_tabBarAtBottom", [NSNumber numberWithBool:tabBarAtBottom], OBJC_ASSOCIATION_COPY); +} + +- (bool)tabBarAtBottom +{ + return [(objc_getAssociatedObject(self, @"NavBarCompat_tabBarAtBottom")) boolValue]; +} + +@end diff --git a/iOS/TabBar/2.1.0 (non-ARC)/TabBar.h b/iOS/TabBar/2.1.0 (non-ARC)/TabBar.h new file mode 100644 index 0000000..068f7af --- /dev/null +++ b/iOS/TabBar/2.1.0 (non-ARC)/TabBar.h @@ -0,0 +1,50 @@ +/* + TabBar.h + + Created by Jesse MacFadyen on 10-02-03. + MIT Licensed + + Originally this code was developed my Michael Nachbaur + Formerly -> PhoneGap :: UIControls.h + Created by Michael Nachbaur on 13/04/09. + Copyright 2009 Decaf Ninja Software. All rights reserved. + + API cleaned up and improved by Andreas Sommer (https://github.com/AndiDog/phonegap-plugins). + */ +#import +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVPlugin.h" +#import + +@interface TabBar : CDVPlugin { + UITabBar* tabBar; + + NSMutableDictionary* tabBarItems; + + // Represents frame of web view as if started in portrait mode. Coordinates are relative to the superview. With + // Cordova 2.1.0, frame.origin.y=0 means directly under the status bar, while in older versions it would have been + // frame.origin.y=20. + CGRect originalWebViewFrame; + + CGFloat navBarHeight; + CGFloat tabBarHeight; + bool tabBarAtBottom; +} + +- (void)create:(CDVInvokedUrlCommand*)command; +- (void)show:(CDVInvokedUrlCommand*)command; +- (void)resize:(CDVInvokedUrlCommand*)command; +- (void)hide:(CDVInvokedUrlCommand*)command; +- (void)init:(CDVInvokedUrlCommand*)command; +- (void)showItems:(CDVInvokedUrlCommand*)command; +- (void)createItem:(CDVInvokedUrlCommand*)command; +- (void)updateItem:(CDVInvokedUrlCommand*)command; +- (void)selectItem:(CDVInvokedUrlCommand*)command; + +@end + +@interface UITabBar (NavBarCompat) +@property (nonatomic) bool tabBarAtBottom; +@end \ No newline at end of file diff --git a/iOS/TabBar/2.1.0 (non-ARC)/TabBar.js b/iOS/TabBar/2.1.0 (non-ARC)/TabBar.js new file mode 100644 index 0000000..6518af3 --- /dev/null +++ b/iOS/TabBar/2.1.0 (non-ARC)/TabBar.js @@ -0,0 +1,158 @@ +/* + This code is adapted from the work of: + Created by Michael Nachbaur on 13/04/09. + Copyright 2009 Decaf Ninja Software. All rights reserved. + MIT licensed + + API cleaned up and improved by Andreas Sommer (https://github.com/AndiDog/phonegap-plugins). +*/ + +function TabBar() { + this.tag = 0; + this.callbacks = {}; + this.selectedItem = null; +} + +/** + * Create a native tab bar that can have tab buttons added to it which can respond to events. + * + * @param options Additional options: + * - selectedImageTintColorRgba: Tint color for selected items (defaults to standard light blue), must define the + * color as string e.g. '255,0,0,128' for 50% transparent red. This is only supported on iOS 5 or newer. + * - tintColorRgba: Tint color for the bar itself (value as above) + */ +TabBar.prototype.create = function(options) { + cordova.exec("TabBar.create", options || {}); +}; + +/** + * Create a new tab bar item for use on a previously created tab bar. Use ::showTabBarItems to show the new item on the tab bar. + * + * If the supplied image name is one of the labels listed below, then this method will construct a tab button + * using the standard system buttons. Note that if you use one of the system images, that the \c title you supply will be ignored. + * + * Tab Buttons + * - tabButton:More + * - tabButton:Favorites + * - tabButton:Featured + * - tabButton:TopRated + * - tabButton:Recents + * - tabButton:Contacts + * - tabButton:History + * - tabButton:Bookmarks + * - tabButton:Search + * - tabButton:Downloads + * - tabButton:MostRecent + * - tabButton:MostViewed + * @param {String} name internal name to refer to this tab by + * @param {String} [title] title text to show on the tab, or null if no text should be shown + * @param {String} [image] image filename or internal identifier to show, or null if now image should be shown + * @param {Object} [options] Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden + */ +TabBar.prototype.createItem = function(name, label, image, options) { + + var tag = this.tag++; + if (options && 'onSelect' in options && typeof(options['onSelect']) == 'function') { + this.callbacks[tag] = {'onSelect':options.onSelect,'name':name}; + //delete options.onSelect; + } + + cordova.exec("TabBar.createItem", name, label, image, tag, options); +}; + +/** + * Function to detect currently selected tab bar item + * @see createItem + * @see showItems + */ +TabBar.prototype.getSelectedItem = function() { + return this.selectedItem; +}; + +/** + * Hide a tab bar. The tab bar has to be created first. + */ +TabBar.prototype.hide = function(animate) { + if (animate === undefined || animate === null) + animate = true; + cordova.exec("TabBar.hide", {animate: animate}); +}; + +/** + * Must be called before any other method in order to initialize the plugin. + */ +TabBar.prototype.init = function() +{ + cordova.exec("TabBar.init"); +}; + +/** + * Internal function called when a tab bar item has been selected. + * @param {Number} tag the tag number for the item that has been selected + */ +TabBar.prototype.onItemSelected = function(tag) +{ + this.selectedItem = tag; + if (typeof(this.callbacks[tag].onSelect) == 'function') + this.callbacks[tag].onSelect(this.callbacks[tag].name); +}; + +TabBar.prototype.resize = function() { + cordova.exec("TabBar.resize"); +}; + +/** + * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item. + * @param {String} tabName the name of the tab to select, or null if all tabs should be deselected + * @see createItem + * @see showItems + */ +TabBar.prototype.selectItem = function(tab) { + cordova.exec("TabBar.selectItem", tab); +}; + +/** + * Show a tab bar. The tab bar has to be created first. + * @param {Object} [options] Options indicating how the tab bar should be shown: + * - \c height integer indicating the height of the tab bar (default: \c 49) + * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom) + */ +TabBar.prototype.show = function(options) { + cordova.exec("TabBar.show", options || {}); +}; + +/** + * Show previously created items on the tab bar + * @param {String} arguments... the item names to be shown + * @param {Object} [options] dictionary of options, notable options including: + * - \c animate indicates that the items should animate onto the tab bar + * @see createItem + * @see create + */ +TabBar.prototype.showItems = function() { + var parameters = [ "TabBar.showItems" ]; + for (var i = 0; i < arguments.length; i++) { + parameters.push(arguments[i]); + } + cordova.exec.apply(this, parameters); +}; + +/** + * Update an existing tab bar item to change its badge value. + * @param {String} name internal name used to represent this item when it was created + * @param {Object} options Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if null or unspecified, the badge will be hidden + */ +TabBar.prototype.updateItem = function(name, options) { + if (!options) options = {}; + cordova.exec("TabBar.updateItem", name, options); +}; + +cordova.addConstructor(function() +{ + if(!window.plugins) + window.plugins = {}; + + window.plugins.tabBar = new TabBar(); +}); diff --git a/iOS/TabBar/2.1.0 (non-ARC)/TabBar.m b/iOS/TabBar/2.1.0 (non-ARC)/TabBar.m new file mode 100755 index 0000000..3f786e2 --- /dev/null +++ b/iOS/TabBar/2.1.0 (non-ARC)/TabBar.m @@ -0,0 +1,462 @@ +/* + TabBar.m + + Created by Jesse MacFadyen on 10-02-03. + MIT Licensed + + Originally this code was developed my Michael Nachbaur + Formerly -> PhoneGap :: UIControls.h + Created by Michael Nachbaur on 13/04/09. + Copyright 2009 Decaf Ninja Software. All rights reserved. + + API cleaned up and improved by Andreas Sommer (https://github.com/AndiDog/phonegap-plugins). + */ + +#import +#import "TabBar.h" +#import +#import + +// For older versions of Cordova, you may have to use: #import "CDVDebug.h" +#import + +@implementation TabBar +#ifndef __IPHONE_3_0 +@synthesize webView; +#endif + +-(CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (TabBar*)[super initWithWebView:theWebView]; + if(self) + { + tabBarItems = [[NSMutableDictionary alloc] initWithCapacity:5]; + + // ----------------------------------------------------------------------- + // This code block is the same for both the navigation and tab bar plugin! + // ----------------------------------------------------------------------- + + // The original web view frame must be retrieved here. On iPhone, it would be 0,0,320,460 for example. Since + // Cordova seems to initialize plugins on the first call, there is a plugin method init() that has to be called + // in order to make Cordova call *this* method. If someone forgets the init() call and uses the navigation bar + // and tab bar plugins together, these values won't be the original web view frame and layout will be wrong. + originalWebViewFrame = theWebView.frame; + UIApplication *app = [UIApplication sharedApplication]; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + switch (orientation) + { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + break; + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + { + float statusBarHeight = 0; + if(!app.statusBarHidden) + statusBarHeight = MIN(app.statusBarFrame.size.width, app.statusBarFrame.size.height); + + originalWebViewFrame = CGRectMake(originalWebViewFrame.origin.y, + originalWebViewFrame.origin.x, + originalWebViewFrame.size.height + statusBarHeight, + originalWebViewFrame.size.width - statusBarHeight); + break; + } + default: + NSLog(@"Unknown orientation: %d", orientation); + break; + } + + navBarHeight = 44.0f; + tabBarHeight = 49.0f; + // ----------------------------------------------------------------------- + + tabBarAtBottom = true; + } + return self; +} + +- (void)dealloc +{ + if (tabBar) + [tabBar release]; + + [super dealloc]; +} + +-(void)correctWebViewFrame +{ + if(!tabBar) + return; + + const bool tabBarShown = !tabBar.hidden; + bool navBarShown = false; + + UIView *parent = [tabBar superview]; + for(UIView *view in parent.subviews) + if([view isMemberOfClass:[UINavigationBar class]]) + { + navBarShown = !view.hidden; + break; + } + + // ----------------------------------------------------------------------------- + // IMPORTANT: Below code is the same in both the navigation and tab bar plugins! + // ----------------------------------------------------------------------------- + + CGFloat left = originalWebViewFrame.origin.x; + CGFloat right = left + originalWebViewFrame.size.width; + CGFloat top = originalWebViewFrame.origin.y; + CGFloat bottom = top + originalWebViewFrame.size.height; + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + switch (orientation) + { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + // No need to change width/height from original frame + break; + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + right = left + originalWebViewFrame.size.height + 20.0f; + bottom = top + originalWebViewFrame.size.width - 20.0f; + break; + default: + NSLog(@"Unknown orientation: %d", orientation); + break; + } + + if(navBarShown) + top += navBarHeight; + + if(tabBarShown) + { + if(tabBarAtBottom) + bottom -= tabBarHeight; + else + top += tabBarHeight; + } + + CGRect webViewFrame = CGRectMake(left, top, right - left, bottom - top); + + [self.webView setFrame:webViewFrame]; + + // ----------------------------------------------------------------------------- + + // NOTE: Following part again for tab bar plugin only + + if(tabBarShown) + { + if(tabBarAtBottom) + [tabBar setFrame:CGRectMake(left, bottom, right - left, tabBarHeight)]; + else + [tabBar setFrame:CGRectMake(left, originalWebViewFrame.origin.y, right - left, tabBarHeight)]; + } +} + +- (UIColor*)colorStringToColor:(NSString*)colorStr +{ + NSArray *rgba = [colorStr componentsSeparatedByString:@","]; + return [UIColor colorWithRed:[[rgba objectAtIndex:0] intValue]/255.0f + green:[[rgba objectAtIndex:1] intValue]/255.0f + blue:[[rgba objectAtIndex:2] intValue]/255.0f + alpha:[[rgba objectAtIndex:3] intValue]/255.0f]; +} + +/** + * Create a native tab bar at either the top or the bottom of the display. + */ +- (void)create:(CDVInvokedUrlCommand*)command +{ + tabBar = [UITabBar new]; + [tabBar sizeToFit]; + tabBar.delegate = self; + tabBar.multipleTouchEnabled = NO; + tabBar.autoresizesSubviews = YES; + tabBar.hidden = YES; + tabBar.userInteractionEnabled = YES; + tabBar.opaque = YES; + + const NSDictionary *options = command ? [command.arguments objectAtIndex:0] : nil; + + if(options) + { + id iconTint = [options objectForKey:@"selectedImageTintColorRgba"]; + id tint = [options objectForKey:@"tintColorRgba"]; + + if(iconTint && iconTint != [NSNull null] && [tabBar respondsToSelector:@selector(setSelectedImageTintColor:)]) + [tabBar setSelectedImageTintColor:[self colorStringToColor:iconTint]]; + if(tint && tint != [NSNull null] && [tabBar respondsToSelector:@selector(setTintColor:)]) + [tabBar setTintColor:[self colorStringToColor:tint]]; + } + + self.webView.superview.autoresizesSubviews = YES; + + [self.webView.superview addSubview:tabBar]; +} + +-(void) init:(CDVInvokedUrlCommand*)command +{ + // Dummy function, see initWithWebView +} + +/** + * Show the tab bar after its been created. + * @brief show the tab bar + * @param arguments unused + * @param options used to indicate options for where and how the tab bar should be placed + * - \c height integer indicating the height of the tab bar (default: \c 49) + * - \c position specifies whether the tab bar will be placed at the \c top or \c bottom of the screen (default: \c bottom) + */ +- (void)show:(CDVInvokedUrlCommand*)command +{ + if (!tabBar) + [self create:nil]; + + // if we are calling this again when its shown, reset + if (!tabBar.hidden) + return; + + const NSDictionary *options = [command.arguments objectAtIndex:0]; + + if(options) + { + id tabBarHeightOpt = [options objectForKey:@"height"]; + id positionOpt = [options objectForKey:@"position"]; + + if(tabBarHeightOpt && tabBarHeightOpt != [NSNull null]) + tabBarHeight = [tabBarHeightOpt floatValue]; + + if([positionOpt isKindOfClass:[NSString class]]) + tabBarAtBottom = ![positionOpt isEqualToString:@"top"]; + } + + tabBar.tabBarAtBottom = tabBarAtBottom; + + if(tabBarHeight == 0) + tabBarHeight = 49.0f; + + tabBar.hidden = NO; + + [self correctWebViewFrame]; +} + +/** + * Resize the tab bar (this should be called on orientation change) + * @brief resize the tab bar on rotation + * @param arguments unused + * @param options unused + */ +- (void)resize:(CDVInvokedUrlCommand*)command +{ + [self correctWebViewFrame]; +} + +/** + * Hide the tab bar + * @brief hide the tab bar + * @param arguments unused + * @param options unused + */ +- (void)hide:(CDVInvokedUrlCommand*)command +{ + if (!tabBar) + [self create:nil]; + + tabBar.hidden = YES; + + [self correctWebViewFrame]; +} + +/** + * Create a new tab bar item for use on a previously created tab bar. Use ::showTabBarItems to show the new item on the tab bar. + * + * If the supplied image name is one of the labels listed below, then this method will construct a tab button + * using the standard system buttons. Note that if you use one of the system images, that the \c title you supply will be ignored. + * - Tab Buttons + * - tabButton:More + * - tabButton:Favorites + * - tabButton:Featured + * - tabButton:TopRated + * - tabButton:Recents + * - tabButton:Contacts + * - tabButton:History + * - tabButton:Bookmarks + * - tabButton:Search + * - tabButton:Downloads + * - tabButton:MostRecent + * - tabButton:MostViewed + * @brief create a tab bar item + * @param arguments Parameters used to create the tab bar + * -# \c name internal name to refer to this tab by + * -# \c title title text to show on the tab, or null if no text should be shown + * -# \c image image filename or internal identifier to show, or null if now image should be shown + * -# \c tag unique number to be used as an internal reference to this button + * @param options Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden + */ +- (void)createItem:(CDVInvokedUrlCommand*)command +{ + if (!tabBar) + [self create:nil]; + + const NSDictionary *options = [command.arguments objectAtIndex:4]; + + NSString *name = [command.arguments objectAtIndex:0]; + NSString *title = [command.arguments objectAtIndex:1]; + NSString *imageName = [command.arguments objectAtIndex:2]; + int tag = [[command.arguments objectAtIndex:3] intValue]; + + UITabBarItem *item = nil; + if ([imageName length] > 0) + { + UITabBarSystemItem systemItem = -1; + if ([imageName isEqualToString:@"tabButton:More"]) systemItem = UITabBarSystemItemMore; + else if ([imageName isEqualToString:@"tabButton:Favorites"]) systemItem = UITabBarSystemItemFavorites; + else if ([imageName isEqualToString:@"tabButton:Featured"]) systemItem = UITabBarSystemItemFeatured; + else if ([imageName isEqualToString:@"tabButton:TopRated"]) systemItem = UITabBarSystemItemTopRated; + else if ([imageName isEqualToString:@"tabButton:Recents"]) systemItem = UITabBarSystemItemRecents; + else if ([imageName isEqualToString:@"tabButton:Contacts"]) systemItem = UITabBarSystemItemContacts; + else if ([imageName isEqualToString:@"tabButton:History"]) systemItem = UITabBarSystemItemHistory; + else if ([imageName isEqualToString:@"tabButton:Bookmarks"]) systemItem = UITabBarSystemItemBookmarks; + else if ([imageName isEqualToString:@"tabButton:Search"]) systemItem = UITabBarSystemItemSearch; + else if ([imageName isEqualToString:@"tabButton:Downloads"]) systemItem = UITabBarSystemItemDownloads; + else if ([imageName isEqualToString:@"tabButton:MostRecent"]) systemItem = UITabBarSystemItemMostRecent; + else if ([imageName isEqualToString:@"tabButton:MostViewed"]) systemItem = UITabBarSystemItemMostViewed; + if (systemItem != -1) + item = [[UITabBarItem alloc] initWithTabBarSystemItem:systemItem tag:tag]; + } + + if (item == nil) + item = [[UITabBarItem alloc] initWithTitle:title image:[UIImage imageNamed:imageName] tag:tag]; + + if(options) + { + id badgeOpt = [options objectForKey:@"badge"]; + + if(badgeOpt && badgeOpt != [NSNull null]) + item.badgeValue = [badgeOpt stringValue]; + } + + [tabBarItems setObject:item forKey:name]; + [item release]; +} + + +/** + * Update an existing tab bar item to change its badge value. + * @brief update the badge value on an existing tab bar item + * @param arguments Parameters used to identify the tab bar item to update + * -# \c name internal name used to represent this item when it was created + * @param options Options for customizing the individual tab item + * - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden + */ +- (void)updateItem:(CDVInvokedUrlCommand*)command +{ + if (!tabBar) + [self create:nil]; + + const NSDictionary *options = [command.arguments objectAtIndex:1]; + + if(!options) + { + NSLog(@"Missing options parameter in tabBar.updateItem"); + return; + } + + NSString *name = [command.arguments objectAtIndex:0]; + UITabBarItem *item = [tabBarItems objectForKey:name]; + if(item) + { + id badgeOpt = [options objectForKey:@"badge"]; + + if(badgeOpt && badgeOpt != [NSNull null]) + item.badgeValue = [badgeOpt stringValue]; + } +} + + +/** + * Show previously created items on the tab bar + * @brief show a list of tab bar items + * @param arguments the item names to be shown + * @param options dictionary of options, notable options including: + * - \c animate indicates that the items should animate onto the tab bar + * @see createItem + * @see create + */ +- (void)showItems:(CDVInvokedUrlCommand*)command +{ + if (!tabBar) + [self create:nil]; + + int i, count = [command.arguments count]; + NSDictionary *options = nil; + + NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:MAX(count - 1, 1)]; + + for(i = 0; i < count; ++i) + { + if(i == count - 1 && [[command.arguments objectAtIndex:i] isKindOfClass:[NSDictionary class]]) + { + options = [command.arguments objectAtIndex:i]; + break; + } + + NSString *itemName = [command.arguments objectAtIndex:i]; + UITabBarItem *item = [tabBarItems objectForKey:itemName]; + if(item) + [items addObject:item]; + else + NSLog(@"Cannot show tab with unknown tag '%@'", itemName); + } + + BOOL animateItems = NO; + if(options && [options objectForKey:@"animate"]) + animateItems = [(NSString*)[options objectForKey:@"animate"] boolValue]; + [tabBar setItems:items animated:animateItems]; + [items release]; +} + +/** + * Manually select an individual tab bar item, or nil for deselecting a currently selected tab bar item. + * @brief manually select a tab bar item + * @param arguments the name of the tab bar item to select + * @see createItem + * @see showItems + */ +- (void)selectItem:(CDVInvokedUrlCommand*)command +{ + if (!tabBar) + [self create:nil]; + + NSString *itemName = [command.arguments objectAtIndex:0]; + UITabBarItem *item = [tabBarItems objectForKey:itemName]; + if (item) + tabBar.selectedItem = item; + else + tabBar.selectedItem = nil; +} + +- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item +{ + NSString * jsCallBack = [NSString stringWithFormat:@"window.plugins.tabBar.onItemSelected(%d);", item.tag]; + [self writeJavascript:jsCallBack]; +} + +@end + + +@implementation UIView (NavBarCompat) + +- (void)setTabBarAtBottom:(bool)tabBarAtBottom +{ + objc_setAssociatedObject(self, @"NavBarCompat_tabBarAtBottom", [NSNumber numberWithBool:tabBarAtBottom], OBJC_ASSOCIATION_COPY); +} + +- (bool)tabBarAtBottom +{ + return [(objc_getAssociatedObject(self, @"NavBarCompat_tabBarAtBottom")) boolValue]; +} + +@end diff --git a/iOS/TabBar/README.md b/iOS/TabBar/README.md new file mode 100644 index 0000000..1ab2b01 --- /dev/null +++ b/iOS/TabBar/README.md @@ -0,0 +1,95 @@ +Tab bar for Cordova on iOS +========================== + +This plugin lets you create and control a native tab bar. + +License +------- + +[MIT license](http://www.opensource.org/licenses/mit-license.html) + +Contributors +------------ + +See TabBar.m for the history. + +Installing the plugin +--------------------- + +- Copy *.m and *.h files to your project's "Plugins" folder (should already exist and contain a README file if you used the Cordova project template) +- They have to be added to the project as well, so drag them from the "Plugins" folder (in Finder) to the same folder (in Xcode) and select to create references +- Open "Resources/Cordova.plist" and under "Plugins", add a key with the plugin name "TabBar" and a string value of "TabBar" + +Note regarding the tab bar +-------------------------- + +Don't forget to add an event handler for orientation changes as follows: + + window.addEventListener("resize", function() { + plugins.tabBar.resize(); + ), false); + +Using the tab bar and navigation bar plugin together +---------------------------------------------------- + +In order to use the [tab bar plugin](https://github.com/AndiDog/phonegap-plugins/tree/master/iOS/TabBar) and [navigation bar plugin](https://github.com/AndiDog/phonegap-plugins/tree/master/iOS/NavigationBar) together, you must initialize both plugins before calling any of their methods, i.e. before creating a navigation/tab bar. For example right when your application starts: + + document.addEventListener("deviceready", function() { + console.log("Cordova ready") + + plugins.navigationBar.init() + plugins.tabBar.init() + + plugins.navigationBar.create() + plugins.tabBar.create() + + // ... + +This is because both plugins are aware of each other and resize Cordova's web view accordingly, but therefore they have to know the web view's initial dimensions. If for example you only initialize the tab bar plugin, create the tab bar and later decide to also create a navigation bar, the navigation bar plugin would think the original web view size is 320x411 instead of 320x460 (on iPhone). Layouting *could* be done using the screen size as well but it's currently implemented like this. + +Example +------- + +This example shows how to use the tab bar: + + document.addEventListener("deviceready", function() { + console.log("PhoneGap ready") + + plugins.tabBar.init() + + plugins.tabBar.create() + // or with an orange tint: + plugins.tabBar.create({selectedImageTintColorRgba: '255,40,0,255'}) + + plugins.tabBar.createItem("contacts", "Unused, iOS replaces this text by Contacts", "tabButton:Contacts") + plugins.tabBar.createItem("recents", "Unused, iOS replaces this text by Recents", "tabButton:Recents") + + // Example with selection callback + plugins.tabBar.createItem("another", "Some text", "/www/your-own-image.png", { + onSelect: function() { + alert("another tab selected") + } + }) + + plugins.tabBar.show() + // Or with custom style (defaults to 49px height, positioned at bottom): plugins.tabBar.show({height: 80, position: 'top'}) + plugins.tabBar.showItems("contacts", "recents", "another") + + window.addEventListener("resize", function() { plugins.tabBar.resize() }, false) + }, false) + +Retina images +------------- + +You can also have different images for the normal and retina quality like "image.png" and "image@2x.png". The code to assign the image would be: + + plugins.tabBar.createItem("home", "Home", "image.png", { + onSelect: function() { + alert("tab selected") + } + }) + +Reporting issues or requests for improvement +-------------------------------------------- + +Please report problems on [my GitHub fork of phonegap-plugins](https://github.com/AndiDog/phonegap-plugins). \ No newline at end of file diff --git a/iOS/TabBar/example.png b/iOS/TabBar/example.png new file mode 100644 index 0000000..aa25acf Binary files /dev/null and b/iOS/TabBar/example.png differ diff --git a/iOS/Twitter/native/ios/TwitterPlugin.h b/iOS/Twitter/TwitterPlugin.h similarity index 84% rename from iOS/Twitter/native/ios/TwitterPlugin.h rename to iOS/Twitter/TwitterPlugin.h index d2cd308..2cd222a 100755 --- a/iOS/Twitter/native/ios/TwitterPlugin.h +++ b/iOS/Twitter/TwitterPlugin.h @@ -5,14 +5,10 @@ // Created by Antonelli Brian on 10/13/11. // -#import -#import -#import -#ifdef CORDOVA_FRAMEWORK + #import + #import + #import #import -#else - #import "CDVPlugin.h" -#endif @interface TwitterPlugin : CDVPlugin{ } diff --git a/iOS/Twitter/native/ios/TwitterPlugin.m b/iOS/Twitter/TwitterPlugin.m similarity index 98% rename from iOS/Twitter/native/ios/TwitterPlugin.m rename to iOS/Twitter/TwitterPlugin.m index 98aa190..5eb7937 100755 --- a/iOS/Twitter/native/ios/TwitterPlugin.m +++ b/iOS/Twitter/TwitterPlugin.m @@ -5,14 +5,9 @@ // Created by Antonelli Brian on 10/13/11. // -#import "TwitterPlugin.h" -#ifdef CORDOVA_FRAMEWORK + #import "TwitterPlugin.h" #import #import -#else - #import "JSONKit.h" - #import "CDVAvailability.h" -#endif #define TWITTER_URL @"http://api.twitter.com/1/" @@ -92,7 +87,7 @@ else{ #if TARGET_IPHONE_SIMULATOR - NSString *simWarning = @"Test TwitterPlugin on Real Hardware. Tested on Cordova 1.7.0"; + NSString *simWarning = @"Test TwitterPlugin on Real Hardware. Tested on Cordova 2.0.0"; //EXC_BAD_ACCESS occurs on simulator unable to reproduce on real device //running iOS 5.1 and Cordova 1.6.1 NSLog(@"%@",simWarning); diff --git a/iOS/Twitter/example/www/index.html b/iOS/Twitter/example/www/index.html index c40a334..6849e7f 100644 --- a/iOS/Twitter/example/www/index.html +++ b/iOS/Twitter/example/www/index.html @@ -10,9 +10,18 @@ + +

Hey, it's Twitter on PhoneGap!

+

Apache Cordovaâ„¢

+
+ + +
+ +
  1. isAvailable
  2. diff --git a/iOS/Twitter/example/www2.0/TwitterPlugin.js b/iOS/Twitter/example/www2.0/TwitterPlugin.js new file mode 100644 index 0000000..a4c64ff --- /dev/null +++ b/iOS/Twitter/example/www2.0/TwitterPlugin.js @@ -0,0 +1,140 @@ +/** + * @constructor + */ +var Twitter = function(){}; +/** + * Checks if the Twitter SDK is loaded + * @param {Function} response callback on result + * @param {Number} response.response is 1 for success, 0 for failure + * @example + * window.plugins.twitter.isTwitterAvailable(function (response) { + * console.log("twitter available? " + response); + * }); + */ +Twitter.prototype.isTwitterAvailable = function(response){ + cordova.exec(response, null, "TwitterPlugin", "isTwitterAvailable", []); +}; +/** + * Checks if the Twitter SDK can send a tweet + * @param {Function} response callback on result + * @param {Number} response.response is 1 for success, 0 for failure + * @example + * window.plugins.twitter.isTwitterSetup(function (r) { + * console.log("twitter configured? " + r); + * }); + */ +Twitter.prototype.isTwitterSetup = function(response){ + cordova.exec(response, null, "TwitterPlugin", "isTwitterSetup", []); +}; +/** + * Sends a Tweet to Twitter + * @param {Function} success callback + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @param {String} tweetText message to send to twitter + * @param {Object} options (optional) + * @param {String} options.urlAttach URL to embed in Tweet + * @param {String} options.imageAttach Image URL to embed in Tweet + * @param {Number} response.response - 1 on success, 0 on failure + * @example + * window.plugins.twitter.composeTweet( + * function () { console.log("tweet success"); }, + * function (error) { console.log("tweet failure: " + error); }, + * "Text, Image, URL", + * { + * urlAttach:"http://m.youtube.com/#/watch?v=obx2VOtx0qU", + * imageAttach:"http://i.ytimg.com/vi/obx2VOtx0qU/hqdefault.jpg?w=320&h=192&sigh=QD3HYoJj9dtiytpCSXhkaq1oG8M" + * } + * ); + */ +Twitter.prototype.composeTweet = function(success, failure, tweetText, options){ + options = options || {}; + options.text = tweetText; + cordova.exec(success, failure, "TwitterPlugin", "composeTweet", [options]); +}; +/** + * Gets Tweets from Twitter Timeline + * @param {Function} success callback + * @param {Object[]} success.response Tweet objects, see [Twitter Timeline Doc] + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @example + * window.plugins.twitter.getPublicTimeline( + * function (response) { console.log("timeline success: " + JSON.stringify(response)); }, + * function (error) { console.log("timeline failure: " + error); } + * ); + * + * [Twitter Timeline Doc]: https://dev.twitter.com/docs/api/1/get/statuses/public_timeline + */ +Twitter.prototype.getPublicTimeline = function(success, failure){ + cordova.exec(success, failure, "TwitterPlugin", "getPublicTimeline", []); +}; +/** + * Gets Tweets from Twitter Mentions + * @param {Function} success callback + * @param {Object[]} success.result Tweet objects, see [Twitter Mentions Doc] + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @example + * window.plugins.twitter.getMentions( + * function (response) { console.log("mentions success: " + JSON.stringify(response)); }, + * function (error) { console.log("mentions failure: " + error); } + * ); + * + * [Twitter Timeline Doc]: https://dev.twitter.com/docs/api/1/get/statuses/public_timeline + */ +Twitter.prototype.getMentions = function(success, failure){ + cordova.exec(success, failure, "TwitterPlugin", "getMentions", []); +}; +/** + * Gets Tweets from Twitter Mentions API + * @param {Function} success callback + * @param {String} success.response Twitter Username + * @param {Object[]} success.result Tweet objects, see [Twitter Mentions Doc] + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * + * [Twitter Mentions Doc]: https://dev.twitter.com/docs/api/1/get/statuses/mentions + */ +Twitter.prototype.getTwitterUsername = function(success, failure) { + cordova.exec(success, failure, "TwitterPlugin", "getTwitterUsername", []); +}; +/** + * Gets Tweets from Twitter Mentions API + * @param {String} url of [Twitter API Endpoint] + * @param {Object} params key-value map, matching [Twitter API Endpoint] + * @param {Function} success callback + * @param {Object[]} success.response objects returned from Twitter API (Tweets, Users,...) + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @param {Object} options (optional) other options for the HTTP request + * @param {String} options.requestMethod HTTP Request type, ex: "POST" + * @example + * window.plugins.twitter.getTWRequest( + * 'users/lookup.json', + * {user_id: '16141659,783214,6253282'}, + * function (response) { console.log("usersLookup success: " + JSON.stringify(response)); }, + * function (error) { console.log("usersLookup failure: " + error); }, + * {requestMethod: 'POST'} + * ); + * + * [Twitter API Endpoints]: https://dev.twitter.com/docs/api + */ +Twitter.prototype.getTWRequest = function(url, params, success, failure, options){ + options = options || {}; + options.url = url; + options.params = params; + cordova.exec(success, failure, "TwitterPlugin", "getTWRequest", [options]); +}; +// Plug in to Cordova +cordova.addConstructor(function() { + + /* shim to work in 1.5 and 1.6 */ + if (!window.Cordova) { + window.Cordova = cordova; + }; + + + if(!window.plugins) window.plugins = {}; + window.plugins.twitter = new Twitter(); + }); diff --git a/iOS/Twitter/example/www2.0/config.xml b/iOS/Twitter/example/www2.0/config.xml new file mode 100644 index 0000000..a7e35db --- /dev/null +++ b/iOS/Twitter/example/www2.0/config.xml @@ -0,0 +1,47 @@ + + + Hello Cordova + + + A sample Apache Cordova application that responds to the deviceready event. + + + + Apache Cordova Team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOS/Twitter/example/www2.0/cordova-2.0.0.js b/iOS/Twitter/example/www2.0/cordova-2.0.0.js new file mode 100644 index 0000000..c2caa2f --- /dev/null +++ b/iOS/Twitter/example/www2.0/cordova-2.0.0.js @@ -0,0 +1,5240 @@ +// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0 + +// File generated at :: Wed Jul 18 2012 16:47:25 GMT-0700 (PDT) + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +;(function() { + +// file: lib/scripts/require.js +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + }; + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + }; + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} +// file: lib/cordova.js +define("cordova", function(require, exports, module) { +var channel = require('cordova/channel'); + +/** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ +document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); +}, false); +if (document.readyState == 'complete' || document.readyState == 'interactive') { + channel.onDOMContentLoaded.fire(); +} + +/** + * Intercept calls to addEventListener + removeEventListener and handle deviceready, + * resume, and pause events. + */ +var m_document_addEventListener = document.addEventListener; +var m_document_removeEventListener = document.removeEventListener; +var m_window_addEventListener = window.addEventListener; +var m_window_removeEventListener = window.removeEventListener; + +/** + * Houses custom event handlers to intercept on document + window event listeners. + */ +var documentEventHandlers = {}, + windowEventHandlers = {}; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof documentEventHandlers[e] != 'undefined') { + if (evt === 'deviceready') { + documentEventHandlers[e].subscribeOnce(handler); + } else { + documentEventHandlers[e].subscribe(handler); + } + } else { + m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (typeof windowEventHandlers[e] != 'undefined') { + windowEventHandlers[e].subscribe(handler); + } else { + m_window_addEventListener.call(window, evt, handler, capture); + } +}; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof documentEventHandlers[e] != "undefined") { + documentEventHandlers[e].unsubscribe(handler); + } else { + m_document_removeEventListener.call(document, evt, handler, capture); + } +}; + +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + // If unsubcribing from an event that is handled by a plugin + if (typeof windowEventHandlers[e] != "undefined") { + windowEventHandlers[e].unsubscribe(handler); + } else { + m_window_removeEventListener.call(window, evt, handler, capture); + } +}; + +function createEvent(type, data) { + var event = document.createEvent('Events'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + return event; +} + +if(typeof window.console === "undefined") { + window.console = { + log:function(){} + }; +} + +var cordova = { + define:define, + require:require, + /** + * Methods to add/remove your own addEventListener hijacking on document + window. + */ + addWindowEventHandler:function(event, opts) { + return (windowEventHandlers[event] = channel.create(event, opts)); + }, + addDocumentEventHandler:function(event, opts) { + return (documentEventHandlers[event] = channel.create(event, opts)); + }, + removeWindowEventHandler:function(event) { + delete windowEventHandlers[event]; + }, + removeDocumentEventHandler:function(event) { + delete documentEventHandlers[event]; + }, + /** + * Retreive original event handlers that were replaced by Cordova + * + * @return object + */ + getOriginalHandlers: function() { + return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener}, + 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}}; + }, + /** + * Method to fire event from native code + */ + fireDocumentEvent: function(type, data) { + var evt = createEvent(type, data); + if (typeof documentEventHandlers[type] != 'undefined') { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } else { + document.dispatchEvent(evt); + } + }, + fireWindowEvent: function(type, data) { + var evt = createEvent(type,data); + if (typeof windowEventHandlers[type] != 'undefined') { + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); + } else { + window.dispatchEvent(evt); + } + }, + // TODO: this is Android only; think about how to do this better + shuttingDown:false, + UsePolling:false, + // END TODO + + // TODO: iOS only + // This queue holds the currently executing command and all pending + // commands executed with cordova.exec(). + commandQueue:[], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing:false, + // END TODO + /** + * Plugin callback mechanism. + */ + callbackId: 0, + callbacks: {}, + callbackStatus: { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }, + + /** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ + callbackSuccess: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == cordova.callbackStatus.OK) { + try { + if (cordova.callbacks[callbackId].success) { + cordova.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + + /** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ + callbackError: function(callbackId, args) { + if (cordova.callbacks[callbackId]) { + try { + if (cordova.callbacks[callbackId].fail) { + cordova.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + }, + addConstructor: function(func) { + channel.onCordovaReady.subscribeOnce(function() { + try { + func(); + } catch(e) { + console.log("Failed to run constructor: " + e); + } + }); + } +}; + +// Register pause, resume and deviceready channels as events on document. +channel.onPause = cordova.addDocumentEventHandler('pause'); +channel.onResume = cordova.addDocumentEventHandler('resume'); +channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); + +module.exports = cordova; + +}); + +// file: lib/common/builder.js +define("cordova/builder", function(require, exports, module) { +var utils = require('cordova/utils'); + +function each(objects, func, context) { + for (var prop in objects) { + if (objects.hasOwnProperty(prop)) { + func.apply(context, [objects[prop], prop]); + } + } +} + +function include(parent, objects, clobber, merge) { + each(objects, function (obj, key) { + try { + var result = obj.path ? require(obj.path) : {}; + + if (clobber) { + // Clobber if it doesn't exist. + if (typeof parent[key] === 'undefined') { + parent[key] = result; + } else if (typeof obj.path !== 'undefined') { + // If merging, merge properties onto parent, otherwise, clobber. + if (merge) { + recursiveMerge(parent[key], result); + } else { + parent[key] = result; + } + } + result = parent[key]; + } else { + // Overwrite if not currently defined. + if (typeof parent[key] == 'undefined') { + parent[key] = result; + } else if (merge && typeof obj.path !== 'undefined') { + // If merging, merge parent onto result + recursiveMerge(result, parent[key]); + parent[key] = result; + } else { + // Set result to what already exists, so we can build children into it if they exist. + result = parent[key]; + } + } + + if (obj.children) { + include(result, obj.children, clobber, merge); + } + } catch(e) { + utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"'); + } + }); +} + +/** + * Merge properties from one object onto another recursively. Properties from + * the src object will overwrite existing target property. + * + * @param target Object to merge properties into. + * @param src Object to merge properties from. + */ +function recursiveMerge(target, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) { + if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) { + // If the target object is a constructor override off prototype. + target.prototype[prop] = src[prop]; + } else { + target[prop] = typeof src[prop] === 'object' ? recursiveMerge( + target[prop], src[prop]) : src[prop]; + } + } + } + return target; +} + +module.exports = { + build: function (objects) { + return { + intoButDontClobber: function (target) { + include(target, objects, false, false); + }, + intoAndClobber: function(target) { + include(target, objects, true, false); + }, + intoAndMerge: function(target) { + include(target, objects, true, true); + } + }; + } +}; + +}); + +// file: lib/common/channel.js +define("cordova/channel", function(require, exports, module) { +var utils = require('cordova/utils'); + +/** + * Custom pub-sub "channel" that can have functions subscribed to it + * This object is used to define and control firing of events for + * cordova initialization. + * + * The order of events during page load and Cordova startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * onNativeReady Internal event that indicates the Cordova native side is ready. + * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created. + * onCordovaInfoReady Internal event fired when device properties are available. + * onCordovaConnectionReady Internal event fired when the connection property has been set. + * onDeviceReady User event fired to indicate that Cordova is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only Cordova events that user code should register for are: + * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript + * pause App has moved to background + * resume App has returned to foreground + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + * + * The DOM lifecycle events should be used for saving and restoring state + * window.onload + * window.onunload + * + */ + +/** + * Channel + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = {}; + this.numHandlers = 0; + this.guid = 1; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } +}, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + if (this.id === null) { + fail(ContactError.UNKNOWN_ERROR); + } + else { + exec(successCB, fail, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + var fail = function(code) { + errorCB(new ContactError(code)); + }; + var success = function(result) { + if (result) { + if (typeof successCB === 'function') { + var fullContact = require('cordova/plugin/contacts').create(result); + successCB(convertIn(fullContact)); + } + } + else { + // no Entry object returned + fail(ContactError.UNKNOWN_ERROR); + } + }; + var dupContact = convertOut(utils.clone(this)); + exec(success, fail, "Contacts", "save", [dupContact]); +}; + + +module.exports = Contact; + +}); + +// file: lib/common/plugin/ContactAddress.js +define("cordova/plugin/ContactAddress", function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; +}); + +// file: lib/common/plugin/ContactError.js +define("cordova/plugin/ContactError", function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; +}); + +// file: lib/common/plugin/ContactField.js +define("cordova/plugin/ContactField", function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = (type && type.toString()) || null; + this.value = (value && value.toString()) || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; +}); + +// file: lib/common/plugin/ContactFindOptions.js +define("cordova/plugin/ContactFindOptions", function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; +}); + +// file: lib/common/plugin/ContactName.js +define("cordova/plugin/ContactName", function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; +}); + +// file: lib/common/plugin/ContactOrganization.js +define("cordova/plugin/ContactOrganization", function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; +}); + +// file: lib/common/plugin/Coordinates.js +define("cordova/plugin/Coordinates", function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = (alt !== undefined ? alt : null); + /** + * The direction the device is moving at the position. + */ + this.heading = (head !== undefined ? head : null); + /** + * The velocity with which the device is moving at the position. + */ + this.speed = (vel !== undefined ? vel : null); + + if (this.speed === 0 || this.speed === null) { + this.heading = NaN; + } + + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +// file: lib/common/plugin/DirectoryEntry.js +define("cordova/plugin/DirectoryEntry", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + FileError = require('cordova/plugin/FileError'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = typeof errorCallback !== 'function' ? null : function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +// file: lib/common/plugin/DirectoryReader.js +define("cordova/plugin/DirectoryReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError') ; + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = typeof successCallback !== 'function' ? null : function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('method "readAsBinaryString" is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; +}); + +// file: lib/common/plugin/FileSystem.js +define("cordova/plugin/FileSystem", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(root.name, root.fullPath); + } +}; + +module.exports = FileSystem; + +}); + +// file: lib/common/plugin/FileTransfer.js +define("cordova/plugin/FileTransfer", function(require, exports, module) { +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new (require('cordova/plugin/DirectoryEntry'))(); + } + else if (result.isFile) { + entry = new (require('cordova/plugin/FileEntry'))(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +// file: lib/common/plugin/FileTransferError.js +define("cordova/plugin/FileTransferError", function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code, source, target, status) { + this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +// file: lib/common/plugin/FileUploadOptions.js +define("cordova/plugin/FileUploadOptions", function(require, exports, module) { +/** + * Options to customize the HTTP request used to upload files. + * @constructor + * @param fileKey {String} Name of file request parameter. + * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. + * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. + * @param params {Object} Object with key: value params to send to the server. + */ +var FileUploadOptions = function(fileKey, fileName, mimeType, params) { + this.fileKey = fileKey || null; + this.fileName = fileName || null; + this.mimeType = mimeType || null; + this.params = params || null; +}; + +module.exports = FileUploadOptions; +}); + +// file: lib/common/plugin/FileUploadResult.js +define("cordova/plugin/FileUploadResult", function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; +}); + +// file: lib/common/plugin/FileWriter.js +define("cordova/plugin/FileWriter", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // set error + this.error = new FileError(FileError.ABORT_ERR); + + this.readyState = FileWriter.DONE; + + // If abort callback + if (typeof this.onabort === "function") { + this.onabort(new ProgressEvent("abort", {"target":this})); + } + + // If write end callback + if (typeof this.onwriteend === "function") { + this.onwriteend(new ProgressEvent("writeend", {"target":this})); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":me})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + + me.length = me.position; + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "write", [this.fileName, text, this.position]); +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + if (!offset && offset !== 0) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger then file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart(new ProgressEvent("writestart", {"target":this})); + } + + // Write file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite(new ProgressEvent("write", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // DONE state + me.readyState = FileWriter.DONE; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {"target":me})); + } + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend(new ProgressEvent("writeend", {"target":me})); + } + }, "File", "truncate", [this.fileName, size]); +}; + +module.exports = FileWriter; + +}); + +// file: lib/common/plugin/Flags.js +define("cordova/plugin/Flags", function(require, exports, module) { +/** + * Supplies arguments to methods that lookup or create files and directories. + * + * @param create + * {boolean} file or directory if it doesn't exist + * @param exclusive + * {boolean} used with create; if true the command will fail if + * target path exists + */ +function Flags(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +} + +module.exports = Flags; +}); + +// file: lib/common/plugin/LocalFileSystem.js +define("cordova/plugin/LocalFileSystem", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; +}); + +// file: lib/common/plugin/Media.js +define("cordova/plugin/Media", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +var mediaObjects = {}; + +/** + * This class provides access to the device media, interfaces to both sound and video + * + * @constructor + * @param src The file name or url to play + * @param successCallback The callback to be called when the file is done playing or recording. + * successCallback() + * @param errorCallback The callback to be called if there is an error. + * errorCallback(int errorCode) - OPTIONAL + * @param statusCallback The callback to be called when media status has changed. + * statusCallback(int statusCode) - OPTIONAL + */ +var Media = function(src, successCallback, errorCallback, statusCallback) { + + // successCallback optional + if (successCallback && (typeof successCallback !== "function")) { + console.log("Media Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Media Error: errorCallback is not a function"); + return; + } + + // statusCallback optional + if (statusCallback && (typeof statusCallback !== "function")) { + console.log("Media Error: statusCallback is not a function"); + return; + } + + this.id = utils.createUUID(); + mediaObjects[this.id] = this; + this.src = src; + this.successCallback = successCallback; + this.errorCallback = errorCallback; + this.statusCallback = statusCallback; + this._duration = -1; + this._position = -1; + exec(null, this.errorCallback, "Media", "create", [this.id, this.src]); +}; + +// Media messages +Media.MEDIA_STATE = 1; +Media.MEDIA_DURATION = 2; +Media.MEDIA_POSITION = 3; +Media.MEDIA_ERROR = 9; + +// Media states +Media.MEDIA_NONE = 0; +Media.MEDIA_STARTING = 1; +Media.MEDIA_RUNNING = 2; +Media.MEDIA_PAUSED = 3; +Media.MEDIA_STOPPED = 4; +Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; + +// "static" function to return existing objs. +Media.get = function(id) { + return mediaObjects[id]; +}; + +/** + * Start or resume playing audio file. + */ +Media.prototype.play = function(options) { + exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]); +}; + +/** + * Stop playing audio file. + */ +Media.prototype.stop = function() { + var me = this; + exec(function() { + me._position = 0; + me.successCallback(); + }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); +}; + +/** + * Seek or jump to a new time in the track.. + */ +Media.prototype.seekTo = function(milliseconds) { + var me = this; + exec(function(p) { + me._position = p; + }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]); +}; + +/** + * Pause playing audio file. + */ +Media.prototype.pause = function() { + exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]); +}; + +/** + * Get duration of an audio file. + * The duration is only set for audio that is playing, paused or stopped. + * + * @return duration or -1 if not known. + */ +Media.prototype.getDuration = function() { + return this._duration; +}; + +/** + * Get position of audio. + */ +Media.prototype.getCurrentPosition = function(success, fail) { + var me = this; + exec(function(p) { + me._position = p; + success(p); + }, fail, "Media", "getCurrentPositionAudio", [this.id]); +}; + +/** + * Start recording audio file. + */ +Media.prototype.startRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); +}; + +/** + * Stop recording audio file. + */ +Media.prototype.stopRecord = function() { + exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); +}; + +/** + * Release the resources. + */ +Media.prototype.release = function() { + exec(null, this.errorCallback, "Media", "release", [this.id]); +}; + +/** + * Adjust the volume. + */ +Media.prototype.setVolume = function(volume) { + exec(null, null, "Media", "setVolume", [this.id, volume]); +}; + +/** + * Audio has status update. + * PRIVATE + * + * @param id The media object id (string) + * @param status The status code (int) + * @param msg The status message (string) + */ +Media.onStatus = function(id, msg, value) { + var media = mediaObjects[id]; + // If state update + if (msg === Media.MEDIA_STATE) { + if (value === Media.MEDIA_STOPPED) { + if (media.successCallback) { + media.successCallback(); + } + } + if (media.statusCallback) { + media.statusCallback(value); + } + } + else if (msg === Media.MEDIA_DURATION) { + media._duration = value; + } + else if (msg === Media.MEDIA_ERROR) { + if (media.errorCallback) { + // value should be a MediaError object when msg == MEDIA_ERROR + media.errorCallback(value); + } + } + else if (msg === Media.MEDIA_POSITION) { + media._position = value; + } +}; + +module.exports = Media; +}); + +// file: lib/common/plugin/MediaError.js +define("cordova/plugin/MediaError", function(require, exports, module) { +/** + * This class contains information about any Media errors. + * @constructor + */ +var MediaError = function(code, msg) { + this.code = (code !== undefined ? code : null); + this.message = msg || ""; +}; + +MediaError.MEDIA_ERR_NONE_ACTIVE = 0; +MediaError.MEDIA_ERR_ABORTED = 1; +MediaError.MEDIA_ERR_NETWORK = 2; +MediaError.MEDIA_ERR_DECODE = 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; + +module.exports = MediaError; +}); + +// file: lib/common/plugin/MediaFile.js +define("cordova/plugin/MediaFile", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + File = require('cordova/plugin/File'), + CaptureError = require('cordova/plugin/CaptureError'); +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + MediaFile.__super__.constructor.apply(this, arguments); +}; + +utils.extend(MediaFile, File); + +/** + * Request capture format data for a specific file and type + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { + if (typeof this.fullPath === "undefined" || this.fullPath === null) { + errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT)); + } else { + exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); + } +}; + +// TODO: can we axe this? +/** + * Casts a PluginResult message property (array of objects) to an array of MediaFile objects + * (used in Objective-C and Android) + * + * @param {PluginResult} pluginResult + */ +MediaFile.cast = function(pluginResult) { + var mediaFiles = []; + for (var i=0; i.dispatchEvent + // need to first figure out how to implement EventTarget + } + } + return event; + }; + try { + var ev = createEvent({type:"abort",target:document}); + return function ProgressEvent(type, data) { + data.type = type; + return createEvent(data); + }; + } catch(e){ + */ + return function ProgressEvent(type, dict) { + this.type = type; + this.bubbles = false; + this.cancelBubble = false; + this.cancelable = false; + this.lengthComputable = false; + this.loaded = dict && dict.loaded ? dict.loaded : 0; + this.total = dict && dict.total ? dict.total : 0; + this.target = dict && dict.target ? dict.target : null; + }; + //} +})(); + +module.exports = ProgressEvent; +}); + +// file: lib/common/plugin/accelerometer.js +define("cordova/plugin/accelerometer", function(require, exports, module) { +/** + * This class provides access to device accelerometer data. + * @constructor + */ +var utils = require("cordova/utils"), + exec = require("cordova/exec"), + Acceleration = require('cordova/plugin/Acceleration'); + +// Is the accel sensor running? +var running = false; + +// Keeps reference to watchAcceleration calls. +var timers = {}; + +// Array of listeners; used to keep track of when we should call start and stop. +var listeners = []; + +// Last returned acceleration object from native +var accel = null; + +// Tells native to start. +function start() { + exec(function(a) { + var tempListeners = listeners.slice(0); + accel = new Acceleration(a.x, a.y, a.z, a.timestamp); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].win(accel); + } + }, function(e) { + var tempListeners = listeners.slice(0); + for (var i = 0, l = tempListeners.length; i < l; i++) { + tempListeners[i].fail(e); + } + }, "Accelerometer", "start", []); + running = true; +} + +// Tells native to stop. +function stop() { + exec(null, null, "Accelerometer", "stop", []); + running = false; +} + +// Adds a callback pair to the listeners array +function createCallbackPair(win, fail) { + return {win:win, fail:fail}; +} + +// Removes a win/fail listener pair from the listeners array +function removeListeners(l) { + var idx = listeners.indexOf(l); + if (idx > -1) { + listeners.splice(idx, 1); + if (listeners.length === 0) { + stop(); + } + } +} + +var accelerometer = { + /** + * Asynchronously aquires the current acceleration. + * + * @param {Function} successCallback The function to call when the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + */ + getCurrentAcceleration: function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + throw "getCurrentAcceleration must be called with at least a success callback function as first parameter."; + } + + var p; + var win = function(a) { + removeListeners(p); + successCallback(a); + }; + var fail = function(e) { + removeListeners(p); + errorCallback(e); + }; + + p = createCallbackPair(win, fail); + listeners.push(p); + + if (!running) { + start(); + } + }, + + /** + * Asynchronously aquires the acceleration repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the acceleration data is available + * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL) + * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchAcceleration: function(successCallback, errorCallback, options) { + // Default interval (10 sec) + var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000; + + // successCallback required + if (typeof successCallback !== "function") { + throw "watchAcceleration must be called with at least a success callback function as first parameter."; + } + + // Keep reference to watch id, and report accel readings as often as defined in frequency + var id = utils.createUUID(); + + var p = createCallbackPair(function(){}, function(e) { + removeListeners(p); + errorCallback(e); + }); + listeners.push(p); + + timers[id] = { + timer:window.setInterval(function() { + if (accel) { + successCallback(accel); + } + }, frequency), + listeners:p + }; + + if (running) { + // If we're already running then immediately invoke the success callback + // but only if we have retreived a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } + } else { + start(); + } + + return id; + }, + + /** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ + clearWatch: function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + window.clearInterval(timers[id].timer); + removeListeners(timers[id].listeners); + delete timers[id]; + } + } +}; + +module.exports = accelerometer; + +}); + +// file: lib/common/plugin/battery.js +define("cordova/plugin/battery", function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + return battery.channels.batterystatus.numHandlers + + battery.channels.batterylow.numHandlers + + battery.channels.batterycritical.numHandlers; +} + +var Battery = function() { + this._level = null; + this._isPlugged = null; + // Create new event handlers on the window (returns a channel instance) + var subscriptionEvents = { + onSubscribe:this.onSubscribe, + onUnsubscribe:this.onUnsubscribe + }; + this.channels = { + batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents), + batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents), + batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents) + }; +}; +/** + * Event handlers for when callbacks get registered for the battery. + * Keep track of how many handlers we have so we can start and stop the native battery listener + * appropriately (and hopefully save on battery life!). + */ +Battery.prototype.onSubscribe = function() { + var me = battery; + // If we just registered the first handler, make sure native listener is started. + if (handlers() === 1) { + exec(me._status, me._error, "Battery", "start", []); + } +}; + +Battery.prototype.onUnsubscribe = function() { + var me = battery; + + // If we just unregistered the last handler, make sure native listener is stopped. + if (handlers() === 0) { + exec(null, null, "Battery", "stop", []); + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = battery; + var level = info.level; + if (me._level !== level || me._isPlugged !== info.isPlugged) { + // Fire batterystatus event + cordova.fireWindowEvent("batterystatus", info); + + // Fire low battery event + if (level === 20 || level === 5) { + if (level === 20) { + cordova.fireWindowEvent("batterylow", info); + } + else { + cordova.fireWindowEvent("batterycritical", info); + } + } + } + me._level = level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +var battery = new Battery(); + +module.exports = battery; +}); + +// file: lib/common/plugin/capture.js +define("cordova/plugin/capture", function(require, exports, module) { +var exec = require('cordova/exec'), + MediaFile = require('cordova/plugin/MediaFile'); + +/** + * Launches a capture of different types. + * + * @param (DOMString} type + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +function _capture(type, successCallback, errorCallback, options) { + var win = function(pluginResult) { + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult[i].name; + mediaFile.fullPath = pluginResult[i].fullPath; + mediaFile.type = pluginResult[i].type; + mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate; + mediaFile.size = pluginResult[i].size; + mediaFiles.push(mediaFile); + } + successCallback(mediaFiles); + }; + exec(win, errorCallback, "Capture", type, [options]); +} +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +} + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + _capture("captureAudio", successCallback, errorCallback, options); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + _capture("captureImage", successCallback, errorCallback, options); +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + _capture("captureVideo", successCallback, errorCallback, options); +}; + + +module.exports = new Capture(); + +}); + +// file: lib/common/plugin/compass.js +define("cordova/plugin/compass", function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + CompassHeading = require('cordova/plugin/CompassHeading'), + CompassError = require('cordova/plugin/CompassError'), + timers = {}, + compass = { + /** + * Asynchronously acquires the current heading. + * @param {Function} successCallback The function to call when the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {CompassOptions} options The options for getting the heading data (not used). + */ + getCurrentHeading:function(successCallback, errorCallback, options) { + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var win = function(result) { + var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp); + successCallback(ch); + }; + var fail = function(code) { + var ce = new CompassError(code); + errorCallback(ce); + }; + + // Get heading + exec(win, fail, "Compass", "getHeading", [options]); + }, + + /** + * Asynchronously acquires the heading repeatedly at a given interval. + * @param {Function} successCallback The function to call each time the heading + * data is available + * @param {Function} errorCallback The function to call when there is an error + * getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data + * such as timeout and the frequency of the watch. For iOS, filter parameter + * specifies to watch via a distance filter rather than time. + */ + watchHeading:function(successCallback, errorCallback, options) { + // Default interval (100 msec) + var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100; + var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + var id = utils.createUUID(); + if (filter > 0) { + // is an iOS request for watch by filter, no timer needed + timers[id] = "iOS"; + compass.getCurrentHeading(successCallback, errorCallback, options); + } else { + // Start watch timer to get headings + timers[id] = window.setInterval(function() { + compass.getCurrentHeading(successCallback, errorCallback); + }, frequency); + } + + return id; + }, + + /** + * Clears the specified heading watch. + * @param {String} watchId The ID of the watch returned from #watchHeading. + */ + clearWatch:function(id) { + // Stop javascript timer & remove from timer list + if (id && timers[id]) { + if (timers[id] != "iOS") { + clearInterval(timers[id]); + } else { + // is iOS watch by filter so call into device to stop + exec(null, null, "Compass", "stopHeading", []); + } + delete timers[id]; + } + } + }; + +module.exports = compass; +}); + +// file: lib/common/plugin/console-via-logger.js +define("cordova/plugin/console-via-logger", function(require, exports, module) { +//------------------------------------------------------------------------------ + +var logger = require("cordova/plugin/logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = utils.vformat(arguments[1], [].slice.call(arguments, 2)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrapperedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrapperedOrigCall(WinConsole[key], console[key]); + } +} + +}); + +// file: lib/common/plugin/contacts.js +define("cordova/plugin/contacts", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'), + Contact = require('cordova/plugin/Contact'); + +/** +* Represents a group of Contacts. +* @constructor +*/ +var contacts = { + /** + * Returns an array of Contacts matching the search criteria. + * @param fields that should be searched + * @param successCB success callback + * @param errorCB error callback + * @param {ContactFindOptions} options that can be applied to contact searching + * @return array of Contacts matching search criteria + */ + find:function(fields, successCB, errorCB, options) { + if (!successCB) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (!fields || (utils.isArray(fields) && fields.length === 0)) { + if (typeof errorCB === "function") { + errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR)); + } + } else { + var win = function(result) { + var cs = []; + for (var i = 0, l = result.length; i < l; i++) { + cs.push(contacts.create(result[i])); + } + successCB(cs); + }; + exec(win, errorCB, "Contacts", "search", [fields, options]); + } + }, + + /** + * This function creates a new contact, but it does not persist the contact + * to device storage. To persist the contact to device storage, invoke + * contact.save(). + * @param properties an object who's properties will be examined to create a new Contact + * @returns new Contact object + */ + create:function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) { + contact[i] = properties[i]; + } + } + return contact; + } +}; + +module.exports = contacts; + +}); + +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.name = null; + this.uuid = null; + this.cordova = null; + + var me = this; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/geolocation.js +define("cordova/plugin/geolocation", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'), + Position = require('cordova/plugin/Position'); + +var timers = {}; // list of timers in use + +// Returns default params, overrides if provided with values +function parseParameters(options) { + var opt = { + maximumAge: 0, + enableHighAccuracy: false, + timeout: Infinity + }; + + if (options) { + if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) { + opt.maximumAge = options.maximumAge; + } + if (options.enableHighAccuracy !== undefined) { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (options.timeout !== undefined && !isNaN(options.timeout)) { + if (options.timeout < 0) { + opt.timeout = 0; + } else { + opt.timeout = options.timeout; + } + } + } + + return opt; +} + +// Returns a timeout failure, closed over a specified timeout value and error callback. +function createTimeout(errorCallback, timeout) { + var t = setTimeout(function() { + clearTimeout(t); + t = null; + errorCallback({ + code:PositionError.TIMEOUT, + message:"Position retrieval timed out." + }); + }, timeout); + return t; +} + +var geolocation = { + lastPosition:null, // reference to last known (cached) position returned + /** + * Asynchronously aquires the current position. + * + * @param {Function} successCallback The function to call when the position data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) + * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) + */ + getCurrentPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("getCurrentPosition must be called with at least one argument."); + } + options = parseParameters(options); + + // Timer var that will fire an error callback if no position is retrieved from native + // before the "timeout" param provided expires + var timeoutTimer = null; + + var win = function(p) { + clearTimeout(timeoutTimer); + if (!timeoutTimer) { + // Timeout already happened, or native fired error callback for + // this geo request. + // Don't continue with success callback. + return; + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + var fail = function(e) { + clearTimeout(timeoutTimer); + timeoutTimer = null; + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just + // fire the success callback with the cached position. + if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) { + successCallback(geolocation.lastPosition); + // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object. + } else if (options.timeout === 0) { + fail({ + code:PositionError.TIMEOUT, + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + }); + // Otherwise we have to call into native to retrieve a position. + } else { + if (options.timeout !== Infinity) { + // If the timeout value was not set to Infinity (default), then + // set up a timeout function that will fire the error callback + // if no successful position was retrieved before timeout expired. + timeoutTimer = createTimeout(fail, options.timeout); + } else { + // This is here so the check in the win function doesn't mess stuff up + // may seem weird but this guarantees timeoutTimer is + // always truthy before we call into native + timeoutTimer = true; + } + exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]); + } + return timeoutTimer; + }, + /** + * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, + * the successCallback is called with the new location. + * + * @param {Function} successCallback The function to call each time the location data is available + * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ + watchPosition:function(successCallback, errorCallback, options) { + if (arguments.length === 0) { + throw new Error("watchPosition must be called with at least one argument."); + } + options = parseParameters(options); + + var id = utils.createUUID(); + + // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition + timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options); + + var fail = function(e) { + clearTimeout(timers[id]); + var err = new PositionError(e.code, e.message); + if (errorCallback) { + errorCallback(err); + } + }; + + var win = function(p) { + clearTimeout(timers[id]); + if (options.timeout !== Infinity) { + timers[id] = createTimeout(fail, options.timeout); + } + var pos = new Position( + { + latitude:p.latitude, + longitude:p.longitude, + altitude:p.altitude, + accuracy:p.accuracy, + heading:p.heading, + velocity:p.velocity, + altitudeAccuracy:p.altitudeAccuracy + }, + (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp))) + ); + geolocation.lastPosition = pos; + successCallback(pos); + }; + + exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]); + + return id; + }, + /** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchPosition + */ + clearWatch:function(id) { + if (id && timers[id] !== undefined) { + clearTimeout(timers[id]); + delete timers[id]; + exec(null, null, "Geolocation", "clearWatch", [id]); + } + } +}; + +module.exports = geolocation; + +}); + +// file: lib/ios/plugin/ios/Contact.js +define("cordova/plugin/ios/Contact", function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'); + +/** + * Provides iOS Contact.display API. + */ +module.exports = { + display : function(errorCB, options) { + /* + * Display a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + */ + + if (this.id === null) { + if (typeof errorCB === "function") { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + } + else { + exec(null, errorCB, "Contacts","displayContact", [this.id, options]); + } + } +}; +}); + +// file: lib/ios/plugin/ios/Entry.js +define("cordova/plugin/ios/Entry", function(require, exports, module) { +module.exports = { + toURL:function() { + // TODO: refactor path in a cross-platform way so we can eliminate + // these kinds of platform-specific hacks. + return "file://localhost" + this.fullPath; + }, + toURI: function() { + console.log("DEPRECATED: Update your code to use 'toURL'"); + return "file://localhost" + this.fullPath; + } +}; +}); + +// file: lib/ios/plugin/ios/FileReader.js +define("cordova/plugin/ios/FileReader", function(require, exports, module) { +var exec = require('cordova/exec'), + FileError = require('cordova/plugin/FileError'), + FileReader = require('cordova/plugin/FileReader'), + ProgressEvent = require('cordova/plugin/ProgressEvent'); + +module.exports = { + readAsText:function(file, encoding) { + // Figure out pathing + this.fileName = ''; + if (typeof file.fullPath === 'undefined') { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + // Default encoding is UTF-8 + var enc = encoding ? encoding : "UTF-8"; + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save result + me.result = decodeURIComponent(r); + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // null result + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + "File", "readAsText", [this.fileName, enc]); + } +}; +}); + +// file: lib/ios/plugin/ios/console.js +define("cordova/plugin/ios/console", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * This class provides access to the debugging console. + * @constructor + */ +var DebugConsole = function() { + this.winConsole = window.console; + this.logLevel = DebugConsole.INFO_LEVEL; +}; + +// from most verbose, to least verbose +DebugConsole.ALL_LEVEL = 1; // same as first level +DebugConsole.INFO_LEVEL = 1; +DebugConsole.WARN_LEVEL = 2; +DebugConsole.ERROR_LEVEL = 4; +DebugConsole.NONE_LEVEL = 8; + +DebugConsole.prototype.setLevel = function(level) { + this.logLevel = level; +}; + +var stringify = function(message) { + try { + if (typeof message === "object" && JSON && JSON.stringify) { + try { + return JSON.stringify(message); + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + } else { + return message.toString(); + } + } catch (e) { + return e.toString(); + } +}; + +/** + * Print a normal log message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.log = function(message) { + if (this.logLevel <= DebugConsole.INFO_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'INFO' } ]); + } + else if (this.winConsole && this.winConsole.log) { + this.winConsole.log(message); + } +}; + +/** + * Print a warning message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.warn = function(message) { + if (this.logLevel <= DebugConsole.WARN_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'WARN' } ]); + } + else if (this.winConsole && this.winConsole.warn) { + this.winConsole.warn(message); + } +}; + +/** + * Print an error message to the console + * @param {Object|String} message Message or object to print to the console + */ +DebugConsole.prototype.error = function(message) { + if (this.logLevel <= DebugConsole.ERROR_LEVEL) { + exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'ERROR' } ]); + } + else if (this.winConsole && this.winConsole.error){ + this.winConsole.error(message); + } +}; + +module.exports = new DebugConsole(); +}); + +// file: lib/ios/plugin/ios/contacts.js +define("cordova/plugin/ios/contacts", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides iOS enhanced contacts API. + */ +module.exports = { + newContactUI : function(successCallback) { + /* + * Create a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * returns: the id of the created contact as param to successCallback + */ + exec(successCallback, null, "Contacts","newContact", []); + }, + chooseContact : function(successCallback, options) { + /* + * Select a contact using the iOS Contact Picker UI + * NOT part of W3C spec so no official documentation + * + * @param errorCB error callback + * @param options object + * allowsEditing: boolean AS STRING + * "true" to allow editing the contact + * "false" (default) display contact + * + * returns: the id of the selected contact as param to successCallback + */ + exec(successCallback, null, "Contacts","chooseContact", [options]); + } +}; +}); + +// file: lib/ios/plugin/ios/nativecomm.js +define("cordova/plugin/ios/nativecomm", function(require, exports, module) { +var cordova = require('cordova'); + +/** + * Called by native code to retrieve all queued commands and clear the queue. + */ +module.exports = function() { + var json = JSON.stringify(cordova.commandQueue); + cordova.commandQueue = []; + return json; +}; +}); + +// file: lib/ios/plugin/ios/notification.js +define("cordova/plugin/ios/notification", function(require, exports, module) { +var Media = require('cordova/plugin/Media'); + +module.exports = { + beep:function(count) { + (new Media('beep.wav')).play(); + } +}; +}); + +// file: lib/common/plugin/logger.js +define("cordova/plugin/logger", function(require, exports, module) { +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // if not using the console, use the native logger + if (!UseConsole) { + exec(null, null, "Logger", "logLevel", [level, message]); + return; + } + + // make sure console is not using logger + if (console.__usingCordovaLogger) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: console.log(message); break; + case logger.ERROR: console.log("ERROR: " + message); break; + case logger.WARN: console.log("WARN: " + message); break; + case logger.INFO: console.log("INFO: " + message); break; + case logger.DEBUG: console.log("DEBUG: " + message); break; + } +}; + +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i 3) { + fail(FileError.SYNTAX_ERR); + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + if (typeof successCallback === 'function') { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + } + else { + // no FileSystem object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + exec(success, fail, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; +}); + +// file: lib/common/plugin/resolveLocalFileSystemURI.js +define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + FileError = require('cordova/plugin/FileError'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + if (typeof errorCallback === 'function') { + errorCallback(new FileError(error)); + } + }; + // sanity check for 'not:valid:filename' + if(!uri || uri.split(":").length > 2) { + setTimeout( function() { + fail(FileError.ENCODING_ERR); + },0); + return; + } + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + if (entry) { + if (typeof successCallback === 'function') { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + +// file: lib/common/plugin/splashscreen.js +define("cordova/plugin/splashscreen", function(require, exports, module) { +var exec = require('cordova/exec'); + +var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, + hide:function() { + exec(null, null, "SplashScreen", "hide", []); + } +}; + +module.exports = splashscreen; +}); + +// file: lib/common/utils.js +define("cordova/utils", function(require, exports, module) { +var utils = exports; + +/** + * Returns an indication of whether the argument is an array or not + */ +utils.isArray = function(a) { + return Object.prototype.toString.call(a) == '[object Array]'; +}; + +/** + * Returns an indication of whether the argument is a Date or not + */ +utils.isDate = function(d) { + return Object.prototype.toString.call(d) == '[object Date]'; +}; + +/** + * Does a deep clone of the object. + */ +utils.clone = function(obj) { + if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') { + return obj; + } + + var retVal, i; + + if(utils.isArray(obj)){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(utils.clone(obj[i])); + } + return retVal; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = utils.clone(obj[i]); + } + } + return retVal; +}; + +/** + * Returns a wrappered version of the function + */ +utils.close = function(context, func, params) { + if (typeof params == 'undefined') { + return function() { + return func.apply(context, arguments); + }; + } else { + return function() { + return func.apply(context, params); + }; + } +}; + +/** + * Create a UUID + */ +utils.createUUID = function() { + return UUIDcreatePart(4) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(2) + '-' + + UUIDcreatePart(6); +}; + +/** + * Extends a child object from a parent object using classical inheritance + * pattern. + */ +utils.extend = (function() { + // proxy used to establish prototype chain + var F = function() {}; + // extend Child from Parent + return function(Child, Parent) { + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.__super__ = Parent.prototype; + Child.prototype.constructor = Child; + }; +}()); + +/** + * Alerts a message in any available way: alert or console.log. + */ +utils.alert = function(msg) { + if (alert) { + alert(msg); + } else if (console && console.log) { + console.log(msg); + } +}; + +/** + * Formats a string and arguments following it ala sprintf() + * + * see utils.vformat() for more information + */ +utils.format = function(formatString /* ,... */) { + var args = [].slice.call(arguments, 1); + return utils.vformat(formatString, args); +}; + +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +utils.vformat = function(formatString, args) { + if (formatString === null || formatString === undefined) return ""; + if (arguments.length == 1) return formatString.toString(); + if (typeof formatString != "string") return formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var arg = args.shift(); + var match = pattern.exec(rest); + + if (!match) break; + + rest = match[3]; + + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(formatted(arg, match[2])); + } + + result.push(rest); + + return result.join(''); +}; + +//------------------------------------------------------------------------------ +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i + + + + + + + + Hello Cordova + + +
    +

    Apache Cordovaâ„¢

    +
    + + +
    +
    + + + + + + + + +

    Hey, it's Twitter on PhoneGap!

    +
    +
      +
    1. isAvailable
    2. +
    3. isSetup
    4. +
    5. tweet (text, img, url)
    6. +
    7. tweet (text, remote img)
    8. +
    9. tweet (text, local img)
    10. +
    11. tweet (text, url)
    12. +
    13. tweet (text)
    14. +
    15. tweet (empty)
    16. +
    17. timeline
    18. +
    19. mentions
    20. +
    21. friendsIds
    22. +
    23. usersLookup
    24. +
    +
    + + + + diff --git a/iOS/Twitter/example/www2.0/js/index.js b/iOS/Twitter/example/www2.0/js/index.js new file mode 100644 index 0000000..40de817 --- /dev/null +++ b/iOS/Twitter/example/www2.0/js/index.js @@ -0,0 +1,24 @@ +var app = { +initialize: function() { + this.bind(); +}, +bind: function() { + document.addEventListener('deviceready', this.deviceready, false); +}, +deviceready: function() { + // note that this is an event handler so the scope is that of the event + // so we need to call app.report(), and not this.report() + app.report('deviceready'); + + TwitterDemo.setup(); + + +}, +report: function(id) { + console.log("report:" + id); + // hide the .pending

    and show the .complete

    + document.querySelector('#' + id + ' .pending').className += ' hide'; + var completeElem = document.querySelector('#' + id + ' .complete'); + completeElem.className = completeElem.className.split('hide').join(''); +} +}; diff --git a/iOS/Twitter/example/www2.0/ninja-lolcat.gif b/iOS/Twitter/example/www2.0/ninja-lolcat.gif new file mode 100644 index 0000000..51f1959 Binary files /dev/null and b/iOS/Twitter/example/www2.0/ninja-lolcat.gif differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_128.png b/iOS/Twitter/example/www2.0/res/icon/cordova_128.png new file mode 100644 index 0000000..3516df3 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_128.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_16.png b/iOS/Twitter/example/www2.0/res/icon/cordova_16.png new file mode 100644 index 0000000..54e19c5 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_16.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_24.png b/iOS/Twitter/example/www2.0/res/icon/cordova_24.png new file mode 100644 index 0000000..c7d43ad Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_24.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_256.png b/iOS/Twitter/example/www2.0/res/icon/cordova_256.png new file mode 100644 index 0000000..e1cd0e6 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_256.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_32.png b/iOS/Twitter/example/www2.0/res/icon/cordova_32.png new file mode 100644 index 0000000..734fffc Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_32.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_48.png b/iOS/Twitter/example/www2.0/res/icon/cordova_48.png new file mode 100644 index 0000000..8ad8bac Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_48.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_512.png b/iOS/Twitter/example/www2.0/res/icon/cordova_512.png new file mode 100644 index 0000000..c9465f3 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_512.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_64.png b/iOS/Twitter/example/www2.0/res/icon/cordova_64.png new file mode 100644 index 0000000..03b3849 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_64.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_android_36.png b/iOS/Twitter/example/www2.0/res/icon/cordova_android_36.png new file mode 100644 index 0000000..cd5032a Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_android_36.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_android_48.png b/iOS/Twitter/example/www2.0/res/icon/cordova_android_48.png new file mode 100644 index 0000000..e79c606 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_android_48.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_android_72.png b/iOS/Twitter/example/www2.0/res/icon/cordova_android_72.png new file mode 100644 index 0000000..4d27634 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_android_72.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_android_96.png b/iOS/Twitter/example/www2.0/res/icon/cordova_android_96.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_android_96.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_bb_80.png b/iOS/Twitter/example/www2.0/res/icon/cordova_bb_80.png new file mode 100644 index 0000000..f86a27a Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_bb_80.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_ios_114.png b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_114.png new file mode 100644 index 0000000..efd9c37 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_114.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_ios_144.png b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_144.png new file mode 100644 index 0000000..dd819da Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_144.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_ios_57.png b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_57.png new file mode 100644 index 0000000..c795fc4 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_57.png differ diff --git a/iOS/Twitter/example/www2.0/res/icon/cordova_ios_72.png b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_72.png new file mode 100644 index 0000000..b1cfde7 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/icon/cordova_ios_72.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_hdpi_landscape.png b/iOS/Twitter/example/www2.0/res/screen/android_hdpi_landscape.png new file mode 100644 index 0000000..a61e2b1 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_hdpi_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_hdpi_portrait.png b/iOS/Twitter/example/www2.0/res/screen/android_hdpi_portrait.png new file mode 100644 index 0000000..5d6a28a Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_hdpi_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_ldpi_landscape.png b/iOS/Twitter/example/www2.0/res/screen/android_ldpi_landscape.png new file mode 100644 index 0000000..f3934cd Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_ldpi_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_ldpi_portrait.png b/iOS/Twitter/example/www2.0/res/screen/android_ldpi_portrait.png new file mode 100644 index 0000000..65ad163 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_ldpi_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_mdpi_landscape.png b/iOS/Twitter/example/www2.0/res/screen/android_mdpi_landscape.png new file mode 100644 index 0000000..a1b697c Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_mdpi_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_mdpi_portrait.png b/iOS/Twitter/example/www2.0/res/screen/android_mdpi_portrait.png new file mode 100644 index 0000000..ea15693 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_mdpi_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_xhdpi_landscape.png b/iOS/Twitter/example/www2.0/res/screen/android_xhdpi_landscape.png new file mode 100644 index 0000000..79f2f09 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_xhdpi_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/android_xhdpi_portrait.png b/iOS/Twitter/example/www2.0/res/screen/android_xhdpi_portrait.png new file mode 100644 index 0000000..c2e8042 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/android_xhdpi_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/blackberry_transparent_300.png b/iOS/Twitter/example/www2.0/res/screen/blackberry_transparent_300.png new file mode 100644 index 0000000..b548bdc Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/blackberry_transparent_300.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/blackberry_transparent_400.png b/iOS/Twitter/example/www2.0/res/screen/blackberry_transparent_400.png new file mode 100644 index 0000000..3facdf9 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/blackberry_transparent_400.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/ipad_landscape.png b/iOS/Twitter/example/www2.0/res/screen/ipad_landscape.png new file mode 100644 index 0000000..04be5ac Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/ipad_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/ipad_portrait.png b/iOS/Twitter/example/www2.0/res/screen/ipad_portrait.png new file mode 100644 index 0000000..41e839d Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/ipad_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/ipad_retina_landscape.png b/iOS/Twitter/example/www2.0/res/screen/ipad_retina_landscape.png new file mode 100644 index 0000000..95c542d Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/ipad_retina_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/ipad_retina_portrait.png b/iOS/Twitter/example/www2.0/res/screen/ipad_retina_portrait.png new file mode 100644 index 0000000..aae1862 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/ipad_retina_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/iphone_landscape.png b/iOS/Twitter/example/www2.0/res/screen/iphone_landscape.png new file mode 100644 index 0000000..d154883 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/iphone_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/iphone_portrait.png b/iOS/Twitter/example/www2.0/res/screen/iphone_portrait.png new file mode 100644 index 0000000..6fcba56 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/iphone_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/iphone_retina_landscape.png b/iOS/Twitter/example/www2.0/res/screen/iphone_retina_landscape.png new file mode 100644 index 0000000..0165669 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/iphone_retina_landscape.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/iphone_retina_portrait.png b/iOS/Twitter/example/www2.0/res/screen/iphone_retina_portrait.png new file mode 100644 index 0000000..bd24886 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/iphone_retina_portrait.png differ diff --git a/iOS/Twitter/example/www2.0/res/screen/windows_phone_portrait.jpg b/iOS/Twitter/example/www2.0/res/screen/windows_phone_portrait.jpg new file mode 100644 index 0000000..9f95387 Binary files /dev/null and b/iOS/Twitter/example/www2.0/res/screen/windows_phone_portrait.jpg differ diff --git a/iOS/Twitter/example/www2.0/spec.html b/iOS/Twitter/example/www2.0/spec.html new file mode 100644 index 0000000..83d7d2e --- /dev/null +++ b/iOS/Twitter/example/www2.0/spec.html @@ -0,0 +1,50 @@ + + + + Jasmine Spec Runner + + + + + + + + + + + + + + + + + +

    + + diff --git a/iOS/Twitter/example/www2.0/spec/helper.js b/iOS/Twitter/example/www2.0/spec/helper.js new file mode 100644 index 0000000..9f99445 --- /dev/null +++ b/iOS/Twitter/example/www2.0/spec/helper.js @@ -0,0 +1,11 @@ +afterEach(function() { + document.getElementById('stage').innerHTML = ''; +}); + +var helper = { + trigger: function(obj, name) { + var e = document.createEvent('Event'); + e.initEvent(name, true, true); + obj.dispatchEvent(e); + } +}; diff --git a/iOS/Twitter/example/www2.0/spec/index.js b/iOS/Twitter/example/www2.0/spec/index.js new file mode 100644 index 0000000..121cf63 --- /dev/null +++ b/iOS/Twitter/example/www2.0/spec/index.js @@ -0,0 +1,49 @@ +describe('app', function() { + describe('initialize', function() { + it('should bind deviceready', function() { + runs(function() { + spyOn(app, 'deviceready'); + app.initialize(); + helper.trigger(window.document, 'deviceready'); + }); + + waitsFor(function() { + return (app.deviceready.calls.length > 0); + }, 'deviceready should be called once', 500); + + runs(function() { + expect(app.deviceready).toHaveBeenCalled(); + }); + }); + }); + + describe('deviceready', function() { + it('should report that it fired', function() { + spyOn(app, 'report'); + app.deviceready(); + expect(app.report).toHaveBeenCalledWith('deviceready'); + }); + }); + + describe('report', function() { + beforeEach(function() { + var el = document.getElementById('stage'); + el.innerHTML = ['
    ', + '

    Pending

    ', + '

    Complete

    ', + '
    '].join('\n'); + }); + + it('should show the completion state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .complete:not(.hide)'); + expect(el).toBeTruthy(); + }); + + it('should hide the pending state', function() { + app.report('deviceready'); + var el = document.querySelector('#deviceready .pending.hide'); + expect(el).toBeTruthy(); + }); + }); +}); diff --git a/iOS/Twitter/install b/iOS/Twitter/install deleted file mode 100644 index 3a32024..0000000 --- a/iOS/Twitter/install +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env ruby - -def replace_in_file(filepath, regexp, *args, &block) - content = File.read(filepath).gsub(regexp, *args, &block) - File.open(filepath, 'wb') { |file| file.write(content) } -end - -file = File.expand_path(ARGV[0]) -platform = ( File.extension(file) == ".plist" ? "ios" : "android" ) - -if (platform == "ios") - replace_in_file(file, /\s*com.phonegap.twitter<\/key>\n/mi) do |match| - "" - end - replace_in_file(file, /\s*TwitterPlugin<\/string>\n/mi) do |match| - "" - end - replace_in_file(file, /Plugins<\/key>\n\s*/mi) do |match| - "Plugins\n\t\n\t\tcom.phonegap.twitter\n\t\tTwitterPlugin" - end -elsif (platform == "android") -end \ No newline at end of file diff --git a/iOS/Twitter/js/TwitterPlugin.js b/iOS/Twitter/js/TwitterPlugin.js new file mode 100644 index 0000000..a4c64ff --- /dev/null +++ b/iOS/Twitter/js/TwitterPlugin.js @@ -0,0 +1,140 @@ +/** + * @constructor + */ +var Twitter = function(){}; +/** + * Checks if the Twitter SDK is loaded + * @param {Function} response callback on result + * @param {Number} response.response is 1 for success, 0 for failure + * @example + * window.plugins.twitter.isTwitterAvailable(function (response) { + * console.log("twitter available? " + response); + * }); + */ +Twitter.prototype.isTwitterAvailable = function(response){ + cordova.exec(response, null, "TwitterPlugin", "isTwitterAvailable", []); +}; +/** + * Checks if the Twitter SDK can send a tweet + * @param {Function} response callback on result + * @param {Number} response.response is 1 for success, 0 for failure + * @example + * window.plugins.twitter.isTwitterSetup(function (r) { + * console.log("twitter configured? " + r); + * }); + */ +Twitter.prototype.isTwitterSetup = function(response){ + cordova.exec(response, null, "TwitterPlugin", "isTwitterSetup", []); +}; +/** + * Sends a Tweet to Twitter + * @param {Function} success callback + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @param {String} tweetText message to send to twitter + * @param {Object} options (optional) + * @param {String} options.urlAttach URL to embed in Tweet + * @param {String} options.imageAttach Image URL to embed in Tweet + * @param {Number} response.response - 1 on success, 0 on failure + * @example + * window.plugins.twitter.composeTweet( + * function () { console.log("tweet success"); }, + * function (error) { console.log("tweet failure: " + error); }, + * "Text, Image, URL", + * { + * urlAttach:"http://m.youtube.com/#/watch?v=obx2VOtx0qU", + * imageAttach:"http://i.ytimg.com/vi/obx2VOtx0qU/hqdefault.jpg?w=320&h=192&sigh=QD3HYoJj9dtiytpCSXhkaq1oG8M" + * } + * ); + */ +Twitter.prototype.composeTweet = function(success, failure, tweetText, options){ + options = options || {}; + options.text = tweetText; + cordova.exec(success, failure, "TwitterPlugin", "composeTweet", [options]); +}; +/** + * Gets Tweets from Twitter Timeline + * @param {Function} success callback + * @param {Object[]} success.response Tweet objects, see [Twitter Timeline Doc] + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @example + * window.plugins.twitter.getPublicTimeline( + * function (response) { console.log("timeline success: " + JSON.stringify(response)); }, + * function (error) { console.log("timeline failure: " + error); } + * ); + * + * [Twitter Timeline Doc]: https://dev.twitter.com/docs/api/1/get/statuses/public_timeline + */ +Twitter.prototype.getPublicTimeline = function(success, failure){ + cordova.exec(success, failure, "TwitterPlugin", "getPublicTimeline", []); +}; +/** + * Gets Tweets from Twitter Mentions + * @param {Function} success callback + * @param {Object[]} success.result Tweet objects, see [Twitter Mentions Doc] + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @example + * window.plugins.twitter.getMentions( + * function (response) { console.log("mentions success: " + JSON.stringify(response)); }, + * function (error) { console.log("mentions failure: " + error); } + * ); + * + * [Twitter Timeline Doc]: https://dev.twitter.com/docs/api/1/get/statuses/public_timeline + */ +Twitter.prototype.getMentions = function(success, failure){ + cordova.exec(success, failure, "TwitterPlugin", "getMentions", []); +}; +/** + * Gets Tweets from Twitter Mentions API + * @param {Function} success callback + * @param {String} success.response Twitter Username + * @param {Object[]} success.result Tweet objects, see [Twitter Mentions Doc] + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * + * [Twitter Mentions Doc]: https://dev.twitter.com/docs/api/1/get/statuses/mentions + */ +Twitter.prototype.getTwitterUsername = function(success, failure) { + cordova.exec(success, failure, "TwitterPlugin", "getTwitterUsername", []); +}; +/** + * Gets Tweets from Twitter Mentions API + * @param {String} url of [Twitter API Endpoint] + * @param {Object} params key-value map, matching [Twitter API Endpoint] + * @param {Function} success callback + * @param {Object[]} success.response objects returned from Twitter API (Tweets, Users,...) + * @param {Function} failure callback + * @param {String} failure.error reason for failure + * @param {Object} options (optional) other options for the HTTP request + * @param {String} options.requestMethod HTTP Request type, ex: "POST" + * @example + * window.plugins.twitter.getTWRequest( + * 'users/lookup.json', + * {user_id: '16141659,783214,6253282'}, + * function (response) { console.log("usersLookup success: " + JSON.stringify(response)); }, + * function (error) { console.log("usersLookup failure: " + error); }, + * {requestMethod: 'POST'} + * ); + * + * [Twitter API Endpoints]: https://dev.twitter.com/docs/api + */ +Twitter.prototype.getTWRequest = function(url, params, success, failure, options){ + options = options || {}; + options.url = url; + options.params = params; + cordova.exec(success, failure, "TwitterPlugin", "getTWRequest", [options]); +}; +// Plug in to Cordova +cordova.addConstructor(function() { + + /* shim to work in 1.5 and 1.6 */ + if (!window.Cordova) { + window.Cordova = cordova; + }; + + + if(!window.plugins) window.plugins = {}; + window.plugins.twitter = new Twitter(); + }); diff --git a/iOS/Twitter/package.json b/iOS/Twitter/package.json deleted file mode 100644 index dd06914..0000000 --- a/iOS/Twitter/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "author": "Brian Antonelli", - "name": "com.phonegap.twitter", - "description": "PhoneGap Twitter Plugin", - "version": "0.0.1", - "repository": { - "type": "git", - "url": "git://github.com/phonegap/phonegap-plugins.git" - }, - "engines": { - "phonegap": "1.2.0" - }, - "dependencies": {}, - "development_dependencies": {} -} \ No newline at end of file diff --git a/iOS/Twitter/www/TwitterPlugin.js b/iOS/Twitter/www/TwitterPlugin.js deleted file mode 100644 index bf8f56e..0000000 --- a/iOS/Twitter/www/TwitterPlugin.js +++ /dev/null @@ -1,46 +0,0 @@ -var Twitter = function(){}; - -Twitter.prototype.isTwitterAvailable = function(response){ - cordova.exec(response, null, "TwitterPlugin", "isTwitterAvailable", []); -}; - -Twitter.prototype.isTwitterSetup = function(response){ - cordova.exec(response, null, "TwitterPlugin", "isTwitterSetup", []); -}; - -Twitter.prototype.composeTweet = function(success, failure, tweetText, options){ - options = options || {}; - options.text = tweetText; - cordova.exec(success, failure, "TwitterPlugin", "composeTweet", [options]); -}; - -Twitter.prototype.getPublicTimeline = function(success, failure){ - cordova.exec(success, failure, "TwitterPlugin", "getPublicTimeline", []); -}; - -Twitter.prototype.getMentions = function(success, failure){ - cordova.exec(success, failure, "TwitterPlugin", "getMentions", []); -}; - -Twitter.prototype.getTwitterUsername = function(success, failure) { - cordova.exec(success, failure, "TwitterPlugin", "getTwitterUsername", []); -}; - -Twitter.prototype.getTWRequest = function(url, params, success, failure, options){ - options = options || {}; - options.url = url; - options.params = params; - cordova.exec(success, failure, "TwitterPlugin", "getTWRequest", [options]); -}; - -cordova.addConstructor(function() { - - /* shim to work in 1.5 and 1.6 */ - if (!window.Cordova) { - window.Cordova = cordova; - }; - - - if(!window.plugins) window.plugins = {}; - window.plugins.twitter = new Twitter(); - }); diff --git a/iOS/VolumeSlider/README.md b/iOS/VolumeSlider/README.md index 181e9c4..8783111 100644 --- a/iOS/VolumeSlider/README.md +++ b/iOS/VolumeSlider/README.md @@ -1,6 +1,8 @@ VolumeSlider ============ +*Updated for Cordova 2.0* + Installation ------------ diff --git a/iOS/VolumeSlider/VolumeSlider.h b/iOS/VolumeSlider/VolumeSlider.h index d65af42..995a246 100644 --- a/iOS/VolumeSlider/VolumeSlider.h +++ b/iOS/VolumeSlider/VolumeSlider.h @@ -7,11 +7,7 @@ // MIT Licensed // -#ifdef CORDOVA_FRAMEWORK #import -#else -#import "CDVPlugin.h" -#endif #import diff --git a/iOS/VolumeSlider/VolumeSlider.js b/iOS/VolumeSlider/VolumeSlider.js index 3d1d566..260efed 100644 --- a/iOS/VolumeSlider/VolumeSlider.js +++ b/iOS/VolumeSlider/VolumeSlider.js @@ -1,41 +1,42 @@ // -// VolumeSlider.js -// Volume Slider Cordova Plugin +// VolumeSlider.js +// Volume Slider Cordova Plugin // -// Created by Tommy-Carlos Williams on 20/07/11. -// Copyright 2011 Tommy-Carlos Williams. All rights reserved. +// Created by Tommy-Carlos Williams on 20/07/11. +// Copyright 2011 Tommy-Carlos Williams. All rights reserved. // MIT Licensed // - -var VolumeSlider = function(){ - -} - -/** - * Create a volume slider. - */ -VolumeSlider.prototype.createVolumeSlider = function(originx,originy,width,height) { - Cordova.exec(null, null, "VolumeSlider","createVolumeSlider", [originx, originy, width, height]); -}; - -/** - * Show the volume slider - */ -VolumeSlider.prototype.showVolumeSlider = function() { - Cordova.exec(null, null, "VolumeSlider","showVolumeSlider", []); -}; -/** - * Hide the volume slider - */ -VolumeSlider.prototype.hideVolumeSlider = function() { - Cordova.exec(null, null, "VolumeSlider","hideVolumeSlider", []); -}; - - -Cordova.addConstructor(function(){ - if(!window.plugins) - { - window.plugins = {}; - } - window.plugins.volumeSlider = new VolumeSlider(); -}); +(function(){ + var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks + var VolumeSlider = function(){ + }; + + /** + * Create a volume slider. + */ + VolumeSlider.prototype.createVolumeSlider = function(originx,originy,width,height) { + cordovaRef.exec(null, null, "VolumeSlider","createVolumeSlider", [originx, originy, width, height]); + }; + + /** + * Show the volume slider + */ + VolumeSlider.prototype.showVolumeSlider = function() { + cordovaRef.exec(null, null, "VolumeSlider","showVolumeSlider", []); + }; + /** + * Hide the volume slider + */ + VolumeSlider.prototype.hideVolumeSlider = function() { + cordovaRef.exec(null, null, "VolumeSlider","hideVolumeSlider", []); + }; + + + cordovaRef.addConstructor(function(){ + if(!window.plugins) + { + window.plugins = {}; + } + window.plugins.volumeSlider = new VolumeSlider(); + }); +})(); \ No newline at end of file diff --git a/iOS/callvenderapp/README.md b/iOS/callvenderapp/README.md new file mode 100644 index 0000000..54979af --- /dev/null +++ b/iOS/callvenderapp/README.md @@ -0,0 +1,8 @@ +callvenderapp- +============== + +phonegap plugin call vendor app by Custom URL Schemes if not exits and goto a url to download + + +see example in example.html + diff --git a/iOS/callvenderapp/callvenderapp.h b/iOS/callvenderapp/callvenderapp.h new file mode 100644 index 0000000..ff22e62 --- /dev/null +++ b/iOS/callvenderapp/callvenderapp.h @@ -0,0 +1,21 @@ +// +// MyClass.h +// hello +// +// Created by kissthink on 12-4-25. +// Copyright 2012å¹´ __MyCompanyName__. All rights reserved. +// + +#import + +@interface callvenderapp : CDVPlugin + +{ + NSString * callbackID; + +} + +@property (nonatomic,copy) NSString* callbackID; + +- (void) run:(NSMutableString*) arguments withDict : (NSMutableDictionary*)options; +@end diff --git a/iOS/callvenderapp/callvenderapp.js b/iOS/callvenderapp/callvenderapp.js new file mode 100644 index 0000000..5a2b78e --- /dev/null +++ b/iOS/callvenderapp/callvenderapp.js @@ -0,0 +1,43 @@ +/* + callvenderapp.js + + + Created by kissthink on 12-4-26. + Copyright 2012å¹´ __MyCompanyName__. All rights reserved. +*/ + + +/** +* Phonegap KissthinkPlugin Instance plugin +* Copyright (c)kissthink 2012 +* +*/ +var KissthinkPlugin = { + +run: function(types, success, fail) { +return PhoneGap.exec(success, fail, "callvenderapp", "run", types); +} +}; + + +/** +* exampel +* + +KissthinkPlugin.run( +["flypiggroupnote://","http://itunes.apple.com/us/app/.....url..."] , + +function(result) { + +alert("Success : \r\n"+result); + +}, + +function(error) { + +alert("Error : \r\n"+error); +} +); + + +*/ \ No newline at end of file diff --git a/iOS/callvenderapp/callvenderapp.m b/iOS/callvenderapp/callvenderapp.m new file mode 100644 index 0000000..6e642f7 --- /dev/null +++ b/iOS/callvenderapp/callvenderapp.m @@ -0,0 +1,64 @@ +// +// MyClass.m +// hello +// +// Created by kisshtink on 12-4-25. +// Copyright 2012å¹´ __MyCompanyName__. All rights reserved. +// + +#import "callvenderapp.h" + +@implementation callvenderapp +@synthesize callbackID; +- (id)init +{ + self = [super init]; + if (self) { + // Initialization code here. + } + + return self; +} + +-(void) run:(NSMutableString *)arguments withDict:(NSMutableDictionary *)options +{ + + //The first argument in the arguments parameter is the callbackID. + //We use this to send data back to the successCallback or failureCallback + //through PluginResult. + self.callbackID = [arguments pop]; + + //Get the string that javascript sent us + NSString *stringObtainedFromJavascript = [arguments objectAtIndex:0]; + NSString *stringObtainedFromJavascript1 = [arguments objectAtIndex:1]; + + //Create the Message that we wish to send to the Javascript + NSMutableString *stringToReturn = [NSMutableString stringWithString: @"StringReceived:"]; + //Append the received string to the string we plan to send out + [stringToReturn appendString: stringObtainedFromJavascript]; + //Create Plugin Result + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: [stringToReturn stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + + NSURL *appUrl = [NSURL URLWithString:stringObtainedFromJavascript ] ; + + NSURL *appUrlStore = [NSURL URLWithString: stringObtainedFromJavascript1] ; + + + if([[UIApplication sharedApplication] canOpenURL:appUrl]) + { + [[UIApplication sharedApplication] openURL:appUrl]; + [self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]]; + } + else + { + + [[UIApplication sharedApplication] openURL:appUrlStore]; + + [self writeJavascript: [pluginResult toErrorCallbackString:self.callbackID]]; + + } + + + +} +@end diff --git a/iOS/callvenderapp/example.html b/iOS/callvenderapp/example.html new file mode 100644 index 0000000..da3e3a4 --- /dev/null +++ b/iOS/callvenderapp/example.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + call my app + + +

    Hey, it's callvenderapp plugin

    + add plist plugins row : callvenderapp callvenderapp + + + diff --git a/iPhone/AudioRecord/README.md b/iPhone/AudioRecord/README.md index 33bf4e0..2fbecb2 100644 --- a/iPhone/AudioRecord/README.md +++ b/iPhone/AudioRecord/README.md @@ -9,11 +9,11 @@ PhoneGap's media library does not let you provide settings to the audio recordin The plugin adds two methods to Phonegap's existing media class: `media.startRecordWithSettings(options)` and `media.stopRecordWithSettings`. You must include all of the encoding options listed in the example or it will crash. The plugin will allows you to pass a FormatID, SampleRate, NumberOfChannels, and LinearPCMBitDepth to the record function. - var recordSettings = { - "FormatID": "kAudioFormatULaw", - "SampleRate": 8000.0, - "NumberOfChannels": 1, - "LinearPCMBitDepth": 16 + var recordSettings = { + "FormatID": "kAudioFormatULaw", + "SampleRate": 8000.0, + "NumberOfChannels": 1, + "LinearPCMBitDepth": 16 } media.startRecordWithSettings(recordSettings); media.stopRecordWithSettings(); diff --git a/iPhone/BarcodeScanner/test/desktop-app/images/qr_code-large.txt b/iPhone/BarcodeScanner/test/desktop-app/images/qr_code-large.txt index 2b574b7..aeae873 100644 --- a/iPhone/BarcodeScanner/test/desktop-app/images/qr_code-large.txt +++ b/iPhone/BarcodeScanner/test/desktop-app/images/qr_code-large.txt @@ -1,12 +1,12 @@ -BEGIN:VCARD -N:Kennedy;Steve -TEL:+44 (0)7775 755503 -ADR;HOME:;;Flat 2, 43 Howitt Road, Belsize Park;London;;NW34LU;UK -ORG:NetTek Ltd; -TITLE:Consultant -EMAIL:steve@nettek.co.uk -URL:www.nettek.co.uk -EMAIL;IM:MSN:steve@gbnet.net -NOTE:Testing 1 2 3 -BDAY:19611105 +BEGIN:VCARD +N:Kennedy;Steve +TEL:+44 (0)7775 755503 +ADR;HOME:;;Flat 2, 43 Howitt Road, Belsize Park;London;;NW34LU;UK +ORG:NetTek Ltd; +TITLE:Consultant +EMAIL:steve@nettek.co.uk +URL:www.nettek.co.uk +EMAIL;IM:MSN:steve@gbnet.net +NOTE:Testing 1 2 3 +BDAY:19611105 END:VCARD \ No newline at end of file diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_left.png b/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_left.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_left@2x.png b/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_left@2x.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_right.png b/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_right.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_right@2x.png b/iPhone/ChildBrowser/ChildBrowser.bundle/arrow_right@2x.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/but_refresh.png b/iPhone/ChildBrowser/ChildBrowser.bundle/but_refresh.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/but_refresh@2x.png b/iPhone/ChildBrowser/ChildBrowser.bundle/but_refresh@2x.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/compass.png b/iPhone/ChildBrowser/ChildBrowser.bundle/compass.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.bundle/compass@2x.png b/iPhone/ChildBrowser/ChildBrowser.bundle/compass@2x.png old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/ChildBrowser.js b/iPhone/ChildBrowser/ChildBrowser.js old mode 100644 new mode 100755 index 30fe20a..f03af55 --- a/iPhone/ChildBrowser/ChildBrowser.js +++ b/iPhone/ChildBrowser/ChildBrowser.js @@ -1,80 +1,73 @@ /* MIT licensed */ // (c) 2010 Jesse MacFadyen, Nitobi -/*global PhoneGap/Cordova */ +/*global PhoneGap */ -function ChildBrowser() -{ - // Does nothing +function ChildBrowser() { + // Does nothing } + // Callback when the location of the page changes // called from native ChildBrowser._onLocationChange = function(newLoc) { - window.plugins.childBrowser.onLocationChange(newLoc); + window.plugins.childBrowser.onLocationChange(newLoc); }; + // Callback when the user chooses the 'Done' button // called from native ChildBrowser._onClose = function() { - window.plugins.childBrowser.onClose(); + window.plugins.childBrowser.onClose(); }; + // Callback when the user chooses the 'open in Safari' button // called from native ChildBrowser._onOpenExternal = function() { - window.plugins.childBrowser.onOpenExternal(); + window.plugins.childBrowser.onOpenExternal(); }; + // Pages loaded into the ChildBrowser can execute callback scripts, so be careful to // check location, and make sure it is a location you trust. // Warning ... don't exec arbitrary code, it's risky and could fuck up your app. // called from native ChildBrowser._onJSCallback = function(js,loc) { - // Not Implemented - //window.plugins.childBrowser.onJSCallback(js,loc); + // Not Implemented + //window.plugins.childBrowser.onJSCallback(js,loc); }; + /* The interface that you will use to access functionality */ // Show a webpage, will result in a callback to onLocationChange ChildBrowser.prototype.showWebPage = function(loc) { - if (typeof PhoneGap !== "undefined") - { - PhoneGap.exec("ChildBrowserCommand.showWebPage", loc); - } - if (typeof Cordova !== "undefined") - { - Cordova.exec("ChildBrowserCommand.showWebPage", loc); - } + PhoneGap.exec("ChildBrowserCommand.showWebPage", loc); }; + // close the browser, will NOT result in close callback ChildBrowser.prototype.close = function() { - if (typeof PhoneGap !== "undefined") - { - PhoneGap.exec("ChildBrowserCommand.close"); - } - if (typeof Cordova !== "undefined") - { - Cordova.exec("ChildBrowserCommand.close"); - } + PhoneGap.exec("ChildBrowserCommand.close"); }; + // Not Implemented ChildBrowser.prototype.jsExec = function(jsString) { - // Not Implemented!! - //PhoneGap.exec("ChildBrowserCommand.jsExec",jsString); + // Not Implemented!! + //PhoneGap.exec("ChildBrowserCommand.jsExec",jsString); }; + // Note: this plugin does NOT install itself, call this method some time after deviceready to install it // it will be returned, and also available globally from window.plugins.childBrowser ChildBrowser.install = function() { - if(!window.plugins) - { - window.plugins = { - }; - } - window.plugins.childBrowser = new ChildBrowser(); - return window.plugins.childBrowser; + if(!window.plugins) { + window.plugins = {}; + } + + window.plugins.childBrowser = new ChildBrowser(); + return window.plugins.childBrowser; }; + diff --git a/iPhone/ChildBrowser/ChildBrowserCommand.h b/iPhone/ChildBrowser/ChildBrowserCommand.h old mode 100644 new mode 100755 index ac9c71d..5e86a67 --- a/iPhone/ChildBrowser/ChildBrowserCommand.h +++ b/iPhone/ChildBrowser/ChildBrowserCommand.h @@ -1,25 +1,23 @@ +// +// PhoneGap ! ChildBrowserCommand +// +// // Created by Jesse MacFadyen on 10-05-29. // Copyright 2010 Nitobi. All rights reserved. -// Copyright 2012, Randy McMillan -// Continued maintainance @RandyMcMillan 2010/2011/2012 +// #import #ifdef PHONEGAP_FRAMEWORK -#import -#endif -//#else -#ifdef CORDOVA_FRAMEWORK -#import + #import +#else + #import "PGPlugin.h" #endif #import "ChildBrowserViewController.h" -#ifdef PHONEGAP_FRAMEWORK - @interface ChildBrowserCommand : PGPlugin { -#endif -#ifdef CORDOVA_FRAMEWORK - @interface ChildBrowserCommand : CDVPlugin { -#endif + +@interface ChildBrowserCommand : PGPlugin { + ChildBrowserViewController* childBrowser; } diff --git a/iPhone/ChildBrowser/ChildBrowserCommand.m b/iPhone/ChildBrowser/ChildBrowserCommand.m old mode 100644 new mode 100755 index 862cdd4..dc174f4 --- a/iPhone/ChildBrowser/ChildBrowserCommand.m +++ b/iPhone/ChildBrowser/ChildBrowserCommand.m @@ -1,16 +1,18 @@ +// + +// +// // Created by Jesse MacFadyen on 10-05-29. // Copyright 2010 Nitobi. All rights reserved. -// Copyright 2012, Randy McMillan -// Continued maintainance @RandyMcMillan 2010/2011/2012 +// #import "ChildBrowserCommand.h" #ifdef PHONEGAP_FRAMEWORK #import -#endif -//#else -#ifdef CORDOVA_FRAMEWORK -#import + +#else + #import "PhoneGapViewController.h" #endif @@ -30,18 +32,11 @@ NSString* strOrientations = [ options objectForKey:@"supportedOrientations"]; NSArray* supportedOrientations = [strOrientations componentsSeparatedByString:@","]; */ -#ifdef PHONEGAP_FRAMEWORK + PhoneGapViewController* cont = (PhoneGapViewController*)[ super appViewController ]; childBrowser.supportedOrientations = cont.supportedOrientations; [ cont presentModalViewController:childBrowser animated:YES ]; -#endif - -#ifdef CORDOVA_FRAMEWORK - CDVViewController* cont = (CDVViewController*)[ super viewController ]; - childBrowser.supportedOrientations = cont.supportedOrientations; - [ cont presentModalViewController:childBrowser animated:YES ]; -#endif - + NSString *url = (NSString*) [arguments objectAtIndex:0]; diff --git a/iPhone/ChildBrowser/ChildBrowserViewController.h b/iPhone/ChildBrowser/ChildBrowserViewController.h old mode 100644 new mode 100755 index c845553..8767614 --- a/iPhone/ChildBrowser/ChildBrowserViewController.h +++ b/iPhone/ChildBrowser/ChildBrowserViewController.h @@ -1,7 +1,9 @@ -// Created by Jesse MacFadyen on 10-05-29. -// Copyright 2010 Nitobi. All rights reserved. -// Copyright 2012, Randy McMillan -// Continued maintainance @RandyMcMillan 2010/2011/2012 +// +// ChildBrowserViewController.h +// +// Created by Jesse MacFadyen on 21/07/09. +// Copyright 2009 Nitobi. All rights reserved. +// #import diff --git a/iPhone/ChildBrowser/ChildBrowserViewController.m b/iPhone/ChildBrowser/ChildBrowserViewController.m old mode 100644 new mode 100755 index a300ed1..6fc9cce --- a/iPhone/ChildBrowser/ChildBrowserViewController.m +++ b/iPhone/ChildBrowser/ChildBrowserViewController.m @@ -1,7 +1,9 @@ -/// Created by Jesse MacFadyen on 10-05-29. -// Copyright 2010 Nitobi. All rights reserved. -// Copyright 2012, Randy McMillan -// Continued maintainance @RandyMcMillan 2010/2011/2012 +// +// ChildBrowserViewController.m +// +// Created by Jesse MacFadyen on 21/07/09. +// Copyright 2009 Nitobi. All rights reserved. +// #import "ChildBrowserViewController.h" @@ -104,12 +106,8 @@ { [delegate onClose]; } - if ([self respondsToSelector:@selector(presentingViewController)]) { - //Reference UIViewController.h Line:179 for update to iOS 5 difference - @RandyMcMillan - [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; - } else { - [[self parentViewController] dismissModalViewControllerAnimated:YES]; - } + + [ [super parentViewController] dismissModalViewControllerAnimated:YES]; } -(IBAction) onDoneButtonPress:(id)sender @@ -215,25 +213,5 @@ } -/* -- (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error { - NSLog (@"webView:didFailLoadWithError"); - [spinner stopAnimating]; - addressLabel.text = @"Failed"; - if (error != NULL) { - UIAlertView *errorAlert = [[UIAlertView alloc] - initWithTitle: [error localizedDescription] - message: [error localizedFailureReason] - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [errorAlert show]; - [errorAlert release]; - } -} - -*/ - - @end diff --git a/iPhone/ChildBrowser/ChildBrowserViewController.xib b/iPhone/ChildBrowser/ChildBrowserViewController.xib old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/FBConnectExample/FBConnect.js b/iPhone/ChildBrowser/FBConnectExample/FBConnect.js old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/FBConnectExample/README.txt b/iPhone/ChildBrowser/FBConnectExample/README.txt old mode 100644 new mode 100755 diff --git a/iPhone/ChildBrowser/README.md b/iPhone/ChildBrowser/README.txt old mode 100644 new mode 100755 similarity index 79% rename from iPhone/ChildBrowser/README.md rename to iPhone/ChildBrowser/README.txt index 71d0ff9..11bc291 --- a/iPhone/ChildBrowser/README.md +++ b/iPhone/ChildBrowser/README.txt @@ -1,13 +1,11 @@ -UPDATED March 2 2012 for Cordova 1.5 with limited backwards support for PhoneGap 1.4.1 (minor changes may be needed for anything earlier than 1.3) - @RandyMcMillan -================================= -The child browser allows you to display external webpages within your PhoneGap/Cordova application. + +The child browser allows you to display external webpages within your PhoneGap application. A simple use case would be: -- Users can follow links/buttons to view web content without leaving your app. -- Display web pages/images/videos/pdfs in the ChildBrowser. +- Users can post links, and you don't want your users to exit your app to view the link. This command creates a popup browser that is shown in front of your app, when the user presses the done button they are simply returned to your app ( actually they never left ) @@ -15,11 +13,9 @@ The ChildBrowser has buttons for refreshing, navigating back + forwards, as well Note, because this is open source, I could not include the graphics I usually use for the back/forward and safari buttons. I have changed the XIB file to use system buttons ( rewind / fast-forward + action ) Ideally you should modify the XIB to use your own look. -Here is a sample command to open google in a ChildBrowser : +Here is a sample command to open google in a childbrowser : PhoneGap.exec("ChildBrowserCommand.showWebPage", "http://www.google.com" ); -or -Cordova.exec("ChildBrowserCommand.showWebPage", "http://www.google.com" ); ================================= diff --git a/iPhone/HockeyApp/pluginHockeyApp/pluginHockeyApp/HockeyAppKit/HockeyKit/Hockey.bundle/sv.lproj/Hockey.strings b/iPhone/HockeyApp/pluginHockeyApp/pluginHockeyApp/HockeyAppKit/HockeyKit/Hockey.bundle/sv.lproj/Hockey.strings index 37815e5..b95f0c9 100644 --- a/iPhone/HockeyApp/pluginHockeyApp/pluginHockeyApp/HockeyAppKit/HockeyKit/Hockey.bundle/sv.lproj/Hockey.strings +++ b/iPhone/HockeyApp/pluginHockeyApp/pluginHockeyApp/HockeyAppKit/HockeyKit/Hockey.bundle/sv.lproj/Hockey.strings @@ -1,87 +1,87 @@ -/* - Hockey.strings - Hockey - - Created by Andreas Linde on 11/15/10. - Copyright 2010 buzzworks.de. All rights reserved. - Swedish translation by Joakim Ramer. - */ - - -/* Alert view */ - -/* For dialogs yes buttons */ -"HockeyYes" = "Ja"; - -/* For dialogs no buttons */ -"HockeyNo" = "Nej"; - -/* Update available */ -"HockeyUpdateAvailable" = "Uppdatering tillgänglig"; - -/* Would you like to check out the new update? You can do this later on at any time in the In-App settings. */ -"HockeyUpdateAlertText" = "Vill du hämta den nya uppdateringen?"; - - -/* Update details screen */ - -/* Update Details */ -"HockeyUpdateScreenTitle" = "Uppdatera"; - - -/* Settings */ - -/* Screen title for settings view */ -"HockeySettingsTitle" = "Inställningar"; - -/* Text asking the user to send user data (on/off switch) */ -"HockeySettingsUserData" = "Skicka användardata"; - -/* Description text for turning on/off sending user data */ -"HockeySettingsUserDataDescription" = "Skicka användardata skickar följande till utvecklaren: app version, språk, enhetstyp och iOS version."; - -/* Text asking the user to send usage data (on/off switch) */ -"HockeySettingsUsageData" = "Skicka användn.data"; - -/* Description text for turning on/off sending usage data */ -"HockeySettingsUsageDataDescription" = "Skicka användningsdata skickar information om hur länge appen testats (i antal minuter), till utvecklaren."; - -/* Title for defining when update checks may be made */ -"HockeySectionCheckTitle" = "Leta efter uppdateringar"; - -/* On Startup */ -"HockeySectionCheckStartup" = "Vid uppstart"; - -/* Daily */ -"HockeySectionCheckDaily" = "Dagligen"; - -/* Manually */ -"HockeySectionCheckManually" = "Manuellt"; - - -"HockeyVersion" = "Version"; -"HockeyShowPreviousVersions" = "Se tidigare versioner..."; -"HockeyNoUpdateNeededTitle" = "Ingen uppdatering tillgänglig"; -"HockeyNoUpdateNeededMessage" = "%@ är den senaste versionen."; -"HockeyUpdateAlertTextWithAppVersion" = "%@ finns."; -"HockeyIgnore" = "Ignorera"; -"HockeyShowUpdate" = "Visa"; -"HockeyInstallUpdate" = "Installera"; -"HockeyError" = "Error"; -"HockeyOK" = "OK"; -"HockeyWarning" = "Varning"; -"HockeyNoReleaseNotesAvailable" = "Inga releasenoteringar tillgängliga."; - -"HockeyAuthorizationProgress" = "Auktoriserar..."; -"HockeyAuthorizationOffline" = "Internetuppkoppling krävs!"; -"HockeyAuthorizationDenied" = "Auktoriseringen nekades. Kontakta utvecklaren."; - -"HockeyiOS3Message" = "In-App nedladdning kräver iOS 4 eller högre. Du kan uppdatera applikationen genoma att ladda ner IPA-filen från %@ och synka in den i iTunes."; -"HockeySimulatorMessage" = "Hockey Update fungerar inte i Simulatorn.\nitms-services:// url schemat är implementerat men fungerar ej."; - -"HockeyButtonCheck" = "FRÅGA"; -"HockeyButtonSearching" = "FRÅGAR"; -"HockeyButtonUpdate" = "UPPDATERA"; -"HockeyButtonInstalling" = "INSTALLERAR"; -"HockeyButtonOffline" = "OFFLINE"; +/* + Hockey.strings + Hockey + + Created by Andreas Linde on 11/15/10. + Copyright 2010 buzzworks.de. All rights reserved. + Swedish translation by Joakim Ramer. + */ + + +/* Alert view */ + +/* For dialogs yes buttons */ +"HockeyYes" = "Ja"; + +/* For dialogs no buttons */ +"HockeyNo" = "Nej"; + +/* Update available */ +"HockeyUpdateAvailable" = "Uppdatering tillgänglig"; + +/* Would you like to check out the new update? You can do this later on at any time in the In-App settings. */ +"HockeyUpdateAlertText" = "Vill du hämta den nya uppdateringen?"; + + +/* Update details screen */ + +/* Update Details */ +"HockeyUpdateScreenTitle" = "Uppdatera"; + + +/* Settings */ + +/* Screen title for settings view */ +"HockeySettingsTitle" = "Inställningar"; + +/* Text asking the user to send user data (on/off switch) */ +"HockeySettingsUserData" = "Skicka användardata"; + +/* Description text for turning on/off sending user data */ +"HockeySettingsUserDataDescription" = "Skicka användardata skickar följande till utvecklaren: app version, språk, enhetstyp och iOS version."; + +/* Text asking the user to send usage data (on/off switch) */ +"HockeySettingsUsageData" = "Skicka användn.data"; + +/* Description text for turning on/off sending usage data */ +"HockeySettingsUsageDataDescription" = "Skicka användningsdata skickar information om hur länge appen testats (i antal minuter), till utvecklaren."; + +/* Title for defining when update checks may be made */ +"HockeySectionCheckTitle" = "Leta efter uppdateringar"; + +/* On Startup */ +"HockeySectionCheckStartup" = "Vid uppstart"; + +/* Daily */ +"HockeySectionCheckDaily" = "Dagligen"; + +/* Manually */ +"HockeySectionCheckManually" = "Manuellt"; + + +"HockeyVersion" = "Version"; +"HockeyShowPreviousVersions" = "Se tidigare versioner..."; +"HockeyNoUpdateNeededTitle" = "Ingen uppdatering tillgänglig"; +"HockeyNoUpdateNeededMessage" = "%@ är den senaste versionen."; +"HockeyUpdateAlertTextWithAppVersion" = "%@ finns."; +"HockeyIgnore" = "Ignorera"; +"HockeyShowUpdate" = "Visa"; +"HockeyInstallUpdate" = "Installera"; +"HockeyError" = "Error"; +"HockeyOK" = "OK"; +"HockeyWarning" = "Varning"; +"HockeyNoReleaseNotesAvailable" = "Inga releasenoteringar tillgängliga."; + +"HockeyAuthorizationProgress" = "Auktoriserar..."; +"HockeyAuthorizationOffline" = "Internetuppkoppling krävs!"; +"HockeyAuthorizationDenied" = "Auktoriseringen nekades. Kontakta utvecklaren."; + +"HockeyiOS3Message" = "In-App nedladdning kräver iOS 4 eller högre. Du kan uppdatera applikationen genoma att ladda ner IPA-filen från %@ och synka in den i iTunes."; +"HockeySimulatorMessage" = "Hockey Update fungerar inte i Simulatorn.\nitms-services:// url schemat är implementerat men fungerar ej."; + +"HockeyButtonCheck" = "FRÅGA"; +"HockeyButtonSearching" = "FRÅGAR"; +"HockeyButtonUpdate" = "UPPDATERA"; +"HockeyButtonInstalling" = "INSTALLERAR"; +"HockeyButtonOffline" = "OFFLINE"; "HockeyInstalled" = "INSTALLERAD"; \ No newline at end of file diff --git a/iPhone/Prompt/Prompt.h b/iPhone/Prompt/Prompt.h index 43a006a..d6d961b 100644 --- a/iPhone/Prompt/Prompt.h +++ b/iPhone/Prompt/Prompt.h @@ -3,10 +3,10 @@ // MIT Licensed #import -#ifdef PHONEGAP_FRAMEWORK -#import +#ifdef CORDOVA_FRAMEWORK +#import #else -#import "PGPlugin.h" +#import "Cordova/CDVPlugin.h" #endif @interface PromptAlertView : UIAlertView { @@ -26,10 +26,9 @@ - (NSString *)getCallback; @end -@interface Prompt : PGPlugin { - -} +@interface Prompt : CDVPlugin - (void) show:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; + @end diff --git a/iPhone/Prompt/Prompt.js b/iPhone/Prompt/Prompt.js index abe8541..1a8942d 100644 --- a/iPhone/Prompt/Prompt.js +++ b/iPhone/Prompt/Prompt.js @@ -32,15 +32,15 @@ Prompt.prototype.show = function(title, okCallback, cancelCallback, okButtonTitl } }; var callback = 'window.plugins.Prompt.callbackMap.' + key; - PhoneGap.exec("Prompt.show", callback, defaults); + cordova.exec("Prompt.show", callback, defaults); }; Prompt.prototype.callbackMap = {}; Prompt.prototype.callbackIdx = 0; -PhoneGap.addConstructor(function() { - if(!window.plugins) { - window.plugins = {}; - } +if(!window.plugins) { + window.plugins = {}; +} +if(!window.plugins.Prompt) { window.plugins.Prompt = new Prompt(); }); diff --git a/iPhone/Torch/Torch.h b/iPhone/Torch/Torch.h index f086a53..23b137c 100644 --- a/iPhone/Torch/Torch.h +++ b/iPhone/Torch/Torch.h @@ -9,11 +9,19 @@ #ifdef PHONEGAP_FRAMEWORK #import -#else -#import "PGPlugin.h" #endif +#ifdef CORDOVA_FRAMEWORK +#import +#endif + +#ifdef PHONEGAP_FRAMEWORK @interface Torch : PGPlugin { +#endif + +#ifdef CORDOVA_FRAMEWORK +@interface Torch : CDVPlugin { +#endif AVCaptureSession* session; } diff --git a/iPhone/Torch/Torch.m b/iPhone/Torch/Torch.m index 5df3e44..a4ccfbe 100644 --- a/iPhone/Torch/Torch.m +++ b/iPhone/Torch/Torch.m @@ -18,8 +18,14 @@ @synthesize session; - +#ifdef PHONEGAP_FRAMEWORK - (PGPlugin*) initWithWebView:(UIWebView*)theWebView +#endif + +#ifdef CORDOVA_FRAMEWORK +- (CDVPlugin*) initWithWebView:(UIWebView*)theWebView +#endif + { self = (Torch*)[super initWithWebView:theWebView]; if (self) {