<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<script type="text/javascript" charset="utf-8" src="js/myphonegap.js"></script>
</head>
<body>
<p>PHONEGAP SIMULATE !</p>
</body>
</html>
MainActivity.java 源碼
package com.example.mobilephonebills;
import com.example.telephonefare.R;
import com.example.telephonefare.R.layout;
import com.example.telephonefare.R.menu;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
public class ChartActivity extends Activity {
WebView mWebView = null;
LinearLayout mMenuItem1,mMenuItem2,mMenuItem3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chart);
prepareView();
}
private void prepareView(){
mMenuItem1 = (LinearLayout)findViewById(R.id.home_menu_bt1);
mMenuItem2 = (LinearLayout)findViewById(R.id.home_menu_bt2);
mMenuItem3 = (LinearLayout)findViewById(R.id.home_menu_bt3);
OnClickListener listener2= new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.home_menu_bt1:
break;
case R.id.home_menu_bt2:
Intent intent3 = new Intent(ChartActivity.this,ChartActivity.class);
ChartActivity.this.startActivity(intent3);
break;
case R.id.home_menu_bt3:
break;
default:
break;
}
}
};
mMenuItem1.setOnClickListener(listener2);
mMenuItem2.setOnClickListener(listener2);
mMenuItem3.setOnClickListener(listener2);
mWebView = (WebView)findViewById(R.id.charts_webView);
mWebView.setBackgroundColor(0);
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.setBackgroundColor(0);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
mWebView.loadUrl("file:///android_asset/www/chart.html");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.chart, menu);
return true;
}
private class MyWebViewClient extends WebViewClient{
// 在WebView中而不是默認瀏覽器中顯示頁面
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO Auto-generated method stub
if (Uri.parse(url).getHost().equals("file:///android_asset/www/chart.html")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
view.loadUrl(url);
return true;
}
}
}
myphonegap.js 源碼
;(function(){
var require,//myphonegap內部的工具函數,用來導入相關的模塊
define;//在myphonegap註冊相關的模塊
//通過一個立即調用的匿名函數,來給require和define賦上實際的函數
(function(){
var modules={}; // 模塊數組,添加模塊類似給這個對象添加了屬性,模塊名爲屬性名,模塊對象爲屬性值,或者說是鍵值對
build = function(module){ //根據模塊對象構造模塊導出對象,模塊導出對象存儲在modules這個對象數組內
var factory = module.factory;
module.exports = {}; //給當前模塊加入了一個exports屬性
delete module.factory; //刪除了module的屬性
factory(require,module.exports,module); //構建導出模塊,module.exports是傳出參數(實參,引用傳遞)
return module.exports;
}
require = function(id){ //根據模塊名稱/id請求模塊對象,如果是第一次請求,就構建對象
if(!modules[id]){
throw "module " + id + " not found!";
}
return modules[id].factory?build(modules[id]):modules[id].exports;
}
define = function(id,factory){ //定義模塊,模塊名稱、構建模塊對象的工廠方法。
if(modules[id]){
throw "module " + id + " is exist!";
}
modules[id] = { //定義模塊對象,左邊的值爲屬性名,右邊的值爲傳入的參數
id:id,
factory:factory
};
}
})();
//註冊myphonegap模塊
define("myphonegap", function(require, exports, module){
console.info("create myphonegap module");
var channel = require('myphonegap/channel');
document.addEventListener('DOMContentLoaded', function() {
channel.onDOMContentLoaded.fire();
}, false);
if (document.readyState == 'complete' || document.readyState == 'interactive') {
channel.onDOMContentLoaded.fire();
}
var m_document_addEventListener = document.addEventListener;
var m_document_removeEventListener = document.removeEventListener;
var m_window_addEventListener = window.addEventListener;
var m_window_removeEventListener = window.removeEventListener;
var documentEventHandlers = {},
windowEventHandlers = {};
document.addEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
if (typeof documentEventHandlers[e] != 'undefined') {
documentEventHandlers[e].subscribe(handler);
} else {
m_document_addEventListener.call(document, evt, handler, capture);
}
};
window.addEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
if (typeof windowEventHandlers[e] != 'undefined') {
windowEventHandlers[e].subscribe(handler);
} else {
m_window_addEventListener.call(window, evt, handler, capture);
}
};
document.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
// If unsubscribing from an event that is handled by a plugin
if (typeof documentEventHandlers[e] != "undefined") {
documentEventHandlers[e].unsubscribe(handler);
} else {
m_document_removeEventListener.call(document, evt, handler, capture);
}
};
window.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
// If unsubscribing from an event that is handled by a plugin
if (typeof windowEventHandlers[e] != "undefined") {
windowEventHandlers[e].unsubscribe(handler);
} else {
m_window_removeEventListener.call(window, evt, handler, capture);
}
};
function createEvent(type, data) {
var event = document.createEvent('Events');
event.initEvent(type, false, false);
if (data) {
for (var i in data) {
if (data.hasOwnProperty(i)) {
event[i] = data[i];
}
}
}
return event;
}
if(typeof window.console === "undefined") {
window.console = {
log:function(){}
};
}
var myphonegap = {
define:define,
require:require,
/**
*在document和window上添加自定義的事件監聽器方法
* Methods to add/remove your own addEventListener hijacking on document + window.
*/
addWindowEventHandler:function(event) {
return (windowEventHandlers[event] = channel.create(event));
},
addStickyDocumentEventHandler:function(event) {
return (documentEventHandlers[event] = channel.createSticky(event));
},
addDocumentEventHandler:function(event) {
return (documentEventHandlers[event] = channel.create(event));
},
removeWindowEventHandler:function(event) {
delete windowEventHandlers[event];
},
removeDocumentEventHandler:function(event) {
delete documentEventHandlers[event];
},
/**
* 以對象形式返回DOM中原來定義的事件偵聽函數
* @return object
*/
getOriginalHandlers: function() {
return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
},
/**
*從本地代碼觸發事件
*/
fireDocumentEvent: function(type, data, bNoDetach) {
var evt = createEvent(type, data);
if (typeof documentEventHandlers[type] != 'undefined') {
if( bNoDetach ) {
documentEventHandlers[type].fire(evt);
}
else {
setTimeout(function() {
documentEventHandlers[type].fire(evt);
}, 0);
}
} else {
document.dispatchEvent(evt);
}
},
fireWindowEvent: function(type, data) {
var evt = createEvent(type,data);
if (typeof windowEventHandlers[type] != 'undefined') {
setTimeout(function() {
windowEventHandlers[type].fire(evt);
}, 0);
} else {
window.dispatchEvent(evt);
}
},
/**
* 插件回調機制
* 回調ID採用隨機數避免加載刷新後發生衝突,這樣避免新的回調函數取得和舊回調函數同樣的ID
*/
callbackId: Math.floor(Math.random() * 2000000000),
callbacks: {},
callbackStatus: {
NO_RESULT: 0,
OK: 1,
CLASS_NOT_FOUND_EXCEPTION: 2,
ILLEGAL_ACCESS_EXCEPTION: 3,
INSTANTIATION_EXCEPTION: 4,
MALFORMED_URL_EXCEPTION: 5,
IO_EXCEPTION: 6,
INVALID_ACTION: 7,
JSON_EXCEPTION: 8,
ERROR: 9
},
/**
* 本地方法返回成功時調用該函數
*/
callbackSuccess: function(callbackId, args) {
try {
myphonegap.callbackFromNative(callbackId, true, args.status, args.message, args.keepCallback);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = "+e);
}
},
/**
* 本地方法返回失敗時調用該函數
*/
callbackError: function(callbackId, args) {
try {
myphonegap.callbackFromNative(callbackId, false, args.status, args.message, args.keepCallback);
} catch (e) {
console.log("Error in error callback: " + callbackId + " = "+e);
}
},
/**
* 從本地動作返回一個結果時調用該函數
*/
callbackFromNative: function(callbackId, success, status, message, keepCallback) {
var callback = myphonegap.callbacks[callbackId];
if (callback) {
if (success && status == myphonegap.callbackStatus.OK) {
callback.success && callback.success(message);
} else if (!success) {
callback.fail && callback.fail(message);
}
// Clear callback if not expecting any more results
if (!keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
},
addConstructor: function(func) {
channel.onMyphonegapReady.subscribe(function() {
try {
func();
} catch(e) {
console.log("Failed to run constructor: " + e);
}
});
},
Hello:function(name){
console.info("hello, "+name +" !");
}
};
module.exports = myphonegap;
});
//註冊myphonegap/builder模塊
define("myphonegap/builder", function(require, exports, module) {
var utils = require('myphonegap/utils');
function each(objects, func, context) {
for (var prop in objects) {
if (objects.hasOwnProperty(prop)) {
//console.info(prop);
func.apply(context, [objects[prop], prop]);
}
}
}
function clobber(obj, key, value) {
obj[key] = value;
// Getters can only be overridden by getters.
if (obj[key] !== value) {
utils.defineGetter(obj, key, function() {
return value;
});
}
}
function assignOrWrapInDeprecateGetter(obj, key, value, message) {
if (message) {
utils.defineGetter(obj, key, function() {
console.log(message);
delete obj[key];
clobber(obj, key, value);
return value;
});
} else {
clobber(obj, key, value);
}
}
function include(parent, objects, clobber, merge) {
each(objects, function (obj, key) {
try {
var result = obj.path ? require(obj.path) : {};
if (clobber) {
// Clobber if it doesn't exist.
if (typeof parent[key] === 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else if (typeof obj.path !== 'undefined') {
// If merging, merge properties onto parent, otherwise, clobber.
if (merge) {
recursiveMerge(parent[key], result);
} else {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
}
}
result = parent[key];
} else {
// Overwrite if not currently defined.
if (typeof parent[key] == 'undefined') {
assignOrWrapInDeprecateGetter(parent, key, result, obj.deprecated);
} else {
// Set result to what already exists, so we can build children into it if they exist.
result = parent[key];
}
}
if (obj.children) {
include(result, obj.children, clobber, merge);
}
} catch(e) {
utils.alert('Exception building myphonegap JS globals: ' + e + ' for key "' + key + '"');
}
});
}
function recursiveMerge(target, src) {
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
if (target.prototype && target.prototype.constructor === target) {
// If the target object is a constructor override off prototype.
clobber(target.prototype, prop, src[prop]);
} else {
if (typeof src[prop] === 'object' && typeof target[prop] === 'object') {
recursiveMerge(target[prop], src[prop]);
} else {
clobber(target, prop, src[prop]);
}
}
}
}
}
module.exports = {
buildIntoButDoNotClobber: function(objects, target) {
include(target, objects, false, false);
},
buildIntoAndClobber: function(objects, target) {
include(target, objects, true, false);
},
buildIntoAndMerge: function(objects, target) {
include(target, objects, true, true);
},
recursiveMerge: recursiveMerge,
assignOrWrapInDeprecateGetter: assignOrWrapInDeprecateGetter
};
});
define("myphonegap/channel",function(require,exports,module){
var utils = require("myphonegap/utils"),
nextGuid = 1;
//典型的創建對象方法:通過構造器初始化變量,從而讓各個實例相互獨立;之後通過修改函數原型共享實例方法。
var Channel = function(type,sticky){
this.type = type;
//map of guid -> function
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky?1:0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;
}, channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceReadyChannelsArray: [],
deviceReadyChannelsMap: {},
waitForInitialization: function(feature) {
if (feature) {
var c = channel[feature] || this.createSticky(feature);
this.deviceReadyChannelsMap[feature] = c;
this.deviceReadyChannelsArray.push(c);
}
},
initializationComplete: function(feature) {
var c = this.deviceReadyChannelsMap[feature];
if (c) {
c.fire();
}
},
join: function (h, c) {//join也就是將一組Channel連接起來,並注入一個在所有Channel上只執行一次的公共處理函數
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribe(f):i--;
}
if (!i) h();
}
};
function forceFunction(f){
if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
}
//給對象Channel的原型對象添加訂閱函數,參數:函數對象、上下文、全局唯一ID
Channel.prototype.subscribe = function(f,c,g){
forceFunction(f);//確保f爲函數
if(this.state==2){ //apply方法能劫持另外一個對象的方法,繼承另外一個對象的屬性;f裏面的指針爲c(Channel的實例)
f.apply(c||this,this.fireArgs);
}
var func = f,
guid = f.observer_guid;
if(typeof f == "object"){ func = utils.close(c,f);}
if(!guid){
guid = ''+ nextGuid++;
}
f.observer_guid = guid;
func.observer_guid = guid;
//防止重複添加
if(!this.handlers[guid]){
this.handlers[guid] = func;
this.numHandlers++;
if(this.numHandlers===1){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
Channel.prototype.unsubscribe = function(f){
forceFunction(f);
var guid = f.observer_guid;
if(this.handlers[guid]){
delete this.handlers[guid];
this.numHandlers--;
if(numHandlers===0){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
//訂閱一次
Channel.prototype.subscribeOnce = function(f,c,g){
};
//調用了在通道訂閱的所有函數
Channel.prototype.fire = function(e){
//console.info('fire start:type/'+this.type + ' numHandlers/' +this.numHandlers);
var fail = false,
fireArgs = Array.prototype.slice.call(arguments);
// Apply stickiness.
if (this.state == 1) {
this.state = 2;
this.fireArgs = fireArgs;
}
if (this.numHandlers) {
// Copy the values first so that it is safe to modify it from within
// callbacks.
var toCall = [];
for (var item in this.handlers) {
toCall.push(this.handlers[item]);
}
for (var i = 0; i < toCall.length; ++i) {
toCall[i].apply(this, fireArgs);
// console.info(this.type+' enter func fire ');
}
if (this.state == 2 && this.numHandlers) {
this.numHandlers = 0;
this.handlers = {};
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};
channel.create('onDOMContentLoaded');
channel.create('onNativeReady');
channel.create('onDeviceReady');
channel.waitForInitialization('onMyphonegapReady');
channel.waitForInitialization('onMyphoneConnectionReady');
module.exports = channel;
//console.info('define myphonegap/channel completed');
});
//註冊myphonegap/common模塊
//配置對象,將公共模塊組織起來
define("myphonegap/common",function(require,exports,module){
module.exports = {
defaults: {
myphonegap: {
path: 'myphonegap',
children: {
exec: {
path: 'myphonegap/exec'
}
}
},
Myphonegap: {
children: {
exec: {
path: 'myphonegap/exec'
}
}
}
},
clobbers: {
navigator: {
children: {
connection: {
path: 'myphonegap/plugin/network'
}
}
}
}
};
});
define("myphonegap/exec", function(require, exports, module) {
/**
* 執行cordova命令
* 同步:返回一個JSON字符串;異步:返回空字符串"",這個時候可以根據處理結果調用回調函數
* 參數:(1)success:命令執行成功回調函數
* (2)fail:命令執行失敗回調函數
* (3)service:使用的服務名稱
* (4)action:在cordova中運行的命令
* (5)args:0或多個參數組成的數組
*/
var myphonegap = require('myphonegap');
module.exports = function(success, fail, service, action, args) {
try {
var callbackId = service + myphonegap.callbackId++;//內部回調id
if (success || fail) {//至少傳入了其中一個
myphonegap.callbacks[callbackId] = {success:success, fail:fail};
}
//將參數轉化爲JSON字符串去執行
var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, action, callbackId, true]));
//alert(r);
if (r.length > 0) {
//console.info(r);
var v = JSON.parse(r);//將返回結果解析成對象
//console.info(v);
//console.info(v.status);
//console.info( myphonegap.callbackStatus.OK);
if (v.status === myphonegap.callbackStatus.OK) {
if (success) {//調用成功回調函數
try {
success(v.message);
} catch (e) {
console.log("Error in success callback: " + callbackId + " = " + e);
}
// 清除回調函數
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
return v.message;
}
else if (v.status === myphonegap.callbackStatus.NO_RESULT) {
// 清除回調函數
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
else {// 錯誤
console.log("Error: Status="+v.status+" Message="+v.message);
// 調用失敗回調函數
if (fail) {
try {
fail(v.message);
}
catch (e1) {
console.log("Error in error callback: "+callbackId+" = "+e1);
}
//清除回調函數
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
return null;
}
}
} catch (e2) {
console.log("Error: "+e2);
}
};
});
//註冊myphonegap/platform模塊
define("myphonegap/platform", function(require, exports, module){
module.exports = {
id: "android",
initialize:function() {
},
clobbers: { //需要覆蓋的屬性
navigator: {
children: {
app:{
path: "myphonegap/plugin/android/app"
}
}
},
//省略
open: {
path: "myphonegap/plugin/InAppBrowser"
}
},
merges: { //需要合併的屬性
device: {
path: 'myphonegap/plugin/android/device'
},
navigator: {
children: {
notification: {
path: 'myphonegap/plugin/android/notification'
}
}
}
}
}
});
// 這裏省略了其它插件的註冊
//註冊myphonegap/utils模塊
define("myphonegap/utils", function(require, exports, module){
var utils = exports;
utils.defineGetterSetter = function(obj, key, getFunc, opt_setFunc) {
if (Object.defineProperty) {
var desc = {
get: getFunc,
configurable: true
};
if (opt_setFunc) {
desc.set = opt_setFunc;
}
Object.defineProperty(obj, key, desc);
} else {
obj.__defineGetter__(key, getFunc);
if (opt_setFunc) {
obj.__defineSetter__(key, opt_setFunc);
}
}
};
utils.defineGetter = utils.defineGetterSetter;
utils.alert = function(msg) {
if (window.alert) {
window.alert(msg);
} else if (console && console.log) {
console.log(msg);
}
};
});
(function (context) {
// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
// We replace it so that properties that can't be clobbered can instead be overridden.
if (context.navigator) {
var CordovaNavigator = function() {};
CordovaNavigator.prototype = context.navigator;
context.navigator = new CordovaNavigator();
}
var channel = require("myphonegap/channel"),
_self = {
boot: function () {
/**
* Create all cordova objects once page has fully loaded and native side is ready.
*/
channel.join(function() {
var builder = require('myphonegap/builder'),
base = require('myphonegap/common'),
platform = require('myphonegap/platform');
// Drop the common globals into the window object, but be nice and don't overwrite anything.
builder.buildIntoButDoNotClobber(base.defaults, context);
builder.buildIntoAndClobber(base.clobbers, context);
builder.buildIntoAndMerge(base.merges, context);
builder.buildIntoButDoNotClobber(platform.defaults, context);
builder.buildIntoAndClobber(platform.clobbers, context);
builder.buildIntoAndMerge(platform.merges, context);
// Call the platform-specific initialization
platform.initialize();
// Fire event to notify that all objects are created
channel.onCordovaReady.fire();
// Fire onDeviceReady event once all constructors have run and
// myphonegap info has been received from native side.
channel.join(function() {
require('myphonegap').fireDocumentEvent('deviceready');
}, channel.deviceReadyChannelsArray);
}, [ channel.onNativeReady,channel.onDOMContentLoaded ]);
}
};
// boot up once native side is ready
channel.onNativeReady.subscribe(_self.boot);
// _nativeReady is global variable that the native side can set
// to signify that the native code is ready. It is a global since
// it may be called before any cordova JS is ready.
if (window._nativeReady) {
channel.onNativeReady.fire();
}
}(window));
//所有模塊註冊完之後,再導入cordova至全局環境中
window.myphonegap = require('myphonegap');
window.myphonegap.Hello("wen");
//測試channel模塊
/*
var channel = require("myphonegap/channel");
channel.onNativeReady.subscribe(function(){
console.info("onNativeReady");
}
);
console.info("before native ready");
channel.onNativeReady.fire();
console.info("after native ready");
*/
/*
//測試common模塊與builder模塊
var builder = require("myphonegap/builder");
var common = require("myphonegap/common");
builder.buildIntoButDoNotClobber( common.defaults,window);
window.myphonegap.Hello("Jack");
window.myphonegap.exec("myfunc");
*/
//測試初始化函數
//測試exec模塊
var exec = require("myphonegap/exec");
exec(function(ret){console.info("plus exec success!");console.info("15+15=" + ret)},
function(){console.info("multiply exec failed!")}, 'Math', 'plus',15);
exec(function(ret){console.info("multiply exec success!");console.info("15*15=" + ret)},
function(){console.info("multiply exec failed!")}, 'Math', 'multiply',15);
})();