panlippt
Version:
运行在浏览器中的ppt 演示框架
731 lines (626 loc) • 22.3 kB
JavaScript
;(function(global, DOC, undefined) {
var VERSION = '0.2',
curScriptNode = (function(scripts, node) {
scripts = DOC.getElementsByTagName('script');
node = scripts[scripts.length - 1]; //FF下可以使用DOC.currentScript
return node;
})(),
isDebug = !! curScriptNode.getAttribute('debug'),
MixJSName = curScriptNode.getAttribute('name') || 'MixJS',
CHARSET = curScriptNode.getAttribute('charset') || 'utf-8',
//获取当前文件父路径
PATH = (function(node) {
var url = node.hasAttribute ? // non-IE6/7
node.src :
// see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
node.getAttribute('src', 4);
return url.substr(0, url.lastIndexOf('/')) + '/';
})(curScriptNode),
HEAD = DOC.head || DOC.getElementsByTagName('head')[0] || DOC.documentElement,
BASEELEMENT = HEAD.getElementsByTagName('base')[0] || null,
UA = navigator.userAgent,
isWebKit = ~UA.indexOf('AppleWebKit'),
reg = /[^, ]+/g,
_cleanObj = {},
_emptyArr = [],
_emptyFn = function() {},
_arrSlice = _emptyArr.slice,
/**
* 数组遍历
* @param {[type]} arr [description]
* @param {Function} callback [description] arrvalue index arr
* @param {[type]} scope [description]
* @return {[type]} [description]
*/
each = [].forEach ?
function(arr, callback, scope) {
[].forEach.call(arr, callback, scope);
} : function(arr, callback, scope) {
for(var i = 0, len = arr.length; i < len; i++) {
if(i in arr) {
callback.call(scope, arr[i], i, arr);
}
}
};
var config = {
path: PATH,
perload: _emptyArr,
//预先加载库
debug: isDebug,
charset: CHARSET
},
alias = {},
regAlias = /^[-\w\d_$]{2,}$/i,
_moduleDepsMap = {},
_filesMap = {},
//1:加载之前,2:加载中,3:加载完成
_modulesMap = {}; //1:定义之前 2:等待依赖关系中 3:定义完成
var $ = {
VERSION: VERSION,
path:PATH,
/**
* 别名机制
* @param {String} name 名字
* @param {String} realpath 别名真实url
* @return {[type]} [description]
*/
alias: function(name,arr){
if(regAlias.test(name)){
if($.isUndefined(arr)){
return alias[name];
}else{
arr = String(arr);
alias[name] = arr;
return this;
}
}else{
throw new Error('MixJS.alias name 格式错误');
}
return this;
},
use: function(names, callback) {
names = dealArr(names);
if(names.length === 0) {
$.isFunction(callback) && callback();
return this;
}
var temp = [], self = this;
each(names, function(v) {
var arr = getPath(v),
ext = arr[1],
moduleId = arr[2],
url = arr[0];
if(!defined(moduleId) || (ext==='css' && _filesMap[url]===3)) {
temp.push(moduleId);
var cb = function() {
//_modulesMap[v] = 3;
_filesMap[url] = 3;
temp.pop();
if(temp.length === 0) {
var t = function() {
if(defined(moduleId) || ext==='css') {
$.isFunction(callback) && callback(self);
} else {
var q = Queue.useCallback[moduleId];
q = q ? q : (Queue.useCallback[moduleId] = new Queue(moduleId));
// console.log(q);
q.push(arguments.callee);
}
}
t();
temp = null;
}
};
// console.log(arr);
ext==='css'?loadCSS(url,cb):loadJS(url, cb);
}
});
return this;
},
define: function(name, deps, factory) {
if(!$.isString(name)) {
throw new Error('MixJS.define: name 必须为字符串');
}
if($.isFunction(deps)) {
factory = deps;
deps = _emptyArr;
} else {
deps = dealArr(deps, 'define', name);
}
_moduleDepsMap[name] = deps;
new Module(name, deps, factory)
return this;
},
config: function(cfg) {
config = mix(config, cfg);
return this;
},
loadJS:loadJS,
loadCSS:loadCSS,
defined: defined,
loaded: function(file) {
var url = getPath(file)[0];
return loaded(url);
},
mix: mix,
each: each,
noConflict:function(){
return this;
}
};
//基本类型判断
'Function,String,Array,Number'.replace(reg, function(t) {
$['is' + t] = function(s) {
return isType(s, t)
}
});
if(typeof(/./) !== 'function') {
$.isFunction = function(obj) {
return typeof obj === 'function';
};
}
$.isBoolean = function(obj) {
return obj === true || obj === false || isType(obj,'Boolean')
};
$.isUndefined = function(obj) {
return obj === undefined;
//return obj === void 0;
};
//释放到window
global[MixJSName] = $;
MixJSName !== 'MixJS' && (global.MixJS = $);
/**
* 判断模块是否定义
* @param {[type]} module [description]
* @return {[type]} [description]
*/
function defined(module) {
return _modulesMap[module] === 3;
}
/**
* 判断文件是否加载
* @param {[type]} file [description]
* @return {[type]} [description]
*/
function loaded(file) {
return _filesMap[file] === 3;
}
/**
* 数组去重复项和去除空项
* @param {[type]} arr [description]
* @return {[type]} [description]
*/
function dealArr(arr, isFromDefine, mName) {
arr = String(arr).split(',');
var len = arr.length;
if(len === 0) {
return arr[0] === '' ? _emptyArr : arr;
}
var back = [],
obj = {},
val;
for(var i = 0; i < len; i++) {
val = arr[i];
if(val !== '' && !obj[val]) {
obj[val] = 1;
back.push(val);
isFromDefine === 'define' && (Module.parentModule[val] = mName); //定义父模块
}
}
obj = null;
return back;
}
/**
* 检测依赖关系是否都加载完成
* _moduleDepsMap = {
* test.a:[a,b,c]
* a:[d,e]
* }
* @return {[type]} [description]
*/
function checkDeps(module) {
var arr = _moduleDepsMap[module];
if(!arr || arr.length===0) {
return true;
}
var obj = {},
back = true;
for(var i = 0, len = arr.length; i < len; i++) {
var m = arr[i];
if(m===module){
throw new Error(module + ': 发现循环依赖');
}
if(obj[m] === 1) {
//简单去重,不能保证二次依赖去重复
continue;
}
if(regAlias.test(m) && alias[m]){
//如果是alias模块
if(loaded(alias[m])){
obj[m] = 1;
continue;
}
back = false;
break;
}
if(regISCSS.test(m)){
//如果是css文件
if(loaded(getPath(m)[0])){
obj[m] = 1;
continue;
}
back = false;
break;
}
var temp = _moduleDepsMap[m];
if(temp && !(back = checkDeps(m))) {
break;
} else if(!defined(m)) {
back = false;
break;
} else {
obj[m] = 1;
}
}
obj = null;
return back;
}
var _waitModule = {}; //等待转正的module实例
/**
* 模块类
* @param {[type]} id 模块名称
* @param {Array} deps 依赖模块
* @param {[type]} maker 制造函数
* @param {[type]} root 父模块,默认是MixJS
*/
function Module(id, deps, maker, root) {
this.id = id;
this.deps = deps; //必须是数组
this.maker = maker;
this.root = root || $;
// _modulesMap[id] = 1;//定义之前
// this.queue = new Queue();
if(checkDeps(id)) {
this.namespace();
} else {
this.init();
}
}
Module.parentModule = {};
Module.prototype.init = function() {
_waitModule[this.id] = this;
var self = this;
$.use(this.deps, function() {
self.namespace();
})
}
Module.prototype.destroy = function() {
for(var a in this) {
if(this.hasOwnProperty(a)) {
delete this[a];
}
}
}
Module.prototype.namespace = function() {
if(!this.id) {
return;
}
// _modulesMap[this.id] = 2;//定义等待中,可能因为依赖关系没有加载而处于等待中
if(!checkDeps(this.id)) {
return;
}
var names = this.id.split('/'),
root = this.root;
var name;
while(name = names.shift()) {
if(names.length) {
// console.log(root);
root = (root[name] = root[name] || {});
} else {
if($.isUndefined(root[name])) {
try {
var f = $.isFunction(this.maker) && this.maker(this.root);
if(f) {
f['@GOD'] = 'THEO'; //加个尾巴~
root[name] = f;
_modulesMap[this.id] = 3;
}
} catch(e) {
// Module._definedModulesMap[this.id] = 2;//模块定义可能出错了
throw new Error('Module.namespace error:id=>' + this.id + ',info=>' + e.message);
}
}
}
}
var parent = Module.parentModule[this.id];
if(parent && _waitModule[parent]) {
_waitModule[parent].namespace();
}
var q = Queue.useCallback[this.id];
if(q) {
q.fire();
}
delete _waitModule[this.id];
this.destroy();
}
var regProtocol = /^(\w+)(\d)?:.*/,
//协议
regISJS = /\.js$/i,
//是否为js
regISCSS = /\.css$/i,
//是否为css
regRelative = /\.\.\//g,
//相对路径处理
regEXT = /\.(\w+)$/; //后缀2~4
/**
* 获取真实url
* 来自massframework
* @param {[type]} url [description]
* @return {[type]} [description]
*/
function getPath(url, root) {
var ret;
root = root || config.path;
root = root.substr(0, root.lastIndexOf('/'));
id = url;//模块id
if(regAlias.test(url) && alias[url]){
ret = alias[url];
}else if(regProtocol.test(url)) { //如果用户路径包含协议
ret = url;
} else {
var tmp = url.charAt(0),
_2 = url.slice(0, 2);
if(tmp !== '.' && tmp !== '/') { //相对于根路径
ret = root + '/' + url;
} else if(_2 === './') { //相对于兄弟路径
id = url.substr(2);
ret = root + '/' + id;
} else if(_2 === '..') { //相对于父路径
// var arr = root.replace(/\/$/, '').split('/');
var arr = root.split('/');
regRelative.lastIndex = 0;
tmp = url.replace(regRelative, function() {
arr.pop();
return '';
});
id = tmp;
ret = arr.join('/') + '/' + tmp;
}
}
var ext = 'js'; //默认是js文件
tmp = ret.replace(/[?#].*/, '');
if(regEXT.test(tmp)) {
ext = RegExp.$1;
}
if(ext !== 'css' && tmp === ret && !regISJS.test(ret)) { //如果没有后缀名会补上.js
ret += '.js';
}
return [ret, ext, id];
}
/**
* 一个简单队列
* @param {[type]} id [description]
*/
function Queue(id) {
this.id = id;
this.taskList = [];
}
Queue.useCallback = {}; //放置use使用的callback
/**
* 从后部推入
* @param {Function} fn [description]
* @param {[type]} args [description]
* @param {[type]} scope [description]
* @return {[type]} [description]
*/
Queue.prototype.push = function(fn, args, scope) {
return this._add(fn, args, scope, 'push');
}
Queue.prototype.unshift = function(fn, args, scope) {
return this._add(fn, args, scope, 'unshift');
}
Queue.prototype._add = function(fn, args, scope, type) {
if(!type) {
return this;
}
args = _arrSlice.call(arguments, 0, -1);
if(args.length === 0) {
return this;
}
this.taskList[type](args);
return this;
}
/**
* 从后部弹出
* @return {[type]} [description]
*/
Queue.prototype.fire = function() {
if(this._canIDo()) {
var fn = this.taskList.pop();
var args = $.isArray(fn[1]) ? fn[1] : [],
scope = fn[2] || null;
fn = fn[0];
// argsFromCall = $.isArray(argsFromCall)?argsFromCall:[argsFromCall];
// args = args.concat(argsFromCall);
$.isFunction(fn) && fn.apply(scope, args);
this.destroy();
}
return this;
}
Queue.prototype.destroy = function() {
this.taskList.length = 0;
delete this.taskList;
delete Queue.useCallback[this.id]; //记得用完要销毁罪证哦~
delete this.id;
// delete Queue.modules[this.moduleName];
}
Queue.prototype._canIDo = function() {
return this.taskList.length !== 0;
}
/**
* 加载js
* @param {[type]} url [description]
* @param {Function} callback [description]
* @param {[type]} fail [description]
* @param {[type]} charset [description]
* @return {[type]} [description]
*/
function loadJS(url, callback, fail, charset) {
var node = DOC.createElement('script');
var args = _arrSlice.call(arguments, 0);
if($.isString(fail) && args.length === 3) {
//如果fail为字符串,认为是charset
charset = fail;
} else if(args.length === 4 && $.isString(charset)) {
} else {
charset = config.charset;
}
$.isFunction(callback) && jsCallback(node, callback, fail);
node.charset = charset;
node.async = true;
node.src = url;
HEAD.insertBefore(node, BASEELEMENT);
return $;
}
//jscallback检测
var regJSLOAD = /loaded|complete|undefined/;
function jsCallback(node, callback, fail) {
if($.isFunction(fail)) {
node.onerror = jsGetCallback(node, fail);
node.onload = node.onreadystatechange = jsGetCallback(node, callback);
} else {
node.onload = node.onerror = node.onreadystatechange = jsGetCallback(node, callback);
}
}
//js可以检测error,所以加上了这个函数
function jsGetCallback(node, cb) {
return function(e) {
e = e || global.event;
if(e.type === 'load' || regJSLOAD.test(node.readyState)) {
//确保执行一次+内存回收
node.onload = node.onerror = node.onreadystatechange = null
if(node.parentNode && !config.debug) {
HEAD.removeChild(node)
}
node = undefined
cb()
}
}
}
/**
* 加载css文件
* @param {[type]} url [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
function loadCSS(url, callback, fail) {
var node = DOC.createElement('link');
node.rel = 'stylesheet';
node.type = "text/css";
cssCallback(node, callback, fail);
node.href = url;
HEAD.insertBefore(node, BASEELEMENT);
return $;
}
///===============>css load检测来自seajs
// `onload` event is supported in WebKit since 535.23
// Ref:
// - https://bugs.webkit.org/show_activity.cgi?id=38995
var isOldWebKit = Number(UA.replace(/.*AppleWebKit\/(\d+)\..*/, '$1')) < 536;
// `onload/onerror` event is supported since Firefox 9.0
// Ref:
// - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
// - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldFirefox = UA.indexOf('Firefox') > 0 && !('onload' in DOC.createElement('link'));
var cssCallback = (isOldWebKit || isOldFirefox) ?
function(node, callback, fail) {
$.isFunction(callback) &&
setTimeout(function() {
poll(node, callback)
}, 1) // Begin after node insertion
} : function(node, callback, fail) {
$.isFunction(callback) && (node.onload = function() {
node.onload = node.onerror = null
node = undefined
callback()
});
$.isFunction(fail) && (node.onerror = function(){
node.onload = node.onerror = null
node = undefined
fail()
});
}
function poll(node, callback) {
var isLoaded
// for WebKit < 536
if(isOldWebKit) {
if(node.sheet) {
isLoaded = true
}
}
// for Firefox < 9.0
else if(node.sheet) {
try {
if(node.sheet.cssRules) {
isLoaded = true
}
} catch(ex) {
// The value of `ex.name` is changed from
// 'NS_ERROR_DOM_SECURITY_ERR' to 'SecurityError' since Firefox 13.0
// But Firefox is less than 9.0 in here, So it is ok to just rely on
// 'NS_ERROR_DOM_SECURITY_ERR'
if(ex.name === 'NS_ERROR_DOM_SECURITY_ERR') {
isLoaded = true
}
}
}
setTimeout(function() {
if(isLoaded) {
// Place callback in here due to giving time for style rendering.
callback()
} else {
poll(node, callback)
}
}, 1)
}
/**
* 获取类型
* @param {[type]} obj 要判断的对象
* @return {String} 返回类型
*/
function isType(obj, type) {
return _cleanObj.toString.call(obj).slice(8, -1) === type;
}
/**
* 糅杂
* @param {Object} target 原有的默认
* @param {Object} source 第三方来源
*/
function mix(target, source) {
var args = _arrSlice.call(arguments),
i = 1,
key, self = arguments.callee,
//如果最后参数是布尔,判定是否覆写同名属性
ride = $.isBoolean(args[args.length - 1]) ? args.pop() : true;
if(args.length === 1) {
target = !this.window ? this : _cleanObj;
i = 0;
}
while((source = args[i++])) {
//source = [{a:1},{b:3}];
if($.isArray(source)) {
for(var n = 0, len = source.length; n < len; n++) {
self(target, source[n], ride);
}
continue;
}
//杂糅只允许对象
for(key in source) {
if(ride || !(key in target)) {
target[key] = source[key];
}
}
}
return target;
}
}(window, document));