diff --git a/.idea/libraries/sass_stdlib.xml b/.idea/libraries/sass_stdlib.xml new file mode 100644 index 0000000..546bfd1 --- /dev/null +++ b/.idea/libraries/sass_stdlib.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/01 - viewnavigator/01 - basic/index.html b/samples/01 - viewnavigator/01 - basic/index.html index 03bfc0b..9e96628 100644 --- a/samples/01 - viewnavigator/01 - basic/index.html +++ b/samples/01 - viewnavigator/01 - basic/index.html @@ -1,67 +1,67 @@ - View Navigator Sample - + View Navigator Sample + - - - - - - - + + + + + + + - \ No newline at end of file + diff --git a/samples/01 - viewnavigator/02 - movie reviews/phonegap-1.4.1.js b/samples/01 - viewnavigator/02 - movie reviews/phonegap-1.4.1.js new file mode 100644 index 0000000..680e180 --- /dev/null +++ b/samples/01 - viewnavigator/02 - movie reviews/phonegap-1.4.1.js @@ -0,0 +1,4123 @@ +/* PhoneGap v1.4.1 */ +/* + 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 PhoneGap === "undefined") { + +if (typeof(DeviceInfo) !== 'object'){ + DeviceInfo = {}; +} +/** + * This represents the PhoneGap API itself, and provides a global namespace for accessing + * information about the state of PhoneGap. + * @class + */ +PhoneGap = { + // This queue holds the currently executing command and all pending + // commands executed with PhoneGap.exec(). + commandQueue: [], + // Indicates if we're currently in the middle of flushing the command + // queue on the native side. + commandQueueFlushing: false, + _constructors: [], + documentEventHandler: {}, // Collection of custom document event handlers + windowEventHandler: {} +}; + +/** + * List of resource files loaded by PhoneGap. + * This is used to ensure JS and other files are loaded only once. + */ +PhoneGap.resources = {base: true}; + +/** + * Determine if resource has been loaded by PhoneGap + * + * @param name + * @return + */ +PhoneGap.hasResource = function(name) { + return PhoneGap.resources[name]; +}; + +/** + * Add a resource to list of loaded resources by PhoneGap + * + * @param name + */ +PhoneGap.addResource = function(name) { + PhoneGap.resources[name] = true; +}; + +/** + * Boolean flag indicating if the PhoneGap API is available and initialized. + */ // TODO: Remove this, it is unused here ... -jm +PhoneGap.available = DeviceInfo.uuid != undefined; + +/** + * Add an initialization function to a queue that ensures it will run and initialize + * application constructors only once PhoneGap has been initialized. + * @param {Function} func The function callback you want run once PhoneGap is initialized + */ +PhoneGap.addConstructor = function(func) { + var state = document.readyState; + if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null ) + { + func(); + } + else + { + PhoneGap._constructors.push(func); + } +}; + +(function() + { + var timer = setInterval(function() + { + + var state = document.readyState; + + if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null ) + { + clearInterval(timer); // stop looking + // run our constructors list + while (PhoneGap._constructors.length > 0) + { + var constructor = PhoneGap._constructors.shift(); + try + { + constructor(); + } + catch(e) + { + if (typeof(console['log']) == 'function') + { + console.log("Failed to run constructor: " + console.processMessage(e)); + } + else + { + alert("Failed to run constructor: " + e.message); + } + } + } + // all constructors run, now fire the deviceready event + var e = document.createEvent('Events'); + e.initEvent('deviceready'); + document.dispatchEvent(e); + } + }, 1); +})(); + +// session id for calls +PhoneGap.sessionKey = 0; + +// centralized callbacks +PhoneGap.callbackId = 0; +PhoneGap.callbacks = {}; +PhoneGap.callbackStatus = { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }; + +/** + * Creates a gap bridge iframe used to notify the native code about queued + * commands. + * + * @private + */ +PhoneGap.createGapBridge = function() { + gapBridge = document.createElement("iframe"); + gapBridge.setAttribute("style", "display:none;"); + gapBridge.setAttribute("height","0px"); + gapBridge.setAttribute("width","0px"); + gapBridge.setAttribute("frameborder","0"); + document.documentElement.appendChild(gapBridge); + return gapBridge; +} + +/** + * Execute a PhoneGap command by queuing it and letting the native side know + * there are queued commands. The native side will then request all of the + * queued commands and execute them. + * + * Arguments may be in one of two formats: + * + * FORMAT ONE (preferable) + * The native side will call PhoneGap.callbackSuccess or + * PhoneGap.callbackError, depending upon the result of the action. + * + * @param {Function} success The success callback + * @param {Function} fail The fail callback + * @param {String} service The name of the service to use + * @param {String} action The name of the action to use + * @param {String[]} [args] Zero or more arguments to pass to the method + * + * FORMAT TWO + * @param {String} command Command to be run in PhoneGap, e.g. + * "ClassName.method" + * @param {String[]} [args] Zero or more arguments to pass to the method + * object parameters are passed as an array object + * [object1, object2] each object will be passed as + * JSON strings + */ +PhoneGap.exec = function() { + if (!PhoneGap.available) { + alert("ERROR: Attempting to call PhoneGap.exec()" + +" before 'deviceready'. Ignoring."); + return; + } + + var successCallback, failCallback, service, action, actionArgs; + var callbackId = null; + if (typeof arguments[0] !== "string") { + // FORMAT ONE + successCallback = arguments[0]; + failCallback = arguments[1]; + service = arguments[2]; + action = arguments[3]; + actionArgs = arguments[4]; + + // Since we need to maintain backwards compatibility, we have to pass + // an invalid callbackId even if no callback was provided since plugins + // will be expecting it. The PhoneGap.exec() implementation allocates + // an invalid callbackId and passes it even if no callbacks were given. + callbackId = 'INVALID'; + } else { + // FORMAT TWO + splitCommand = arguments[0].split("."); + action = splitCommand.pop(); + service = splitCommand.join("."); + actionArgs = Array.prototype.splice.call(arguments, 1); + } + + // Start building the command object. + var command = { + className: service, + methodName: action, + arguments: [] + }; + + // Register the callbacks and add the callbackId to the positional + // arguments if given. + if (successCallback || failCallback) { + callbackId = service + PhoneGap.callbackId++; + PhoneGap.callbacks[callbackId] = + {success:successCallback, fail:failCallback}; + } + if (callbackId != null) { + command.arguments.push(callbackId); + } + + for (var i = 0; i < actionArgs.length; ++i) { + var arg = actionArgs[i]; + if (arg == undefined || arg == null) { + continue; + } else if (typeof(arg) == 'object') { + command.options = arg; + } else { + command.arguments.push(arg); + } + } + + // Stringify and queue the command. We stringify to command now to + // effectively clone the command arguments in case they are mutated before + // the command is executed. + PhoneGap.commandQueue.push(JSON.stringify(command)); + + // If the queue length is 1, then that means it was empty before we queued + // the given command, so let the native side know that we have some + // commands to execute, unless the queue is currently being flushed, in + // which case the command will be picked up without notification. + if (PhoneGap.commandQueue.length == 1 && !PhoneGap.commandQueueFlushing) { + if (!PhoneGap.gapBridge) { + PhoneGap.gapBridge = PhoneGap.createGapBridge(); + } + + PhoneGap.gapBridge.src = "gap://ready"; + } +} + +/** + * Called by native code to retrieve all queued commands and clear the queue. + */ +PhoneGap.getAndClearQueuedCommands = function() { + json = JSON.stringify(PhoneGap.commandQueue); + PhoneGap.commandQueue = []; + return json; +} + +/** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + * args.status - PhoneGap.callbackStatus + * args.message - return value + * args.keepCallback - 0 to remove callback, 1 to keep callback in PhoneGap.callbacks[] + */ +PhoneGap.callbackSuccess = function(callbackId, args) { + if (PhoneGap.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status == PhoneGap.callbackStatus.OK) { + try { + if (PhoneGap.callbacks[callbackId].success) { + PhoneGap.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } +}; + +/** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ +PhoneGap.callbackError = function(callbackId, args) { + if (PhoneGap.callbacks[callbackId]) { + try { + if (PhoneGap.callbacks[callbackId].fail) { + PhoneGap.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } +}; + + +/** + * Does a deep clone of the object. + * + * @param obj + * @return + */ +PhoneGap.clone = function(obj) { + if(!obj) { + return obj; + } + + if(obj instanceof Array){ + var retVal = new Array(); + for(var i = 0; i < obj.length; ++i){ + retVal.push(PhoneGap.clone(obj[i])); + } + return retVal; + } + + if (obj instanceof Function) { + return obj; + } + + if(!(obj instanceof Object)){ + return obj; + } + + if (obj instanceof Date) { + return obj; + } + + retVal = new Object(); + for(i in obj){ + if(!(i in retVal) || retVal[i] != obj[i]) { + retVal[i] = PhoneGap.clone(obj[i]); + } + } + return retVal; +}; + +// Intercept calls to document.addEventListener +PhoneGap.m_document_addEventListener = document.addEventListener; + +// Intercept calls to window.addEventListener +PhoneGap.m_window_addEventListener = window.addEventListener; + +/** + * Add a custom window event handler. + * + * @param {String} event The event name that callback handles + * @param {Function} callback The event handler + */ +PhoneGap.addWindowEventHandler = function(event, callback) { + PhoneGap.windowEventHandler[event] = callback; +} + +/** + * Add a custom document event handler. + * + * @param {String} event The event name that callback handles + * @param {Function} callback The event handler + */ +PhoneGap.addDocumentEventHandler = function(event, callback) { + PhoneGap.documentEventHandler[event] = callback; +} + +/** + * Intercept adding document event listeners and handle our own + * + * @param {Object} evt + * @param {Function} handler + * @param capture + */ +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + + // If subscribing to an event that is handled by a plugin + if (typeof PhoneGap.documentEventHandler[e] !== "undefined") { + if (PhoneGap.documentEventHandler[e](e, handler, true)) { + return; // Stop default behavior + } + } + + PhoneGap.m_document_addEventListener.call(document, evt, handler, capture); +}; + +/** + * Intercept adding window event listeners and handle our own + * + * @param {Object} evt + * @param {Function} handler + * @param capture + */ +window.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + + // If subscribing to an event that is handled by a plugin + if (typeof PhoneGap.windowEventHandler[e] !== "undefined") { + if (PhoneGap.windowEventHandler[e](e, handler, true)) { + return; // Stop default behavior + } + } + + PhoneGap.m_window_addEventListener.call(window, evt, handler, capture); +}; + +// Intercept calls to document.removeEventListener and watch for events that +// are generated by PhoneGap native code +PhoneGap.m_document_removeEventListener = document.removeEventListener; + +// Intercept calls to window.removeEventListener +PhoneGap.m_window_removeEventListener = window.removeEventListener; + +/** + * Intercept removing document event listeners and handle our own + * + * @param {Object} evt + * @param {Function} handler + * @param capture + */ +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + + // If unsubcribing from an event that is handled by a plugin + if (typeof PhoneGap.documentEventHandler[e] !== "undefined") { + if (PhoneGap.documentEventHandler[e](e, handler, false)) { + return; // Stop default behavior + } + } + + PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture); +}; + +/** + * Intercept removing window event listeners and handle our own + * + * @param {Object} evt + * @param {Function} handler + * @param capture + */ +window.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + + // If unsubcribing from an event that is handled by a plugin + if (typeof PhoneGap.windowEventHandler[e] !== "undefined") { + if (PhoneGap.windowEventHandler[e](e, handler, false)) { + return; // Stop default behavior + } + } + + PhoneGap.m_window_removeEventListener.call(window, evt, handler, capture); +}; + +/** + * Method to fire document event + * + * @param {String} type The event type to fire + * @param {Object} data Data to send with event + */ +PhoneGap.fireDocumentEvent = function(type, data) { + var e = document.createEvent('Events'); + e.initEvent(type); + if (data) { + for (var i in data) { + e[i] = data[i]; + } + } + document.dispatchEvent(e); +}; + +/** + * Method to fire window event + * + * @param {String} type The event type to fire + * @param {Object} data Data to send with event + */ +PhoneGap.fireWindowEvent = function(type, data) { + var e = document.createEvent('Events'); + e.initEvent(type); + if (data) { + for (var i in data) { + e[i] = data[i]; + } + } + window.dispatchEvent(e); +}; + +/** + * Method to fire event from native code + * Leaving this generic version to handle problems with iOS 3.x. Is currently used by orientation and battery events + * Remove when iOS 3.x no longer supported and call fireWindowEvent or fireDocumentEvent directly + */ +PhoneGap.fireEvent = function(type, target, data) { + var e = document.createEvent('Events'); + e.initEvent(type); + if (data) { + for (var i in data) { + e[i] = data[i]; + } + } + target = target || document; + if (target.dispatchEvent === undefined) { // ie window.dispatchEvent is undefined in iOS 3.x + target = document; + } + + target.dispatchEvent(e); +}; +/** + * Create a UUID + * + * @return + */ +PhoneGap.createUUID = function() { + return PhoneGap.UUIDcreatePart(4) + '-' + + PhoneGap.UUIDcreatePart(2) + '-' + + PhoneGap.UUIDcreatePart(2) + '-' + + PhoneGap.UUIDcreatePart(2) + '-' + + PhoneGap.UUIDcreatePart(6); +}; + +PhoneGap.UUIDcreatePart = function(length) { + var uuidpart = ""; + for (var i=0; i -1) { + me._batteryListener.splice(pos, 1); + } + } else if (eventType === "batterylow") { + var pos = me._lowListener.indexOf(handler); + if (pos > -1) { + me._lowListener.splice(pos, 1); + } + } else if (eventType === "batterycritical") { + var pos = me._criticalListener.indexOf(handler); + if (pos > -1) { + me._criticalListener.splice(pos, 1); + } + } + + // If there are no more registered event listeners stop the battery listener on native side. + if (me._batteryListener.length === 0 && me._lowListener.length === 0 && me._criticalListener.length === 0) { + PhoneGap.exec(null, null, "com.phonegap.battery", "stop", []); + } + } +}; + +/** + * Callback for battery status + * + * @param {Object} info keys: level, isPlugged + */ +Battery.prototype._status = function(info) { + if (info) { + var me = this; + if (me._level != info.level || me._isPlugged != info.isPlugged) { + // Fire batterystatus event + //PhoneGap.fireWindowEvent("batterystatus", info); + // use this workaround since iOS 3.x does have window.dispatchEvent + PhoneGap.fireEvent("batterystatus", window, info); + + // Fire low battery event + if (info.level == 20 || info.level == 5) { + if (info.level == 20) { + //PhoneGap.fireWindowEvent("batterylow", info); + // use this workaround since iOS 3.x does not have window.dispatchEvent + PhoneGap.fireEvent("batterylow", window, info); + } + else { + //PhoneGap.fireWindowEvent("batterycritical", info); + // use this workaround since iOS 3.x does not have window.dispatchEvent + PhoneGap.fireEvent("batterycritical", window, info); + } + } + } + me._level = info.level; + me._isPlugged = info.isPlugged; + } +}; + +/** + * Error callback for battery start + */ +Battery.prototype._error = function(e) { + console.log("Error initializing Battery: " + e); +}; + +PhoneGap.addConstructor(function() { + if (typeof navigator.battery === "undefined") { + navigator.battery = new Battery(); + PhoneGap.addWindowEventHandler("batterystatus", navigator.battery.eventHandler); + PhoneGap.addWindowEventHandler("batterylow", navigator.battery.eventHandler); + PhoneGap.addWindowEventHandler("batterycritical", navigator.battery.eventHandler); + } +}); +}if (!PhoneGap.hasResource("camera")) { + PhoneGap.addResource("camera"); + + +/** + * This class provides access to the device camera. + * @constructor + */ +Camera = function() { + +} +/** + * Available Camera Options + * {boolean} allowEdit - true to allow editing image, default = false + * {number} quality 0-100 (low to high) default = 100 + * {Camera.DestinationType} destinationType default = DATA_URL + * {Camera.PictureSourceType} sourceType default = CAMERA + * {number} targetWidth - width in pixels to scale image default = 0 (no scaling) + * {number} targetHeight - height in pixels to scale image default = 0 (no scaling) + * {Camera.EncodingType} - encodingType default = JPEG + * {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; + } + + PhoneGap.exec(successCallback, errorCallback, "com.phonegap.camera","getPicture",[options]); +}; + + + +PhoneGap.addConstructor(function() { + if (typeof navigator.camera == "undefined") navigator.camera = new Camera(); +}); +}; + +if (!PhoneGap.hasResource("device")) { + PhoneGap.addResource("device"); + +/** + * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +Device = function() +{ + this.platform = null; + this.version = null; + this.name = null; + this.phonegap = null; + this.uuid = null; + try + { + this.platform = DeviceInfo.platform; + this.version = DeviceInfo.version; + this.name = DeviceInfo.name; + this.phonegap = DeviceInfo.gap; + this.uuid = DeviceInfo.uuid; + + } + catch(e) + { + // TODO: + } + this.available = PhoneGap.available = this.uuid != null; +} + +PhoneGap.addConstructor(function() { + if (typeof navigator.device === "undefined") { + navigator.device = window.device = new Device(); + } +}); +}; +if (!PhoneGap.hasResource("capture")) { + PhoneGap.addResource("capture"); +/** + * The CaptureError interface encapsulates all errors in the Capture API. + */ +function CaptureError() { + this.code = null; +}; + +// Capture error codes +CaptureError.CAPTURE_INTERNAL_ERR = 0; +CaptureError.CAPTURE_APPLICATION_BUSY = 1; +CaptureError.CAPTURE_INVALID_ARGUMENT = 2; +CaptureError.CAPTURE_NO_MEDIA_FILES = 3; +CaptureError.CAPTURE_NOT_SUPPORTED = 20; + +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +function Capture() { + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +}; + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + * + * No audio recorder to launch for iOS - return CAPTURE_NOT_SUPPORTED + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options) { + /*if (errorCallback && typeof errorCallback === "function") { + errorCallback({ + "code": CaptureError.CAPTURE_NOT_SUPPORTED + }); + }*/ + PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureAudio", [options]); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options) { + PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureImage", [options]); +}; + +/** + * 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 { + PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "requestFileSystem", [type, size]); + } +}; + +/** + * + * @param {DOMString} uri referring to a local file in a filesystem + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "resolveLocalFileSystemURI", [uri]); +}; + +/** +* This function is required as we need to convert raw +* JSON objects into concrete File and Directory objects. +* +* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects. +* @returns an entry +*/ +LocalFileSystem.prototype._castFS = function(pluginResult) { + var entry = null; + entry = new DirectoryEntry(); + entry.isDirectory = pluginResult.message.root.isDirectory; + entry.isFile = pluginResult.message.root.isFile; + entry.name = pluginResult.message.root.name; + entry.fullPath = pluginResult.message.root.fullPath; + pluginResult.message.root = entry; + return pluginResult; +} + +LocalFileSystem.prototype._castEntry = function(pluginResult) { + var entry = null; + if (pluginResult.message.isDirectory) { + entry = new DirectoryEntry(); + } + else if (pluginResult.message.isFile) { + entry = new FileEntry(); + } + entry.isDirectory = pluginResult.message.isDirectory; + entry.isFile = pluginResult.message.isFile; + entry.name = pluginResult.message.name; + entry.fullPath = pluginResult.message.fullPath; + pluginResult.message = entry; + return pluginResult; +} + +LocalFileSystem.prototype._castEntries = function(pluginResult) { + var entries = pluginResult.message; + var retVal = []; + for (i=0; i - View Navigator Multiple Instances - + View Navigator Multiple Instances + - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/01 - viewnavigator/05 - legacy/libs/jquery.address-1.4.js b/samples/01 - viewnavigator/05 - legacy/libs/jquery.address-1.4.js new file mode 100644 index 0000000..12a9abe --- /dev/null +++ b/samples/01 - viewnavigator/05 - legacy/libs/jquery.address-1.4.js @@ -0,0 +1,655 @@ +/* + * jQuery Address Plugin v1.4 + * http://www.asual.com/jquery/address/ + * + * Copyright (c) 2009-2010 Rostislav Hristov + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Date: 2011-05-04 14:22:12 +0300 (Wed, 04 May 2011) + */ +(function ($) { + + $.address = (function () { + + var _trigger = function(name) { + $($.address).trigger( + $.extend($.Event(name), + (function() { + var parameters = {}, + parameterNames = $.address.parameterNames(); + for (var i = 0, l = parameterNames.length; i < l; i++) { + parameters[parameterNames[i]] = $.address.parameter(parameterNames[i]); + } + return { + value: $.address.value(), + path: $.address.path(), + pathNames: $.address.pathNames(), + parameterNames: parameterNames, + parameters: parameters, + queryString: $.address.queryString() + }; + }).call($.address) + ) + ); + }, + _bind = function(value, data, fn) { + $().bind.apply($($.address), Array.prototype.slice.call(arguments)); + return $.address; + }, + _supportsState = function() { + return (_h.pushState && _opts.state !== UNDEFINED); + }, + _hrefState = function() { + return ('/' + _l.pathname.replace(new RegExp(_opts.state), '') + + _l.search + (_hrefHash() ? '#' + _hrefHash() : '')).replace(_re, '/'); + }, + _hrefHash = function() { + var index = _l.href.indexOf('#'); + return index != -1 ? _crawl(_l.href.substr(index + 1), FALSE) : ''; + }, + _href = function() { + return _supportsState() ? _hrefState() : _hrefHash(); + }, + _window = function() { + try { + return top.document !== UNDEFINED ? top : window; + } catch (e) { + return window; + } + }, + _js = function() { + return 'javascript'; + }, + _strict = function(value) { + value = value.toString(); + return (_opts.strict && value.substr(0, 1) != '/' ? '/' : '') + value; + }, + _crawl = function(value, direction) { + if (_opts.crawlable && direction) { + return (value !== '' ? '!' : '') + value; + } + return value.replace(/^\!/, ''); + }, + _cssint = function(el, value) { + return parseInt(el.css(value), 10); + }, + _search = function(el) { + var url, s; + for (var i = 0, l = el.childNodes.length; i < l; i++) { + try { + if ('src' in el.childNodes[i] && el.childNodes[i].src) { + url = String(el.childNodes[i].src); + } + } catch (e) { + // IE Invalid pointer problem with base64 encoded images + } + s = _search(el.childNodes[i]); + if (s) { + url = s; + } + } + return url; + }, + _listen = function() { + if (!_silent) { + var hash = _href(), + diff = _value != hash; + if (diff) { + if (_msie && _version < 7) { + _l.reload(); + } else { + if (_msie && _version < 8 && _opts.history) { + _st(_html, 50); + } + _value = hash; + _update(FALSE); + } + } + } + }, + _update = function(internal) { + _trigger(CHANGE); + _trigger(internal ? INTERNAL_CHANGE : EXTERNAL_CHANGE); + _st(_track, 10); + }, + _track = function() { + if (_opts.tracker !== 'null' && _opts.tracker !== null) { + var fn = $.isFunction(_opts.tracker) ? _opts.tracker : _t[_opts.tracker], + value = (_l.pathname + _l.search + + ($.address && !_supportsState() ? $.address.value() : '')) + .replace(/\/\//, '/').replace(/^\/$/, ''); + if ($.isFunction(fn)) { + fn(value); + } else if ($.isFunction(_t.urchinTracker)) { + _t.urchinTracker(value); + } else if (_t.pageTracker !== UNDEFINED && $.isFunction(_t.pageTracker._trackPageview)) { + _t.pageTracker._trackPageview(value); + } else if (_t._gaq !== UNDEFINED && $.isFunction(_t._gaq.push)) { + _t._gaq.push(['_trackPageview', decodeURI(value)]); + } + } + }, + _html = function() { + var src = _js() + ':' + FALSE + ';document.open();document.writeln(\'' + + _d.title.replace('\'', '\\\'') + ' - - - - - - - - - - + + + + + + + + + + + - - + + + + + + + + + + - - - - \ No newline at end of file + } + + $( "#"+movie.id ).addClass( "listSelected" ); + + var html = "
"; + html += "

" + movie.title + "

"; + html += "

" + movie.mpaa_rating + ", " + movie.runtime + "min. - (" + movie.year + ")

"; + html += ""; + html += "

" + movie.synopsis + "

"; + html += "

Ratings:

"; + html += "Audience: " + movie.ratings.audience_score + " - " + movie.ratings.audience_rating + "
" + html += "Critics: " + movie.ratings.critics_score + " - " + movie.ratings.critics_rating + "
" + html += "

" + html += "

What the critics say:

" + movie.critics_consensus + "

"; + html += "

Cast:

    "; + + for ( var i = 0; i < movie.abridged_cast.length; i ++ ) + { + var actor = movie.abridged_cast[i]; + html += "
  • " + actor.name + ": "; + + if ( actor.characters ) { + for ( var j = 0; j < actor.characters.length; j ++ ) + { + if ( j > 0 ) { html += ", " }; + html += actor.characters[j]; + } + } + + html += "
  • "; + } + + html += "

"; + + html += "
"; + + var viewDescriptor = { title: movie.title, + view: $(html) + }; + + if ( controller.tabletView ) { + window.splitViewNavigator.replaceBodyView( viewDescriptor ); + } + else { + viewDescriptor.backLabel = "Back"; + window.viewNavigator.pushView( viewDescriptor ); + } + } + + + $(document).ready( function() { + + controller.init(); + var _w = Math.max( $(window).width(), $(window).height() ); + var _h = Math.min( $(window).width(), $(window).height() ); + controller.tabletView = (_w >= 1000 && _h >= 600); + + var defaultView = { title: "Rotten Tomatoes", + view: $("
") + }; + + if ( controller.tabletView ) { + //Setup the ViewNavigator + new SplitViewNavigator( 'body', { + toggleButtonLabel: 'Movies' + }); + window.splitViewNavigator.pushSidebarView( controller.rootView ); + window.splitViewNavigator.pushBodyView( defaultView ); + } + else { + //phone view + window.viewNavigator = new ViewNavigator( 'body' ); + window.viewNavigator.pushView( controller.rootView ); + } + } ); + + + + + + diff --git a/src/libs/iscroll.js b/src/libs/iscroll.js old mode 100755 new mode 100644 index bbf0d42..f8576f0 --- a/src/libs/iscroll.js +++ b/src/libs/iscroll.js @@ -1,60 +1,88 @@ /*! - * iScroll v4.1.9 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org + * iScroll v4.2.5 ~ Copyright (c) 2012 Matteo Spinelli, http://cubiq.org * Released under MIT license, http://cubiq.org/license */ -(function(){ +(function(window, doc){ var m = Math, - mround = function (r) { return r >> 0; }, - vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : - (/firefox/i).test(navigator.userAgent) ? 'Moz' : - (/trident/i).test(navigator.userAgent) ? 'ms' : - 'opera' in window ? 'O' : '', + dummyStyle = doc.createElement('div').style, + vendor = (function () { + var vendors = 't,webkitT,MozT,msT,OT'.split(','), + t, + i = 0, + l = vendors.length; + + for ( ; i < l; i++ ) { + t = vendors[i] + 'ransform'; + if ( t in dummyStyle ) { + return vendors[i].substr(0, vendors[i].length - 1); + } + } + + return false; + })(), + cssVendor = vendor ? '-' + vendor.toLowerCase() + '-' : '', + + // Style properties + transform = prefixStyle('transform'), + transitionProperty = prefixStyle('transitionProperty'), + transitionDuration = prefixStyle('transitionDuration'), + transformOrigin = prefixStyle('transformOrigin'), + transitionTimingFunction = prefixStyle('transitionTimingFunction'), + transitionDelay = prefixStyle('transitionDelay'), // 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), + isAndroid = (/android/gi).test(navigator.appVersion), + isIDevice = (/iphone|ipad/gi).test(navigator.appVersion), + isTouchPad = (/hp-tablet/gi).test(navigator.appVersion), - has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(), + has3d = prefixStyle('perspective') in dummyStyle, hasTouch = 'ontouchstart' in window && !isTouchPad, - hasTransform = vendor + 'Transform' in document.documentElement.style, - hasTransitionEnd = isIDevice || isPlaybook, + hasTransform = vendor !== false, + hasTransitionEnd = prefixStyle('transition') in dummyStyle, - 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', + TRNEND_EV = (function () { + if ( vendor === false ) return false; + + var transitionEnd = { + '' : 'transitionend', + 'webkit' : 'webkitTransitionEnd', + 'Moz' : 'transitionend', + 'O' : 'otransitionend', + 'ms' : 'MSTransitionEnd' + }; + + return transitionEnd[vendor]; + })(), + + 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; + })(), // Helpers - trnOpen = 'translate' + (has3d ? '3d(' : '('), - trnClose = has3d ? ',0)' : ')', + translateZ = has3d ? ' translateZ(0)' : '', // Constructor iScroll = function (el, options) { var that = this, - doc = document, i; that.wrapper = typeof el == 'object' ? el : doc.getElementById(el); @@ -75,6 +103,7 @@ var m = Math, useTransition: false, topOffset: 0, checkDOMChanges: false, // Experimental + handleClick: true, // Scrollbar hScrollbar: true, @@ -118,27 +147,26 @@ var m = Math, that.y = that.options.y; // Normalize options - that.options.useTransform = hasTransform ? that.options.useTransform : false; + that.options.useTransform = hasTransform && that.options.useTransform; 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! + // translate3d and scale doesn't work together! // Ignoring 3d ONLY WHEN YOU SET that.options.zoom if ( that.options.zoom && isAndroid ){ - trnOpen = 'translate('; - trnClose = ')'; + translateZ = ''; } // 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)'; + that.scroller.style[transitionProperty] = that.options.useTransform ? cssVendor + 'transform' : 'top left'; + that.scroller.style[transitionDuration] = '0'; + that.scroller.style[transformOrigin] = '0 0'; + if (that.options.useTransition) that.scroller.style[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; + if (that.options.useTransform) that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px)' + translateZ; else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; if (that.options.useTransition) that.options.fixedScrollbar = true; @@ -148,9 +176,10 @@ var m = Math, 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.wheelAction != 'none') { + that._bind('DOMMouseScroll'); + that._bind('mousewheel'); + } } if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () { @@ -175,17 +204,14 @@ iScroll.prototype = { switch(e.type) { case START_EV: if (!hasTouch && e.button !== 0) return; - var nodeName = e.target.nodeName.toUpperCase(); - if (nodeName == "TEXTAREA" || nodeName == "INPUT" || nodeName == "SELECT" ) 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; + case 'DOMMouseScroll': case 'mousewheel': that._wheel(e); break; + case TRNEND_EV: that._transitionEnd(e); break; } }, @@ -198,12 +224,11 @@ iScroll.prototype = { _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'] = ''; + if (hasTransform) that[dir + 'ScrollbarIndicator'].style[transform] = ''; that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']); that[dir + 'ScrollbarWrapper'] = null; that[dir + 'ScrollbarIndicator'] = null; @@ -219,7 +244,7 @@ iScroll.prototype = { 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'); + bar.style.cssText += ';pointer-events:none;' + cssVendor + 'transition-property:opacity;' + cssVendor + 'transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1'); that.wrapper.appendChild(bar); that[dir + 'ScrollbarWrapper'] = bar; @@ -227,10 +252,10 @@ iScroll.prototype = { // 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 = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);' + cssVendor + 'background-clip:padding-box;' + cssVendor + 'box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';' + cssVendor + '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)'; + bar.style.cssText += ';pointer-events:none;' + cssVendor + 'transition-property:' + cssVendor + 'transform;' + cssVendor + 'transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);' + cssVendor + 'transition-duration:0;' + cssVendor + 'transform: translate(0,0)' + translateZ; + if (that.options.useTransition) bar.style.cssText += ';' + cssVendor + 'transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)'; that[dir + 'ScrollbarWrapper'].appendChild(bar); that[dir + 'ScrollbarIndicator'] = bar; @@ -238,13 +263,13 @@ iScroll.prototype = { if (dir == 'h') { that.hScrollbarSize = that.hScrollbarWrapper.clientWidth; - that.hScrollbarIndicatorSize = m.max(mround(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8); + that.hScrollbarIndicatorSize = m.max(m.round(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.vScrollbarIndicatorSize = m.max(m.round(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; @@ -260,14 +285,16 @@ iScroll.prototype = { }, _pos: function (x, y) { + if (this.zoomed) return; + 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 + ')'; + this.scroller.style[transform] = 'translate(' + x + 'px,' + y + 'px) scale(' + this.scale + ')' + translateZ; } else { - x = mround(x); - y = mround(y); + x = m.round(x); + y = m.round(y); this.scroller.style.left = x + 'px'; this.scroller.style.top = y + 'px'; } @@ -290,14 +317,14 @@ iScroll.prototype = { if (pos < 0) { if (!that.options.fixedScrollbar) { - size = that[dir + 'ScrollbarIndicatorSize'] + mround(pos * 3); + size = that[dir + 'ScrollbarIndicatorSize'] + m.round(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); + size = that[dir + 'ScrollbarIndicatorSize'] - m.round((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); @@ -306,9 +333,9 @@ iScroll.prototype = { } } - that[dir + 'ScrollbarWrapper'].style[vendor + 'TransitionDelay'] = '0'; + that[dir + 'ScrollbarWrapper'].style[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; + that[dir + 'ScrollbarIndicator'].style[transform] = 'translate(' + (dir == 'h' ? pos + 'px,0)' : '0,' + pos + 'px)') + translateZ; }, _start: function (e) { @@ -348,19 +375,20 @@ iScroll.prototype = { 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; + matrix = getComputedStyle(that.scroller, null)[transform].replace(/[^0-9\-.,]/g, '').split(','); + x = +(matrix[12] || matrix[4]); + y = +(matrix[13] || matrix[5]); } else { - x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1; - y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1; + x = +getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, ''); + y = +getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, ''); } if (x != that.x || y != that.y) { - if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + if (that.options.useTransition) that._unbind(TRNEND_EV); else cancelFrame(that.aniTime); that.steps = []; that._pos(x, y); + if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); } } @@ -376,9 +404,9 @@ iScroll.prototype = { if (that.options.onScrollStart) that.options.onScrollStart.call(that, e); - that._bind(MOVE_EV); - that._bind(END_EV); - that._bind(CANCEL_EV); + that._bind(MOVE_EV, window); + that._bind(END_EV, window); + that._bind(CANCEL_EV, window); }, _move: function (e) { @@ -411,7 +439,7 @@ iScroll.prototype = { 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 + ')'; + this.scroller.style[transform] = 'translate(' + newX + 'px,' + newY + 'px) scale(' + scale + ')' + translateZ; if (that.options.onZoom) that.options.onZoom.call(that, e); return; @@ -424,7 +452,7 @@ iScroll.prototype = { 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) { + 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; } @@ -463,7 +491,7 @@ iScroll.prototype = { }, _end: function (e) { - if (hasTouch && e.touches.length != 0) return; + if (hasTouch && e.touches.length !== 0) return; var that = this, point = hasTouch ? e.changedTouches[0] : e, @@ -478,9 +506,9 @@ iScroll.prototype = { snap, scale; - that._unbind(MOVE_EV); - that._unbind(END_EV); - that._unbind(CANCEL_EV); + that._unbind(MOVE_EV, window); + that._unbind(END_EV, window); + that._unbind(CANCEL_EV, window); if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e); @@ -494,8 +522,8 @@ iScroll.prototype = { 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.scroller.style[transitionDuration] = '200ms'; + that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px) scale(' + that.scale + ')' + translateZ; that.zoomed = false; that.refresh(); @@ -517,7 +545,7 @@ iScroll.prototype = { that.options.onZoomEnd.call(that, e); }, 200); // 200 is default zoom duration } - } else { + } else if (this.options.handleClick) { that.doubleTapTimer = setTimeout(function () { that.doubleTapTimer = null; @@ -526,7 +554,7 @@ iScroll.prototype = { while (target.nodeType != 1) target = target.parentNode; if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { - ev = document.createEvent('MouseEvents'); + ev = doc.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, @@ -538,7 +566,7 @@ iScroll.prototype = { } } - that._resetPos(200); + that._resetPos(400); if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); return; @@ -551,8 +579,8 @@ iScroll.prototype = { 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 ((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) { @@ -571,7 +599,7 @@ iScroll.prototype = { } } - that.scrollTo(mround(newPosX), mround(newPosY), newDuration); + that.scrollTo(m.round(newPosX), m.round(newPosY), newDuration); if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e); return; @@ -607,11 +635,11 @@ iScroll.prototype = { } if (that.hScrollbar && that.options.hideScrollbar) { - if (vendor == 'webkit') that.hScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + if (vendor == 'webkit') that.hScrollbarWrapper.style[transitionDelay] = '300ms'; that.hScrollbarWrapper.style.opacity = '0'; } if (that.vScrollbar && that.options.hideScrollbar) { - if (vendor == 'webkit') that.vScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms'; + if (vendor == 'webkit') that.vScrollbarWrapper.style[transitionDelay] = '300ms'; that.vScrollbarWrapper.style.opacity = '0'; } @@ -666,39 +694,28 @@ iScroll.prototype = { if (deltaY > that.minScrollY) deltaY = that.minScrollY; else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY; - - that.scrollTo(deltaX, deltaY, 0); + + if (that.maxScrollY < 0) { + 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._unbind(TRNEND_EV); that._startAni(); }, /** - * - * Utilities - * - */ + * + * Utilities + * + */ _startAni: function () { var that = this, startX = that.x, startY = that.y, @@ -724,7 +741,7 @@ iScroll.prototype = { that._transitionTime(step.time); that._pos(step.x, step.y); that.animating = false; - if (step.time) that._bind('webkitTransitionEnd'); + if (step.time) that._bind(TRNEND_EV); else that._resetPos(0); return; } @@ -754,9 +771,9 @@ iScroll.prototype = { _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; + this.scroller.style[transitionDuration] = time; + if (this.hScrollbar) this.hScrollbarIndicator.style[transitionDuration] = time; + if (this.vScrollbar) this.vScrollbarIndicator.style[transitionDuration] = time; }, _momentum: function (dist, time, maxDistUpper, maxDistLower, size) { @@ -765,7 +782,7 @@ iScroll.prototype = { newDist = (speed * speed) / (2 * deceleration), newTime = 0, outsideDist = 0; - // Proportinally reduce speed if we are outside of the boundaries + // Proportinally reduce speed if we are outside of the boundaries if (dist > 0 && newDist > maxDistUpper) { outsideDist = size / (6 / (newDist / speed * deceleration)); maxDistUpper = maxDistUpper + outsideDist; @@ -781,7 +798,7 @@ iScroll.prototype = { newDist = newDist * (dist < 0 ? -1 : 1); newTime = speed / deceleration; - return { dist: newDist, time: mround(newTime) }; + return { dist: newDist, time: m.round(newTime) }; }, _offset: function (el) { @@ -836,7 +853,7 @@ iScroll.prototype = { that.currPageY = page; // Snap with constant speed (proportional duration) - time = mround(m.max(sizeX, sizeY)) || 200; + time = m.round(m.max(sizeX, sizeY)) || 200; return { x: x, y: y, time: time }; }, @@ -851,14 +868,14 @@ iScroll.prototype = { /** - * - * Public methods - * - */ + * + * Public methods + * + */ destroy: function () { var that = this; - that.scroller.style[vendor + 'Transform'] = ''; + that.scroller.style[transform] = ''; // Remove the scrollbars that.hScrollbar = false; @@ -869,16 +886,16 @@ iScroll.prototype = { // 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); + that._unbind(MOVE_EV, window); + that._unbind(END_EV, window); + that._unbind(CANCEL_EV, window); if (!that.options.hasTouch) { - that._unbind('mouseout', that.wrapper); - that._unbind(WHEEL_EV); + that._unbind('DOMMouseScroll'); + that._unbind('mousewheel'); } - if (that.options.useTransition) that._unbind('webkitTransitionEnd'); + if (that.options.useTransition) that._unbind(TRNEND_EV); if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime); @@ -898,8 +915,8 @@ iScroll.prototype = { 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.scrollerW = m.round(that.scroller.offsetWidth * that.scale); + that.scrollerH = m.round((that.scroller.offsetHeight + that.minScrollY) * that.scale); that.maxScrollX = that.wrapperW - that.scrollerW; that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY; that.dirX = 0; @@ -954,8 +971,8 @@ iScroll.prototype = { that._scrollbar('v'); if (!that.zoomed) { - that.scroller.style[vendor + 'TransitionDuration'] = '0'; - that._resetPos(200); + that.scroller.style[transitionDuration] = '0'; + that._resetPos(400); } }, @@ -1026,9 +1043,9 @@ iScroll.prototype = { 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); + this._unbind(MOVE_EV, window); + this._unbind(END_EV, window); + this._unbind(CANCEL_EV, window); }, enable: function () { @@ -1036,7 +1053,7 @@ iScroll.prototype = { }, stop: function () { - if (this.options.useTransition) this._unbind('webkitTransitionEnd'); + if (this.options.useTransition) this._unbind(TRNEND_EV); else cancelFrame(this.aniTime); this.steps = []; this.moved = false; @@ -1062,8 +1079,8 @@ iScroll.prototype = { 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.scroller.style[transitionDuration] = time + 'ms'; + that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px) scale(' + scale + ')' + translateZ; that.zoomed = false; }, @@ -1072,7 +1089,16 @@ iScroll.prototype = { } }; +function prefixStyle (style) { + if ( vendor === '' ) return style; + + style = style.charAt(0).toUpperCase() + style.substr(1); + return vendor + style; +} + +dummyStyle = null; // for the sake of it + if (typeof exports !== 'undefined') exports.iScroll = iScroll; else window.iScroll = iScroll; -})(); +})(window, document); diff --git a/src/splitviewnavigator/splitviewnavigator.css b/src/splitviewnavigator/splitviewnavigator.css index 1613e9f..2d68267 100644 --- a/src/splitviewnavigator/splitviewnavigator.css +++ b/src/splitviewnavigator/splitviewnavigator.css @@ -12,75 +12,67 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .splitViewNavigator_root { - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; + position:absolute; + top:0px; + bottom:0px; + left:0px; + right:0px; } .splitViewNavigator_sidebar { - position: absolute; - overflow: hidden; + position:absolute; + overflow:hidden; -webkit-backface-visibility: hidden; - - -webkit-transform: translate3d(0,0,0); - -moz-transform: translate3d(0,0,0); - -o-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); transform: translate3d(0,0,0); - border-right: 1px solid #999; + -webkit-transform: translate3d(0,0,0); + border-right:1px solid #999; } .splitViewNavigator_body { - position: absolute; - background: white; - overflow: hidden; + position:absolute; + background:white; + overflow:hidden; -webkit-backface-visibility: hidden; - - -webkit-transform: translate3d(0,0,0); - -moz-transform: translate3d(0,0,0); - -o-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); transform: translate3d(0,0,0); + -webkit-transform: translate3d(0,0,0); } .content_overlay_visible { - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; + position:absolute; + top:0px; + bottom:0px; + left:0px; + right:0px; } .content_overlay_hidden { - position: absolute; - top: -1px; - left: -1px; - width: 0px; - height: 0px; - display: none; + position:absolute; + top:-1px; + left:-1px; + width:0px; + height:0px; + display:none; } .sidebar_portrait { - top: 0px; - bottom: 0px; - left: -240px; - width: 240px; + top:0px; + bottom:0px; + left:-240px; + width:240px; } .body_portrait { - top: 0px; - bottom: 0px; - right: 0px; - left: 0px; + top:0px; + bottom:0px; + right:0px; + left:0px; } .sidebar_landscape { - top: 0px; - bottom: 0px; - left: 0px; - width: 240px; + top:0px; + bottom:0px; + left:0px; + width:240px; } .body_landscape { diff --git a/src/splitviewnavigator/splitviewnavigator.js b/src/splitviewnavigator/splitviewnavigator.js index e768d05..9a94e01 100644 --- a/src/splitviewnavigator/splitviewnavigator.js +++ b/src/splitviewnavigator/splitviewnavigator.js @@ -11,62 +11,72 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var SplitViewNavigator = function( target, toggleButtonLabel, backLinkCSS, bindToWindow ) { - +var SplitViewNavigator = function( target, options ) { + + var defaults = { + CSSNamespace: 'splitViewNavigator_', + toggleButtonLabel: 'Menu', + backLinkCSS: 'viewNavigator_backButton', + bindToWindow: true, + useNoClickDelay: true + }; + + this.options = options = $.extend( {}, defaults, options ); + this.animating = false; this.animationDuration = 350; this.animationPerformed = false; - + this.uniqueId = this.guid(); this.parent = $( target ); - - var regexp = new RegExp("Windows Phone OS 7"); + + var regexp = new RegExp('Windows Phone OS 7'); this.winPhone = (navigator.userAgent.search(regexp) >= 0); - - this.rootElement = $('
'); - this.sidebarContainer = $('
'); + + this.rootElement = $('
'); + this.sidebarContainer = $('
'); this.contentOverlay = $('
'); - this.bodyContainer = $('
'); - - this.sidebarViewNavigator = new ViewNavigator( this.sidebarContainer.get()[0], backLinkCSS, false ); - - this.bodyViewNavigator = new ViewNavigator( this.bodyContainer.get()[0], backLinkCSS, false ); - - this.backLinkCSS = backLinkCSS ? backLinkCSS : "viewNavigator_backButton"; - - this.toggleSidebarButton = $('
  • '+toggleButtonLabel+'
  • '); - + this.bodyContainer = $('
    '); + + this.sidebarViewNavigator = new ViewNavigator( this.sidebarContainer.get()[0], options.backLinkCSS, false ); + + this.bodyViewNavigator = new ViewNavigator( this.bodyContainer.get()[0], options.backLinkCSS, false ); + + this.toggleSidebarButton = $('
  • ' + options.toggleButtonLabel + '
  • '); + this.rootElement.append( this.bodyContainer ); this.rootElement.append( this.contentOverlay ); - + this.rootElement.append( this.sidebarContainer ); - + var self = this; - - /*if ( "onorientationchange" in window ) { - $(window).bind( "orientationchange", function(event){ self.resizeContent() } ) + + /*if ( 'onorientationchange' in window ) { + $(window).bind( 'orientationchange', function(event){ self.resizeContent() } ) } else {*/ //$(window).resize( function(event){ self.resizeContent() } ); - //alert( this.parent.attr( "id" ) ); + //alert( this.parent.attr( 'id' ) ); this.parent.resize( function(event){ self.resizeContent() } ); //} - - if ( bindToWindow != false ) { + + if ( options.bindToWindow != false ) { $(window).resize( function(event){ self.resizeContent() } ); } else { this.parent.resize( function(event){ self.resizeContent() } ); } - + this.resizeContent(); - + this.parent.append( this.rootElement ); - + this.contentOverlay.click( function(event){ self.hideSidebar() } ); - - new NoClickDelay( this.contentOverlay.get()[0] ); - new NoClickDelay( this.toggleSidebarButton.get()[0] ); + + if ( this.options.useNoClickDelay ) { + new NoClickDelay( this.contentOverlay.get()[0] ); + new NoClickDelay( this.toggleSidebarButton.get()[0] ); + } window.splitViewNavigator = this; } @@ -74,68 +84,68 @@ var SplitViewNavigator = function( target, toggleButtonLabel, backLinkCSS, bindT SplitViewNavigator.prototype.resizeContent = function() { this.applyStylesByOrientation(); - this.sidebarViewNavigator.resizeContent(); + this.sidebarViewNavigator.resizeContent(); this.bodyViewNavigator.resizeContent() } SplitViewNavigator.prototype.applyStylesByOrientation = function() { var $window = $(window) - var w = $window.width(); - var h = $window.height(); - - - var orientation = (w >= h) ? "landscape" : "portrait"; - this.contentOverlay.removeClass( "content_overlay_visible" ).addClass( "content_overlay_hidden" ); - - //landscape - if ( orientation == "landscape" && this.orientation != orientation ) { - this.sidebarContainer.removeClass( "sidebar_portrait" ).addClass( "sidebar_landscape" ); - this.bodyViewNavigator.setHeaderPadding( 0 ); - this.toggleSidebarButton.remove(); - if ( this.animationPerformed ) { - this.sidebarContainer.css( "left", 0 ); - } - this.bodyContainer.removeClass( "body_portrait" ).addClass( "body_landscape" ); - } - - //portrait - else if ( this.orientation != orientation ) { - this.sidebarContainer.removeClass( "sidebar_landscape" ).addClass( "sidebar_portrait" ); - this.bodyViewNavigator.setHeaderPadding( "70px" ); + var w = $window.width(); + var h = $window.height(); + + + var orientation = (w >= h) ? 'landscape' : 'portrait'; + this.contentOverlay.removeClass( 'content_overlay_visible' ).addClass( 'content_overlay_hidden' ); + + //landscape + if ( orientation == 'landscape' && this.orientation != orientation ) { + this.sidebarContainer.removeClass( 'sidebar_portrait' ).addClass( 'sidebar_landscape' ); + this.bodyViewNavigator.setHeaderPadding( 0 ); + this.toggleSidebarButton.remove(); + if ( this.animationPerformed ) { + this.sidebarContainer.css( 'left', 0 ); + } + this.bodyContainer.removeClass( 'body_portrait' ).addClass( 'body_landscape' ); + } + + //portrait + else if ( this.orientation != orientation ) { + this.sidebarContainer.removeClass( 'sidebar_landscape' ).addClass( 'sidebar_portrait' ); + this.bodyViewNavigator.setHeaderPadding( '70px' ); this.bodyContainer.append( this.toggleSidebarButton ); - if ( this.animationPerformed ) { - this.sidebarContainer.css( "left", -this.sidebarContainer.width() ); - } - this.bodyContainer.removeClass( "body_landscape" ).addClass( "body_portrait" ); - } - - this.orientation = orientation; + if ( this.animationPerformed ) { + this.sidebarContainer.css( 'left', -this.sidebarContainer.width() ); + } + this.bodyContainer.removeClass( 'body_landscape' ).addClass( 'body_portrait' ); + } + + this.orientation = orientation; } SplitViewNavigator.prototype.showSidebar = function() { this.animationPerformed = true; - if ( this.orientation == "portrait" ) { - this.contentOverlay.removeClass( "content_overlay_hidden" ).addClass( "content_overlay_visible" ); + if ( this.orientation == 'portrait' ) { + this.contentOverlay.removeClass( 'content_overlay_hidden' ).addClass( 'content_overlay_visible' ); this.animating = true; this.sidebarContainer.animate({ left:0, avoidTransforms:false, useTranslate3d: true }, this.animationDuration, this.animationCompleteHandler()); - + } } SplitViewNavigator.prototype.hideSidebar = function() { - if ( this.orientation == "portrait" ) { - this.contentOverlay.removeClass( "content_overlay_visible" ).addClass( "content_overlay_hidden" ); + if ( this.orientation == 'portrait' ) { + this.contentOverlay.removeClass( 'content_overlay_visible' ).addClass( 'content_overlay_hidden' ); this.animating = true; this.sidebarContainer.animate({ left:-this.sidebarContainer.width(), avoidTransforms:false, useTranslate3d: true }, this.animationDuration, this.animationCompleteHandler()); - + } } @@ -143,7 +153,7 @@ SplitViewNavigator.prototype.animationCompleteHandler = function() { var self = this; return function() { self.animating = false; - //self.resetScroller(); + //self.resetScroller(); } } @@ -177,9 +187,9 @@ SplitViewNavigator.prototype.replaceBodyView = function( viewDescriptor ) { //GUID logic from http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript SplitViewNavigator.prototype.S4 = function() { - return (((1+Math.random())*0x10000)|0).toString(16).substring(1); + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); } - + SplitViewNavigator.prototype.guid = function() { - return (this.S4() + this.S4() + "-" + this.S4() + "-4" + this.S4().substr(0,3) + "-" + this.S4() + "-" + this.S4() + this.S4() + this.S4()).toLowerCase(); -} \ No newline at end of file + return (this.S4() + this.S4() + '-' + this.S4() + '-4' + this.S4().substr(0,3) + '-' + this.S4() + '-' + this.S4() + this.S4() + this.S4()).toLowerCase(); +} diff --git a/src/viewnavigator/viewnavigator.css b/src/viewnavigator/viewnavigator.css index 5eea526..eed1ce1 100644 --- a/src/viewnavigator/viewnavigator.css +++ b/src/viewnavigator/viewnavigator.css @@ -12,22 +12,22 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * { -webkit-touch-callout: none; - -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } -input,textarea { +input, +textarea { -webkit-touch-callout: auto; - -webkit-user-select: auto; -moz-user-select: auto; -ms-user-select: auto; user-select: auto; } + body { position: absolute; top: 0px; @@ -48,10 +48,10 @@ body { } .viewNavigator_root { - width: 100%; - height: 100%; - background-color: 666666; - overflow: hidden; + width:100%; + height:100%; + background-color:666666; + overflow:hidden; } .viewNavigator_header { @@ -61,16 +61,18 @@ body { overflow: hide; padding: 0px; background: rgb(167,207,223); /* Old browsers */ -background: -webkit-linear-gradient(top, rgba(167,207,223,1) 0%, rgba(35,83,138,1) 100%); + background: -moz-linear-gradient(top, rgba(167,207,223,1) 0%, rgba(35,83,138,1) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(167,207,223,1)), color-stop(100%,rgba(35,83,138,1))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); /* IE10+ */ + background: linear-gradient(top, rgba(167,207,223,1) 0%,rgba(35,83,138,1) 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a7cfdf', endColorstr='#23538a',GradientType=0 ); /* IE6-9 */ - background: -moz-linear-gradient(top, rgba(167,207,223,1) 0%, rgba(35,83,138,1) 100%); - background: -o-linear-gradient(top, rgba(167,207,223,1) 0%, rgba(35,83,138,1) 100%); - background: -ms-linear-gradient(top, rgba(167,207,223,1) 0%, rgba(35,83,138,1) 100%); - background: linear-gradient(top, rgba(167,207,223,1) 0%, rgba(35,83,138,1) 100%); /* W3C */ -filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a7cfdf', endColorstr='#23538a',GradientType=0 ); /* IE6-9 */ -backface-visibility: hidden; + backface-visibility: hidden; -webkit-backface-visibility: hidden; + transform: translate3d(0,0,0); -webkit-transform: translate3d(0,0,0); -moz-transform: translate3d(0,0,0); -o-transform: translate3d(0,0,0); @@ -114,6 +116,9 @@ backface-visibility: hidden; .viewNavigator_headerContent { backface-visibility: hidden; -webkit-backface-visibility: hidden; + + transform: translate3d(0,0,0); + -webkit-transform: translate3d(0,0,0); -webkit-transform: translate3d(0,0,0); -moz-transform: translate3d(0,0,0); @@ -140,6 +145,7 @@ backface-visibility: hidden; transform: translate3d(0,0,0); } + /* ANDROID BUG WORKAROUND */ .viewNavigator_content select { overflow: auto; @@ -148,6 +154,8 @@ backface-visibility: hidden; -webkit-backface-visibility: auto; } + + .viewNavigator_contentHolder > div:first-child { min-height: 100%; border-bottom: 1px solid #444; @@ -180,6 +188,9 @@ backface-visibility: hidden; min-width: 100%; min-height: 100% backface-visibility: hidden; -webkit-backface-visibility: hidden; + + transform: translate3d(0,0,0); + -webkit-transform: translate3d(0,0,0); -webkit-transform: translate3d(0,0,0); -moz-transform: translate3d(0,0,0); diff --git a/src/viewnavigator/viewnavigator.js b/src/viewnavigator/viewnavigator.js index 7e97548..6d8bbbc 100644 --- a/src/viewnavigator/viewnavigator.js +++ b/src/viewnavigator/viewnavigator.js @@ -10,8 +10,25 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVE ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +var ViewNavigator = function( target, options ) { -var ViewNavigator = function( target, backLinkCSS, bindToWindow ) { + var defaults = { + CSSNamespace: 'viewNavigator_', + backLinkCSS: 'viewNavigator_backButton', + bindToWindow: true, + useNoClickDelay: true + }; + + //support legacy url signature + //function( target, backLinkCSS, bindToWindow ) { + if ( typeof(options) == "string" ){ + options = { + backLinkCSS:arguments[1], + bindToWindow:arguments[2] + } + } + + this.options = options = $.extend( {}, defaults, options ); this.supportsBackKey = true; //phonegap on android only this.animating = false; @@ -20,49 +37,47 @@ var ViewNavigator = function( target, backLinkCSS, bindToWindow ) { this.history = []; this.scroller = null; this.headerPadding = 5; - + this.uniqueId = this.guid(); - - var regexp = new RegExp("Windows Phone OS 7"); + + var regexp = new RegExp('Windows Phone OS 7'); this.winPhone = (navigator.userAgent.search(regexp) >= 0); - - this.rootElement = $('
    '); - this.header = $('
    '); - this.content = $('
    '); + + this.rootElement = $('
    '); + this.header = $('
    '); + this.content = $('
    '); this.rootElement.append( this.header ); this.rootElement.append( this.content ); - + this.parent = $( target ); - - this.backLinkCSS = backLinkCSS ? backLinkCSS : "viewNavigator_backButton"; - + var self = this; //$(window).resize( function(event){ self.resizeContent() } ); //alert( this.parent.toString() ); //this.parent.resize( function(event){ self.resizeContent() } ); - - if ( bindToWindow != false ) { + + if ( options.bindToWindow != false ) { $(window).resize( function(event){ self.resizeContent() } ); } else { this.parent.resize( function(event){ self.resizeContent() } ); } - + this.parent.append( this.rootElement ); - + if ( window.viewNavigators == null || window.viewNavigators == undefined ) { window.viewNavigators = {}; } - window.viewNavigators[ this.uniqueId ] = this; + window.viewNavigators[ this.uniqueId ] = this; } ViewNavigator.prototype.replaceView = function( viewDescriptor ) { if (this.animating) return; - viewDescriptor.animation = "pushEffect" - - //this is a hack to mimic behavior of pushView, then pop off the "current" from the history + viewDescriptor.animation = 'pushEffect' + + //this is a hack to mimic behavior of pushView, then pop off the 'current' from the history this.history.push( viewDescriptor ); this.updateView( viewDescriptor ); this.history.pop(); @@ -73,7 +88,7 @@ ViewNavigator.prototype.replaceView = function( viewDescriptor ) { ViewNavigator.prototype.pushView = function( viewDescriptor ) { if (this.animating) return; - viewDescriptor.animation = "pushEffect" + viewDescriptor.animation = 'pushEffect' this.history.push( viewDescriptor ); this.updateView( viewDescriptor ); } @@ -82,268 +97,277 @@ ViewNavigator.prototype.popView = function() { if (this.animating || this.history.length <= 1 ) return; - + var currentViewDescriptor = this.history[ this.history.length-1]; if ( currentViewDescriptor.backCallback ) { currentViewDescriptor.backCallback(); } - - this.history.pop(); + + this.history.pop(); var viewDescriptor = this.history[ this.history.length-1 ]; - viewDescriptor.animation = "popEffect" + viewDescriptor.animation = 'popEffect' this.updateView( viewDescriptor ); } ViewNavigator.prototype.setHeaderPadding = function( amount ) { this.headerPadding = amount; if ( this.headerBacklink ) { - this.headerBacklink.css("left", amount); + this.headerBacklink.css('left', amount); } } ViewNavigator.prototype.updateView = function( viewDescriptor ) { - + this.animating = true; - - - - + + this.contentPendingRemove = this.contentViewHolder; this.headerContentPendingRemove = this.headerContent; - - this.headerContent = $('
    '); - - this.headerTitle = $("
    " + viewDescriptor.title + "
    "); + + this.headerContent = $('
    '); + + this.headerTitle = $('
    ' + viewDescriptor.title + '
    '); this.headerContent.append( this.headerTitle ); - + var linkGuid = this.guid(); if ( viewDescriptor.backLabel ) { - this.headerBacklink = $(''); + this.headerBacklink = $(''); this.headerContent.append( this.headerBacklink ); - + //this is for proper handling in splitviewnavigator this.setHeaderPadding( this.headerPadding ); } - + var id = this.guid(); - this.contentViewHolder = $('
    '); + this.contentViewHolder = $('
    '); this.contentViewHolder.append( viewDescriptor.view ); this.resizeContent(); - - if ( this.contentPendingRemove ){ - this.contentPendingRemove.stop() + + if ( this.contentPendingRemove ){ + this.contentPendingRemove.stop() } if ( this.headerContentPendingRemove ) { - this.headerContentPendingRemove.stop() + this.headerContentPendingRemove.stop() } this.headerContent.stop() this.contentViewHolder.stop() - - - + + + if ( this.scroller != null ) { - var scrollY = this.scroller.y; - this.scroller.destroy(); - this.scroller = null; - - if (this.contentPendingRemove) { - //console.log( scrollY ); - - //use this to mantain scroll position when scroller is destroyed - var children = $( this.contentPendingRemove.children()[0] ); - children.attr( "scrollY", scrollY ); - var originalTopMargin = children.css( "margin-top" ); - children.attr( "originalTopMargin", originalTopMargin ); - - var cssString = "translate3d(0px, "+(parseInt( scrollY ) + parseInt( originalTopMargin )).toString()+"px, 0px)"; - children.css( "-webkit-transform", cssString ); - - // children.css( "margin-top", (parseInt( scrollY ) + parseInt( originalTopMargin )).toString() + "px" ); - } - } - + var scrollY = this.scroller.y; + this.scroller.destroy(); + this.scroller = null; + + if (this.contentPendingRemove) { + //console.log( scrollY ); + + //use this to mantain scroll position when scroller is destroyed + var children = $( this.contentPendingRemove.children()[0] ); + children.attr( 'scrollY', scrollY ); + var originalTopMargin = children.css( 'margin-top' ); + children.attr( 'originalTopMargin', originalTopMargin ); + + var cssString = 'translate3d(0px, '+(parseInt( scrollY, 10 ) + parseInt( originalTopMargin, 10 )).toString()+'px, 0px)'; + children.css( '-webkit-transform', cssString ); + + // children.css( 'margin-top', (parseInt( scrollY, 10 ) + parseInt( originalTopMargin, 10 )).toString() + 'px' ); + } + } + $(this.contentPendingRemove).click(function(){ return false; }); - - - if ( viewDescriptor.animation == "popEffect" ) { - - this.contentViewHolder.css( "left", -this.contentViewHolder.width() ); - this.contentViewHolder.css( "opacity", 1 ); - this.content.prepend( this.contentViewHolder ); - - this.headerContent.css( "left", -this.animationX ); - this.headerContent.css( "opacity", 0 ); + + + if ( viewDescriptor.animation == 'popEffect' ) { + + this.contentViewHolder.css( 'left', -this.contentViewHolder.width() ); + this.contentViewHolder.css( 'opacity', 1 ); + this.content.prepend( this.contentViewHolder ); + + this.headerContent.css( 'left', -this.animationX ); + this.headerContent.css( 'opacity', 0 ); this.header.append( this.headerContent ); - - var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); - - this.contentPendingRemove.animate({ - left:this.contentViewHolder.width(), - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration*0.8); - - //remove this to change back - this.contentViewHolder.animate({ - left:0, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration/2); - - this.headerContentPendingRemove.animate({ - left:this.animationX, - opacity:0, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration, func ); - - this.headerContent.animate({ - left:0, - opacity:1, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration/2 ); - - - //using a timeout to get around inconsistent response times for webkittransitionend event - //var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); - //setTimeout( func, this.animationDuration+90 ); + + var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); + + this.contentPendingRemove.animate({ + left:this.contentViewHolder.width(), + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration*0.8); + + //remove this to change back + this.contentViewHolder.animate({ + left:0, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration/2); + + this.headerContentPendingRemove.animate({ + left:this.animationX, + opacity:0, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration, func ); + + this.headerContent.animate({ + left:0, + opacity:1, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration/2 ); + + + //using a timeout to get around inconsistent response times for webkittransitionend event + //var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); + //setTimeout( func, this.animationDuration+90 ); } else if ( this.history.length > 1 ) { - - this.contentViewHolder.css( "left", this.contentViewHolder.width() ); - this.contentViewHolder.css( "opacity", 1 ); - - this.content.append( this.contentViewHolder ); - - this.headerContent.css( "left", this.animationX ); - this.headerContent.css( "opacity", 0 ); + + this.contentViewHolder.css( 'left', this.contentViewHolder.width() ); + this.contentViewHolder.css( 'opacity', 1 ); + + this.content.append( this.contentViewHolder ); + + this.headerContent.css( 'left', this.animationX ); + this.headerContent.css( 'opacity', 0 ); this.header.append( this.headerContent ); - var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); + var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); - this.contentViewHolder.animate({ - left:0, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration*0.75); - - this.contentPendingRemove.animate({ - left:-this.contentViewHolder.width()/2, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration, func); - - this.headerContent.animate({ - left:0, - opacity:1, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration*0.75); - - this.headerContentPendingRemove.animate({ - left:-this.animationX, - opacity:0, - avoidTransforms:false, - useTranslate3d: true - }, this.animationDuration ); - - //using a timeout to get around inconsistent response times for webkittransitionend event - //var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); - //setTimeout( func, this.animationDuration+90 ); + this.contentViewHolder.animate({ + left:0, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration*0.75); + + this.contentPendingRemove.animate({ + left:-this.contentViewHolder.width()/2, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration, func); + + this.headerContent.animate({ + left:0, + opacity:1, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration*0.75); + + this.headerContentPendingRemove.animate({ + left:-this.animationX, + opacity:0, + avoidTransforms:false, + useTranslate3d: true + }, this.animationDuration ); + + //using a timeout to get around inconsistent response times for webkittransitionend event + //var func = this.animationCompleteHandler(this.contentPendingRemove, this.headerContentPendingRemove, this.headerContent, this.contentViewHolder ); + //setTimeout( func, this.animationDuration+90 ); } else { - this.contentViewHolder.css( "left", 0 ); - this.contentViewHolder.css( "opacity", 1 ); - this.content.append( this.contentViewHolder ); + this.contentViewHolder.css( 'left', 0 ); + this.contentViewHolder.css( 'opacity', 1 ); + this.content.append( this.contentViewHolder ); - this.headerContent.css( "left", 0 ); - this.headerContent.css( "opacity", 1 ); + this.headerContent.css( 'left', 0 ); + this.headerContent.css( 'opacity', 1 ); this.header.append( this.headerContent ); this.animating = false; this.resetScroller(); } - - if ( viewDescriptor.backLabel ) { - new NoClickDelay( this.headerBacklink.get()[0] ); + + if ( viewDescriptor.backLabel && this.options.useNoClickDelay ) { + new NoClickDelay( this.headerBacklink.get()[0] ); } - + if ( viewDescriptor.showCallback ) { - viewDescriptor.showCallback(); + viewDescriptor.showCallback(); } } ViewNavigator.prototype.destroyScroller = function() { - + if ( !this.winPhone ) { if ( this.scroller != null ) { this.scroller.destroy(); this.scroller = null; } - } + } } ViewNavigator.prototype.resetScroller = function() { - - var id = this.contentViewHolder.attr( "id" ); - var currentViewDescriptor = this.history[ this.history.length-1]; - this.destroyScroller(); - + + var id = this.contentViewHolder.attr( 'id' ); + var currentViewDescriptor = this.history[ this.history.length-1]; + this.destroyScroller(); + if ( !this.winPhone ) { if ( id && !(currentViewDescriptor && currentViewDescriptor.scroll == false)) { var self = this; if ( 'ontouchstart' in window ){ - setTimeout( function() { - - //use this to mantain scroll position when scroller is destroyed - var targetDiv = $( $("#"+id ).children()[0] ); - var scrollY= targetDiv.attr( "scrollY" ); - var originalTopMargin = targetDiv.attr( "originalTopMargin" ); - if ( currentViewDescriptor.maintainScrollPosition !== false && scrollY != undefined && scrollY != "" ){ - // console.log( "resetScroller scrollY: " + scrollY) - // targetDiv.css( "margin-top", originalTopMargin ); - var cssString = "translate3d(0px, "+(originalTopMargin).toString()+"px, 0px)"; - targetDiv.css( "-webkit-transform", cssString ); - } - self.scroller = new iScroll( id ); - if ( currentViewDescriptor.maintainScrollPosition !== false && scrollY != undefined && scrollY != "" ) { - self.scroller.scrollTo( 0, parseInt( scrollY ) ); - } - }, 10 ); - //this.scroller = new iScroll( id ); - } + setTimeout( function() { + + //use this to mantain scroll position when scroller is destroyed + var targetDiv = $( $('#'+id ).children()[0] ); + var scrollY= targetDiv.attr( 'scrollY' ); + var originalTopMargin = targetDiv.attr( 'originalTopMargin' ); + if ( currentViewDescriptor.maintainScrollPosition !== false && scrollY != undefined && scrollY != '' ){ + // console.log( 'resetScroller scrollY: ' + scrollY) + // targetDiv.css( 'margin-top', originalTopMargin ); + var cssString = 'translate3d(0px, '+(originalTopMargin).toString()+'px, 0px)'; + targetDiv.css( '-webkit-transform', cssString ); + } + self.scroller = new iScroll( id , { + useTransition: true, + onBeforeScrollStart: function (e) { + var target = e.target; + while ( target.nodeType != 1 ) target = target.parentNode; + + if ( target.tagName !== 'SELECT' && target.tagName !== 'INPUT' && target.tagName !== 'TEXTAREA' ) { + e.preventDefault(); + } + } + }); + + if ( currentViewDescriptor.maintainScrollPosition !== false && scrollY != undefined && scrollY != '' ) { + self.scroller.scrollTo( 0, parseInt( scrollY, 10 ) ); + } + }, 10 ); + + } else { - var target = $("#"+id ); - target.css( "overflow", "auto" ); + var target = $('#'+id ); + target.css( 'overflow', 'auto' ); } } - } + } } ViewNavigator.prototype.refreshScroller = function() { - + if ( !this.winPhone ) { if ( this.scroller != null ) { this.scroller.refresh(); } - } + } } ViewNavigator.prototype.animationCompleteHandler = function(removalTarget, headerRemovalTarget, headerView, contentView) { var self = this; return function() { self.animating = false; - self.resetScroller(); + self.resetScroller(); if ( removalTarget ) { - removalTarget.unbind( "click" ); + removalTarget.unbind( 'click' ); removalTarget.detach(); } if ( headerRemovalTarget ) { - headerRemovalTarget.unbind( "click" ); - headerRemovalTarget.detach(); + headerRemovalTarget.unbind( 'click' ); + headerRemovalTarget.detach(); } } } @@ -361,11 +385,11 @@ ViewNavigator.prototype.resizeContent = function(event) { //GUID logic from http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript ViewNavigator.prototype.S4 = function() { - return (((1+Math.random())*0x10000)|0).toString(16).substring(1); + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); } - + ViewNavigator.prototype.guid = function() { - return (this.S4() + this.S4() + "-" + this.S4() + "-4" + this.S4().substr(0,3) + "-" + this.S4() + "-" + this.S4() + this.S4() + this.S4()).toLowerCase(); + return (this.S4() + this.S4() + '-' + this.S4() + '-4' + this.S4().substr(0,3) + '-' + this.S4() + '-' + this.S4() + this.S4() + this.S4()).toLowerCase(); } @@ -373,12 +397,12 @@ ViewNavigator.prototype.guid = function() { /* PHONEGAP INTEGRATION */ /* //android+phonegap specific back button support - will only work if phonegap is used on android (www.phonegap.com) -if ( typeof PhoneGap != 'undefined' ) { - document.addEventListener("deviceready", onDeviceReady, false); +if ( typeof PhoneGap != 'undefined' ) { + document.addEventListener('deviceready', onDeviceReady, false); } function onDeviceReady() { - document.addEventListener("backbutton", onBackKey, false); + document.addEventListener('backbutton', onBackKey, false); } function onBackKey( event ) { @@ -389,12 +413,10 @@ function onBackKey( event ) { } } */ - + //block page scrolling if (!document.addEventListener) document.attachEvent('touchmove', function (e) { e.preventDefault(); }); else document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false); - -