Skip to content

Commit 9c0a650

Browse files
committed
Add the WebView
1 parent feb5194 commit 9c0a650

File tree

3 files changed

+357
-0
lines changed

3 files changed

+357
-0
lines changed
Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
/****************************************************************************
2+
Copyright (c) 2013-2014 Chukong Technologies Inc.
3+
4+
http://www.cocos2d-x.org
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.
23+
****************************************************************************/
24+
25+
ccui.WebView = ccui.Widget.extend({
26+
27+
ctor: function(path){
28+
ccui.Widget.prototype.ctor.call(this);
29+
if(path)
30+
this.loadURL(path);
31+
this._EventList = {};
32+
},
33+
34+
35+
setJavascriptInterfaceScheme: function(scheme){},
36+
loadData: function(data, MIMEType, encoding, baseURL){},
37+
loadHTMLString: function(string, baseURL){},
38+
39+
40+
/**
41+
* Load an URL
42+
* @param {String} url
43+
*/
44+
loadURL: function(url){
45+
this._renderCmd.updateURL(url);
46+
cc.eventManager.dispatchCustomEvent(ccui.WebView.EventType.LOADING);
47+
},
48+
49+
/**
50+
* Stop loading
51+
*/
52+
stopLoading: function(){
53+
cc.log("Web does not support loading");
54+
},
55+
reload: function(){
56+
var iframe = this._renderCmd._iframe;
57+
if(iframe){
58+
var win = iframe.contentWindow;
59+
if(win && win.location)
60+
win.location.reload();
61+
}
62+
},
63+
64+
/**
65+
* Determine whether to go back
66+
*/
67+
canGoBack: function(){
68+
cc.log("Web does not support query history");
69+
},
70+
71+
/**
72+
* Determine whether to go forward
73+
*/
74+
canGoForward: function(){
75+
cc.log("Web does not support query history");
76+
},
77+
78+
/**
79+
* go back
80+
*/
81+
goBack: function(){
82+
var iframe = this._renderCmd._iframe;
83+
if(iframe){
84+
var win = iframe.contentWindow;
85+
if(win && win.location)
86+
win.history.back();
87+
}
88+
},
89+
90+
/**
91+
* go forward
92+
*/
93+
goForward: function(){
94+
var iframe = this._renderCmd._iframe;
95+
if(iframe){
96+
var win = iframe.contentWindow;
97+
if(win && win.location)
98+
win.history.forward();
99+
}
100+
},
101+
102+
/**
103+
* In the webview execution within a period of js string
104+
* @param str
105+
*/
106+
evaluateJS: function(str){
107+
var iframe = this._renderCmd._iframe;
108+
if(iframe){
109+
var win = iframe.contentWindow;
110+
try{
111+
win.eval(str);
112+
}catch(err){
113+
console.error(err);
114+
}
115+
}
116+
},
117+
118+
/**
119+
* Limited scale
120+
*/
121+
setScalesPageToFit: function(){
122+
cc.log("Web does not support zoom");
123+
},
124+
125+
/**
126+
* The binding event
127+
* @param event string: load | loading | error Or: ccui.WebView.EventType
128+
* @param callback
129+
*/
130+
addEventListener: function(event, callback){
131+
if(!/^ui_webview_/.test(event))
132+
event = "ui_webview_" + event;
133+
return cc.eventManager.addCustomListener(event, callback);
134+
},
135+
136+
/**
137+
* Delete events
138+
* @param event
139+
* @param callbackOrListener
140+
*/
141+
removeEventListener: function(event, callbackOrListener){
142+
var map, list;
143+
if(!/^ui_webview_/.test(event))
144+
event = "ui_webview_" + event;
145+
if(typeof callbackOrListener === "function"){
146+
map = cc.eventManager._listenersMap[event];
147+
if(map){
148+
list = map.getFixedPriorityListeners();
149+
list && cc.eventManager._removeListenerInCallback(list, callbackOrListener);
150+
}
151+
}else{
152+
map = cc.eventManager._listenersMap[event];
153+
if(map){
154+
list = map.getFixedPriorityListeners();
155+
list && cc.eventManager._removeListenerInVector(list, callbackOrListener);
156+
}
157+
}
158+
},
159+
160+
//setOnShouldStartLoading: function(callback){},
161+
//setOnDidFinishLoading: function(){},
162+
//setOnDidFailLoading: function(){},
163+
//setOnJSCallback: function(){},
164+
165+
//getOnShouldStartLoading: function(){},
166+
//getOnDidFinishLoading: function(){},
167+
//getOnDidFailLoading: function(){},
168+
//getOnJSCallback: function(){},
169+
170+
_createRenderCmd: function(){
171+
return new ccui.WebView.RenderCmd(this);
172+
},
173+
174+
setContentSize: function(w, h){
175+
ccui.Widget.prototype.setContentSize.call(this, w, h);
176+
if(h === undefined){
177+
h = w.height;
178+
w = w.width;
179+
}
180+
this._renderCmd.changeSize(w, h);
181+
},
182+
183+
cleanup: function(){
184+
this._renderCmd.removeDom();
185+
this.stopAllActions();
186+
this.unscheduleAllCallbacks();
187+
}
188+
});
189+
190+
ccui.WebView.EventType = {
191+
LOADING: "ui_webview_loading",
192+
LOADED: "ui_webview_load",
193+
ERROR: "ui_webview_error"
194+
};
195+
196+
(function(){
197+
198+
ccui.WebView.polyfill = {
199+
devicePixelRatio: false,
200+
enableDiv: false
201+
};
202+
203+
if(cc.sys.os === cc.sys.OS_IOS)
204+
ccui.WebView.polyfill.enableDiv = true;
205+
206+
})();
207+
208+
(function(polyfill){
209+
210+
ccui.WebView.RenderCmd = function(node){
211+
cc.Node.CanvasRenderCmd.call(this, node);
212+
213+
this._div = null;
214+
this._iframe = null;
215+
216+
if(polyfill.enableDiv){
217+
this._div = document.createElement("div");
218+
this._div.style["-webkit-overflow"] = "auto";
219+
this._div.style["-webkit-overflow-scrolling"] = "touch";
220+
this._iframe = document.createElement("iframe");
221+
this._div.appendChild(this._iframe);
222+
}else{
223+
this._div = this._iframe = document.createElement("iframe");
224+
}
225+
this._iframe.addEventListener("load", function(){
226+
cc.eventManager.dispatchCustomEvent(ccui.WebView.EventType.LOADED);
227+
});
228+
this._iframe.addEventListener("error", function(){
229+
cc.eventManager.dispatchCustomEvent(ccui.WebView.EventType.ERROR);
230+
});
231+
this._div.style["background-color"] = "#FFF";
232+
this._div.style.height = "200px";
233+
this._div.style.width = "300px";
234+
this._div.style.overflow = "scroll";
235+
this._listener = null;
236+
this.initStyle();
237+
};
238+
239+
var proto = ccui.WebView.RenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype);
240+
proto.constructor = ccui.WebView.RenderCmd;
241+
242+
proto.updateStatus = function(){
243+
polyfill.devicePixelRatio = cc.view.isRetinaEnabled();
244+
var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag;
245+
if(locFlag & flags.transformDirty){
246+
//update the transform
247+
this.transform(this.getParentRenderCmd(), true);
248+
this.updateMatrix(this._worldTransform, cc.view._scaleX, cc.view._scaleY);
249+
this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag;
250+
}
251+
};
252+
253+
proto.visit = function(){
254+
var self = this,
255+
container = cc.container,
256+
eventManager = cc.eventManager;
257+
if(this._node._visible){
258+
container.appendChild(this._div);
259+
if(this._listener === null)
260+
this._listener = eventManager.addCustomListener(cc.game.EVENT_RESIZE, function () {
261+
self.resize();
262+
});
263+
}else{
264+
var hasChild = false;
265+
if('contains' in container) {
266+
hasChild = container.contains(this._div);
267+
}else {
268+
hasChild = container.compareDocumentPosition(this._div) % 16;
269+
}
270+
if(hasChild)
271+
container.removeChild(this._div);
272+
var list = eventManager._listenersMap[cc.game.EVENT_RESIZE].getFixedPriorityListeners();
273+
eventManager._removeListenerInVector(list, this._listener);
274+
this._listener = null;
275+
}
276+
this.updateStatus();
277+
this.resize(cc.view);
278+
};
279+
280+
proto.resize = function(view){
281+
view = view || cc.view;
282+
var node = this._node,
283+
eventManager = cc.eventManager;
284+
if(node._parent && node._visible)
285+
this.updateMatrix(this._worldTransform, view._scaleX, view._scaleY);
286+
else{
287+
var list = eventManager._listenersMap[cc.game.EVENT_RESIZE].getFixedPriorityListeners();
288+
eventManager._removeListenerInVector(list, this._listener);
289+
this._listener = null;
290+
}
291+
};
292+
293+
proto.updateMatrix = function(t, scaleX, scaleY){
294+
var node = this._node;
295+
if(polyfill.devicePixelRatio && scaleX !== 1 && scaleX !== 1){
296+
var dpr = window.devicePixelRatio;
297+
scaleX = scaleX / dpr;
298+
scaleY = scaleY / dpr;
299+
}
300+
if(this._loaded === false) return;
301+
var cw = node._contentSize.width,
302+
ch = node._contentSize.height;
303+
var a = t.a * scaleX,
304+
b = t.b,
305+
c = t.c,
306+
d = t.d * scaleY,
307+
tx = t.tx*scaleX - cw/2 + cw*node._scaleX/2*scaleX,
308+
ty = t.ty*scaleY - ch/2 + ch*node._scaleY/2*scaleY;
309+
var matrix = "matrix(" + a + "," + b + "," + c + "," + d + "," + tx + "," + -ty + ")";
310+
this._div.style["transform"] = matrix;
311+
this._div.style["-webkit-transform"] = matrix;
312+
};
313+
314+
proto.initStyle = function(){
315+
if(!this._div) return;
316+
var div = this._div;
317+
div.style.position = "absolute";
318+
div.style.bottom = "0px";
319+
div.style.left = "0px";
320+
};
321+
322+
proto.updateURL = function(url){
323+
var iframe = this._iframe;
324+
iframe.src = url;
325+
var self = this;
326+
var cb = function(){
327+
self._loaded = true;
328+
iframe.removeEventListener("load", cb);
329+
};
330+
iframe.addEventListener("load", cb);
331+
};
332+
333+
proto.changeSize = function(w, h){
334+
var div = this._div;
335+
if(div){
336+
div.style["width"] = w+"px";
337+
div.style["height"] = h+"px";
338+
}
339+
};
340+
341+
proto.removeDom = function(){
342+
var div = this._div;
343+
if(div){
344+
var hasChild = false;
345+
if('contains' in cc.container) {
346+
hasChild = cc.container.contains(div);
347+
}else {
348+
hasChild = cc.container.compareDocumentPosition(div) % 16;
349+
}
350+
if(hasChild)
351+
cc.container.removeChild(div);
352+
}
353+
};
354+
355+
})(ccui.WebView.polyfill);

moduleConfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@
323323
"extensions/ccui/uiwidgets/UITextBMFont.js",
324324
"extensions/ccui/uiwidgets/UITextField.js",
325325
"extensions/ccui/uiwidgets/UIRichText.js",
326+
"extensions/ccui/uiwidgets/UIWebView.js",
326327
"extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js",
327328
"extensions/ccui/uiwidgets/scroll-widget/UIListView.js",
328329
"extensions/ccui/uiwidgets/scroll-widget/UIPageView.js"

tools/build.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
<file name="extensions/ccui/uiwidgets/UITextAtlas.js"/>
225225
<file name="extensions/ccui/uiwidgets/UITextBMFont.js"/>
226226
<file name="extensions/ccui/uiwidgets/UITextField.js"/>
227+
<file name="extensions/ccui/uiwidgets/UIWebView.js"/>
227228
<file name="extensions/ccui/uiwidgets/UIRichText.js"/>
228229
<file name="extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js"/>
229230
<file name="extensions/ccui/uiwidgets/scroll-widget/UIListView.js"/>

0 commit comments

Comments
 (0)