New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

express-mc-demo

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-mc-demo - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

public/js/detail.js

10

app.js

@@ -35,6 +35,12 @@

app.get('/', routes.index);
app.all('/detail/:id', routes.detail);
app.get('/', routes.list);
app.all('/list', routes.list);
app.all('/list/:fid', routes.list);
app.all('/list/:fid/:page', routes.list);
app.all('/detail/:mid', routes.detail);
app.all('/menu', routes.menu);
app.all('/menu/:fid', routes.menu);
app.all('/moveto.do', routes.actions.moveto);
app.listen(3000);
console.log("Express server listening on port %d in %s mode", 3000, app.settings.env);
{
"name": "express-mc-demo",
"version": "0.0.1",
"version": "0.0.2",
"dependencies": {
"express": ">= 3.2.0",
"ejs": ">= 0.0.1",
"express-pjax": ">= 0.0.1"
"ejs": ">= 0.0.1"
},

@@ -9,0 +8,0 @@ "main": "app.js",

47

public/js/list.js

@@ -1,23 +0,26 @@

(function (exports) {
var List = mod({
el: '#list',
events: {
'click li input': 'doCheck',
'click li a': 'show'
},
show: function (e) {
},
doCheck: function () {
console.log(arguments)
}
(function(exports) {
MC.module('list', {
el: '#list',
events: {
'click li input:checkbox': 'doSelect'
},
getSelected: function() {
var checkedEls = this.$el.find('li input:checkbox:checked'),
selected = [];
_.each(checkedEls, function(el) {
var id = $(el.parentNode).data('id');
if (id) {
selected.push(id);
}
});
return selected;
},
doSelect: function(e) {
var selector = e.data.selector;
this.trigger('select', {
currentTarget: e.currentTarget,
selector: selector
});
}
});
exports.list = List;
$(document).ready(function(){
MC.list.render();
});
})(this.MC || (this.MC = {}));
})(this);

@@ -1,429 +0,981 @@

;(function (exports) {
var MC = exports.MC = {};
// MC.History
// ----------------
var Events = MC.Events = {
on: function(events, callback, context) {
var ev;
events = events.split(/\s+/);
var calls = this._callbacks || (this._callbacks = {});
while (ev = events.shift()) {
var list = calls[ev] || (calls[ev] = {});
var tail = list.tail || (list.tail = list.next = {});
tail.callback = callback;
tail.context = context;
list.tail = tail.next = {};
}
return this;
},
off: function(events, callback, context) {
var ev, calls, node;
if (!events) {
delete this._callbacks;
} else if (calls = this._callbacks) {
events = events.split(/\s+/);
while (ev = events.shift()) {
node = calls[ev];
delete calls[ev];
if (!callback || !node) continue;
while ((node = node.next) && node.next) {
if (node.callback === callback && (!context || node.context === context)) continue;
this.on(ev, node.callback, node.context);
}
}
}
return this;
},
trigger: function(events) {
var event, node, calls, tail, args, all, rest;
if (!(calls = this._callbacks)) return this;
all = calls['all'];
(events = events.split(/\s+/)).push(null);
while (event = events.shift()) {
if (all) events.push({
next: all.next,
tail: all.tail,
event: event
});
if (!(node = calls[event])) continue;
events.push({
next: node.next,
tail: node.tail
});
}
rest = Array.prototype.slice.call(arguments, 1);
while (node = events.pop()) {
tail = node.tail;
args = node.event ? [node.event].concat(rest) : rest;
while ((node = node.next) !== tail) {
node.callback.apply(node.context || this, args);
}
}
return this;
}
};
;
(function(exports) {
var MC = exports.MC = {};
// MC.Router
// ---------------
// MC.Events
// ----------------
//
// 提供任何模块的自定义事件处理。继承该对象即可获得publish/subscribe技能。
// 通过`on`来绑定/监听事件,`off`来解绑/取消事件。`trigger`来抛出事件。
//
var Events = MC.Events = {
/**
* 事件监听,绑定指定自定义事件名,在事件触发时执行相应的回调函数。可通过传入`"all"`来绑定所有自定义事件。
*
* @param {String} events 自定义事件名,多个用空格隔开
* @param {Function} callback 事件回调
* @param {Object} context 事件作用域
* @return {Object} 当前对象
*/
on: function(events, callback, context) {
var ev;
events = events.split(/\s+/);
var calls = this._callbacks || (this._callbacks = {});
while (ev = events.shift()) {
var list = calls[ev] || (calls[ev] = {});
var tail = list.tail || (list.tail = list.next = {});
tail.callback = callback;
tail.context = context;
list.tail = tail.next = {};
}
return this;
},
/**
* 事件解绑/取消监听,移除一个或多个事件的监听。如果`context`为空。
*
* @param {String} events 自定义事件名,多个用空格隔开
* @param {Function} callback 事件回调
* @param {Object} context 事件作用域
* @return {Object} 当前对象
*/
off: function(events, callback, context) {
var ev, calls, node;
if (!events) {
delete this._callbacks;
} else if (calls = this._callbacks) {
events = events.split(/\s+/);
while (ev = events.shift()) {
node = calls[ev];
delete calls[ev];
if (!callback || !node) continue;
while ((node = node.next) && node.next) {
if (node.callback === callback && (!context || node.context === context)) continue;
this.on(ev, node.callback, node.context);
}
}
}
return this;
},
/**
* 抛出一个或多个自定义事件,触发回调函数。
* 通过`object.trigger(event, [*args])`的方式,追加的`args`可作为参数传递给`callback`。
*
* @param {Object} events 自定义事件名,多个用空格隔开
* @return {Object} 当前对象
*/
trigger: function(events) {
var event, node, calls, tail, args, all, rest;
if (!(calls = this._callbacks)) return this;
all = calls['all'];
(events = events.split(/\s+/)).push(null);
while (event = events.shift()) {
if (all) events.push({
next: all.next,
tail: all.tail,
event: event
});
if (!(node = calls[event])) continue;
events.push({
next: node.next,
tail: node.tail
});
}
rest = Array.prototype.slice.call(arguments, 1);
while (node = events.pop()) {
tail = node.tail;
args = node.event ? [node.event].concat(rest) : rest;
while ((node = node.next) !== tail) {
node.callback.apply(node.context || this, args);
}
}
return this;
}
};
// Routers map faux-URLs to actions, and fire events when routes are
// matched. Creating a new one sets its `routes` hash, if not set statically.
var Router = MC.Router = function(options) {
options || (options = {});
if (options.routes) this.routes = options.routes;
this._bindRoutes();
this.initialize.apply(this, arguments);
};
// MC.Router
// ---------------
//
// 路由,将动作(URL的变化)与视图(模块UI的变化)连接起来,达到URL与UI的联动。
// 当匹配到已注册的路由规则时,就执行相应的回调,并抛出事件。
//
var Router = MC.Router = function(options) {
options || (options = {});
if (options.routes) this.routes = options.routes;
this._bindRoutes();
this.initialize.apply(this, arguments);
};
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
// 匹配路由字符串参数的正则表达式。
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
// Set up all inheritable **MC.Router** properties and methods.
_.extend(Router.prototype, Events, {
/**
* 路由规则转为正则表达式
*
* @param {String} route 路由规则字符串
* @return {RegExp} 正则表达式对象
*/
var routeToRegExp = function(route) {
route = route.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^\/]+)';
})
.replace(splatParam, '(.*?)');
return new RegExp('^' + route + '$');
};
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function() {},
/**
* 提取路由参数
*
* @param {String} route 路由规则
* @param {String} fragment URL片段
* @return {Object} 参数对象
*/
var extractParameters = function(route, fragment) {
var params = route.exec(fragment).slice(1);
return _.map(params, function(param) {
return param ? decodeURIComponent(param) : null;
});
};
// Manually bind a single named route to a callback. For example:
//
// this.route('search/:query/p:num', 'search', function(query, num) {
// ...
// });
//
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
MC.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
callback && callback.apply(router, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
MC.history.trigger('route', router, name, args);
});
return this;
},
// 扩展Router对象
_.extend(Router.prototype, Events, {
// Simple proxy to `MC.history` to save a fragment into the history.
navigate: function(fragment, options) {
MC.history.navigate(fragment, options);
return this;
},
/**
* 初始化函数,空方法,可重写来完成初始化逻辑。
*
* @public
*/
initialize: function() {},
// Bind all defined routes to `MC.history`. We have to reverse the
// order of the routes here to support behavior where the most general
// routes can be defined at the bottom of the route map.
_bindRoutes: function() {
if (!this.routes) return;
this.routes = _.result(this, 'routes');
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.route(route, this.routes[route]);
}
},
/**
* 路由规则,路由规则及路由回调函数(或方法名)的键值对。可重写来定制路由规则。
*
* {
* 'list/:fid/:mid': function (route, matched, fid, mid) {
* console.log(fid, mid);
* }
* }
*
* @public
* @type {Object}
*/
routes: {},
// Convert a route string into a regular expression, suitable for matching
// against the current location hash.
_routeToRegExp: function(route) {
route = route.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) {
return optional ? match : '([^\/]+)';
})
.replace(splatParam, '(.*?)');
return new RegExp('^' + route + '$');
},
/**
* 手动绑定一个单一命名路由回调。
*
* @public
* @param {String} route 路由规则,支持字符串或正则,
* @param {String} name 路由规则回调方法名,可在无指定回调d情况下,在当前对象中找到相应的方法。也可作为抛出事件名,如`this.on("route:name", fn)`。
* @param {Function} callback 可选,回调
* @return {Object} 当前对象
*/
route: function(route, name, callback) {
var routeSrc = route,
names, module;
if (!_.isRegExp(route)) {
route = routeToRegExp(route);
}
if (_.isFunction(name)) {
callback = name;
name = '';
}
names = name.split(' ');
name = names[1] || names[0];
module = names[0];
// TODO 添加引用
if (module) this.point(routeSrc, module);
if (!callback) callback = this[name];
var router = this;
MC.history.route(route, function(fragment) {
var args = extractParameters(route, fragment);
// 添加原始route规则和匹配结果片段到callback参数中
args.unshift(routeSrc, fragment);
callback && callback.apply(router, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
MC.history.trigger('route', router, name, args);
});
return this;
},
// Given a route, and a URL fragment that it matches, return the array of
// extracted decoded parameters. Empty or unmatched parameters will be
// treated as `null` to normalize cross-browser behavior.
_extractParameters: function(route, fragment) {
var params = route.exec(fragment).slice(1);
return _.map(params, function(param) {
return param ? decodeURIComponent(param) : null;
});
}
/**
* 无刷新的导航到一个URL。代理到`MC.history.navigate`。
*
* @public
* @param {String} fragment 地址片段
* @param {Object} options 导航参数
* @return {Object} 当前对象
*/
navigate: function(fragment, options) {
MC.history.navigate(fragment, options);
return this;
},
});
point: function (route, module) {
var pointer;
this._pointers = this._pointers || {};
// MC.History
// ----------------
// 无模块(名)和路由,直接返回null
if (!module && !_.isString(route)) {
return null;
}
// Handles cross-browser history management, based on either
// [pushState](http://diveintohtml5.info/history.html) and real URLs, or
// [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
// and URL fragments. If the browser supports neither (old IE, natch),
// falls back to polling.
var History = MC.History = function() {
this.handlers = [];
_.bindAll(this, 'checkUrl');
pointer = this._pointers[route];
// 有引用的缓存,获取并返回模块
if (pointer) {
module = module || MC.module(pointer);
// 模块已经被销毁,回收模块名的引用
if (!module) {
delete this._pointers[route];
return null;
}
return module;
}
// Ensure that `History` can be used outside of the browser.
if (typeof window !== 'undefined') {
this.location = window.location;
this.history = window.history;
}
};
if (_.isString(module) && module) {
module = MC.module(module);
}
// Cached regex for stripping a leading hash/slash and trailing space.
var routeStripper = /^[#\/]|\s+$/g;
this._pointers[route] = module.name;
module.register(route);
return module;
},
// Cached regex for stripping leading and trailing slashes.
var rootStripper = /^\/+|\/+$/g;
/**
* 绑定路由,一次`routes`上的规则。
*
* @private
*/
_bindRoutes: function() {
if (!this.routes) return;
this.routes = _.result(this, 'routes');
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.route(route, this.routes[route]);
}
}
});
// Cached regex for detecting MSIE.
var isExplorer = /msie [\w.]+/;
// MC.History
// ----------------
//
// 提供跨浏览器的历史记录管理,基于PushState和真实的URL,或onhashchange和URL片段。
// 如果浏览器都不支持(旧的IE浏览器)退化使用轮询的方式。
//
var History = MC.History = function() {
this.handlers = [];
_.bindAll(this, 'checkUrl');
// Cached regex for removing a trailing slash.
var trailingSlash = /\/$/;
// 确保`History`可在浏览器以外使用
this.location = global.location;
this.history = global.history;
};
// Has the history handling already been started?
History.started = false;
// 全局作用域。
var global = this;
// Set up all inheritable **MC.History** properties and methods.
_.extend(History.prototype, Events, {
// 斜线或哈希头以及空格结尾。
var routeStripper = /^[#\/]|\s+$/g;
// The default interval to poll for hash changes, if necessary, is
// twenty times a second.
interval: 50,
// 去除前斜线和尾斜线。
var rootStripper = /^\/+|\/+$/g;
// Gets the true hash value. Cannot use location.hash directly due to bug
// in Firefox where location.hash will always be decoded.
getHash: function(window) {
var match = (window || this).location.href.match(/#(.*)$/);
return match ? match[1] : '';
},
// 检测IE。
var isExplorer = /msie [\w.]+/;
// Get the cross-browser normalized URL fragment, either from the URL,
// the hash, or the override.
getFragment: function(fragment, forcePushState) {
if (fragment == null) {
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
fragment = this.location.pathname;
var root = this.root.replace(trailingSlash, '');
if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);
} else {
fragment = this.getHash();
}
}
return fragment.replace(routeStripper, '');
},
// 用于移除斜线结尾。
var trailingSlash = /\/$/;
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start: function(options) {
if (History.started) throw new Error("MC.history has already been started");
History.started = true;
// 历史记录是否已经启动。
History.started = false;
// Figure out the initial configuration. Do we need an iframe?
// Is pushState desired ... is it available?
this.options = _.extend({}, {
root: '/'
}, this.options, options);
this.root = this.options.root;
this._wantsHashChange = this.options.hashChange !== false;
this._wantsPushState = !! this.options.pushState;
this._hasPushState = !! (this.options.pushState && this.history && this.history.pushState);
var fragment = this.getFragment();
var docMode = document.documentMode;
var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
// 扩展History对象。
_.extend(History.prototype, Events, {
// Normalize root to always include a leading and trailing slash.
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
/**
* 在需要轮询hash变化的况下,轮询间隔毫秒数。
*
* @type {Number}
*/
interval: 50,
if (oldIE && this._wantsHashChange) {
this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
this.navigate(fragment);
}
/**
* 获取真正的hash值。
*
* @public
* @param {Object} window 当前window对象
* @return {String} 真正的hash值
*/
getHash: function(window) {
var match = (window || global || this).location.href.match(/#(.*)$/);
return match ? match[1] : '';
},
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if (this._hasPushState) {
$(window).on('popstate', this.checkUrl);
} else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
$(window).on('hashchange', this.checkUrl);
} else if (this._wantsHashChange) {
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}
/**
* 获取跨浏览器的标准化的URL片段。
*
* @public
* @param {String} fragment 可选,原始的URL片段
* @param {Boolean} forcePushState 可选,强制使用pushState
* @return {String} 标准的URL片段
*/
getFragment: function(fragment, forcePushState) {
if (fragment == null) {
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
fragment = this.location.pathname;
var root = this.root.replace(trailingSlash, '');
if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);
} else {
fragment = this.getHash();
}
}
return fragment.replace(routeStripper, '');
},
// Determine if we need to change the base url, for a pushState link
// opened by a non-pushState browser.
this.fragment = fragment;
var loc = this.location;
var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
/**
* 启动历史记录,如果匹配到规则,则返回`true`,否则返回`false`
*
* @public
* @param {Object} options 可选,启动配置
* @param {Boolean} options.pushState 是否使用`pushState`
* @param {Boolean} options.hashChange 是否使用`hashChange`
* @param {Boolean} options.root 根路径
* @param {Boolean} options.silent 首次加载不触发事件
* @return {Boolean} 是否匹配到规则
*/
start: function(options) {
// 已启动
if (History.started) {
return;
}
History.started = true;
// If we've started off with a route from a `pushState`-enabled browser,
// but we're currently in a browser that doesn't support it...
if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
this.fragment = this.getFragment(null, true);
this.location.replace(this.root + this.location.search + '#' + this.fragment);
// Return immediately as browser will do redirect to new url
return true;
this.options = _.extend({}, {
root: '/'
}, this.options, options);
this.root = this.options.root;
this._wantsHashChange = this.options.hashChange !== false;
this._wantsPushState = !! this.options.pushState;
this._hasPushState = !! (this.options.pushState && this.history && this.history.pushState);
var fragment = this.getFragment();
var docMode = document.documentMode;
var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
// Or if we've started out with a hash-based route, but we're currently
// in a browser where it could be `pushState`-based instead...
} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
this.fragment = this.getHash().replace(routeStripper, '');
this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
}
// 标准化的根路径,始终包括开头和结尾的斜线。
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
if (!this.options.silent) return this.loadUrl();
},
// 用iframe的方式兼容老浏览器
if (oldIE && this._wantsHashChange) {
this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
this.navigate(fragment);
}
// Disable MC.history, perhaps temporarily. Not useful in a real app,
// but possibly useful for unit testing Routers.
stop: function() {
$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
clearInterval(this._checkUrlInterval);
History.started = false;
},
// 根据是否使用`PushState`或hash,以及是否支持`hashchange`,做不同的监听操作
if (this._hasPushState) {
$(window).on('popstate', this.checkUrl);
} else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
$(window).on('hashchange', this.checkUrl);
} else if (this._wantsHashChange) {
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}
// Add a route to be tested when the fragment changes. Routes added later
// may override previous routes.
route: function(route, callback) {
this.handlers.unshift({
route: route,
callback: callback
});
},
this.fragment = fragment;
var loc = this.location;
var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
// Checks the current URL to see if it has changed, and if it has,
// calls `loadUrl`, normalizing across the hidden iframe.
checkUrl: function(e) {
var current = this.getFragment();
if (current === this.fragment && this.iframe) {
current = this.getFragment(this.getHash(this.iframe));
}
if (current === this.fragment) return false;
if (this.iframe) this.navigate(current);
this.loadUrl() || this.loadUrl(this.getHash());
},
if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
this.fragment = this.getFragment(null, true);
this.location.replace(this.root + this.location.search + '#' + this.fragment);
// 返回浏览器立即重定向到新的URL
return true;
} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
this.fragment = this.getHash().replace(routeStripper, '');
this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
}
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
loadUrl: function(fragmentOverride) {
var fragment = this.fragment = this.getFragment(fragmentOverride);
var matched = _.any(this.handlers, function(handler) {
if (handler.route.test(fragment)) {
handler.callback(fragment);
return true;
}
});
return matched;
},
// 如果不是`silent`模式,加载当前URL
if (!this.options.silent) return this.loadUrl();
},
// Save a fragment into the hash history, or replace the URL state if the
// 'replace' option is passed. You are responsible for properly URL-encoding
// the fragment in advance.
//
// The options object can contain `trigger: true` if you wish to have the
// route callback be fired (not usually desirable), or `replace: true`, if
// you wish to modify the current URL without adding an entry to the history.
navigate: function(fragment, options) {
if (!History.started) return false;
if (!options || options === true) options = {
trigger: options
};
fragment = this.getFragment(fragment || '');
if (this.fragment === fragment) return;
this.fragment = fragment;
var url = this.root + fragment;
/**
* 禁用路由
*
* @public
*/
stop: function() {
$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
clearInterval(this._checkUrlInterval);
History.started = false;
},
// If pushState is available, we use it to set the fragment as a real URL.
if (this._hasPushState) {
this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
/**
* 添加路由规则
*
* @public
* @param {String} route 路由规则
* @param {Function} callback 回调
*/
route: function(route, callback) {
this.handlers.unshift({
route: route,
callback: callback
});
},
// If hash changes haven't been explicitly disabled, update the hash
// fragment to store history.
} else if (this._wantsHashChange) {
this._updateHash(this.location, fragment, options.replace);
if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
// Opening and closing the iframe tricks IE7 and earlier to push a
// history entry on hash-tag change. When replace is true, we don't
// want this.
if (!options.replace) this.iframe.document.open().close();
this._updateHash(this.iframe.location, fragment, options.replace);
}
/**
* 检测当前URL是否有变化,有则调用`loadUrl`
*
* @public
* @param {Object} e event对象
*/
checkUrl: function(e) {
var current = this.getFragment();
if (current === this.fragment && this.iframe) {
current = this.getFragment(this.getHash(this.iframe));
}
if (current === this.fragment) return false;
if (this.iframe) this.navigate(current);
this.loadUrl() || this.loadUrl(this.getHash());
},
// If you've told us that you explicitly don't want fallback hashchange-
// based history, then `navigate` becomes a page refresh.
} else {
return this.location.assign(url);
}
if (options.trigger) this.loadUrl(fragment);
},
/**
* 加载当前URL片段,如果路由匹配成功,则执行回调并返回`true`,否则返回`false`
*
* @public
* @param {String} fragmentOverride 新的URL片段
* @return {Boolean} 路由匹配是否成功
*/
loadUrl: function(fragmentOverride) {
var fragment = this.fragment = this.getFragment(fragmentOverride);
var matched = _.any(this.handlers, function(handler) {
if (handler.route.test(fragment)) {
handler.callback(fragment);
return true;
}
});
return matched;
},
// Update the hash location, either replacing the current entry, or adding
// a new one to the browser history.
_updateHash: function(location, fragment, replace) {
if (replace) {
var href = location.href.replace(/(javascript:|#).*$/, '');
location.replace(href + '#' + fragment);
} else {
// Some browsers require that `hash` contains a leading #.
location.hash = '#' + fragment;
}
}
/**
*
*
* @public
* @param {String} fragment URL片段
* @param {Object} options 配置参数
*/
navigate: function(fragment, options) {
if (!History.started) return false;
if (!options || options === true) options = {
trigger: options
};
fragment = this.getFragment(fragment || '');
if (this.fragment === fragment) return;
this.fragment = fragment;
var url = this.root + fragment;
});
// replace 或者 产生新的记录
if (this._hasPushState) {
this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
} else if (this._wantsHashChange) {
this._updateHash(this.location, fragment, options.replace);
if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
if (!options.replace) this.iframe.document.open().close();
this._updateHash(this.iframe.location, fragment, options.replace);
}
} else {
return this.location.assign(url);
}
if (options.trigger) this.loadUrl(fragment);
},
// Create the default MC.history.
MC.history = new History;
/**
* 更新hash
*
* @private
* @param {Object} location location
* @param {String} fragment 片段
* @param {Boolean} replace 是否替换
*/
_updateHash: function(location, fragment, replace) {
if (replace) {
var href = location.href.replace(/(javascript:|#).*$/, '');
location.replace(href + '#' + fragment);
} else {
// hash 头
location.hash = '#' + fragment;
}
}
// Helpers
// -------
});
// Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
var extend = function(protoProps, staticProps) {
var parent = this;
var child;
// 创建`History`的实例
MC.history = new History;
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function() {
return parent.apply(this, arguments);
};
}
// MC.Module
// ----------------
//
// 模块,可把页面上的一个功能划分成一个模块。
// 模块管理自身的渲染、更新及事件绑定。
//
var Module = function() {
// 是否已渲染
this.rendered = false;
this.events = this.events || {};
this._initRoutes();
_.defaults(this.events, {
// TODO 默认不可覆盖
'click a': '_handleClick'
});
this.initialize.apply(this, arguments);
};
// Add static properties to the constructor function, if supplied.
_.extend(child, parent, staticProps);
// 规则容器
var containers = MC._containers = {};
// 标记DOM READY
var IS_DOM_READY = false;
$(function() {
IS_DOM_READY = true;
});
// Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function.
var Surrogate = function() {
this.constructor = child;
};
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
var backfillParameters = function(route, params) {
params = params || {};
route = route.replace(/\{(\w+)\}/g, function(s, k) {
return _.isUndefined(params[k]) ? s : params[k];
}).replace(/\(\/[^(\/:)]*:([^(\/:)]+)\)/g, function(s, k) {
return _.isUndefined(params[k]) ? '' : ('/' + params[k]);
}).replace(/\/[^(\/:)]*:([^(\/:)]+)/g, function(s, k) {
return _.isUndefined(params[k]) ? '/0' : ('/' + params[k]);
});
return route;
};
// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) _.extend(child.prototype, protoProps);
var extractParameterNames = function(route) {
var keys = {};
// Set a convenience property in case the parent's prototype is needed
// later.
child.__super__ = parent.prototype;
// true表示必选,false表示可选
route = route.replace(/\{(\w+)\}/g, function(s, k) {
keys[k] = true;
return s;
}).replace(/\(\/[^(\/:)]*:([^(\/:)]+)\)/g, function(s, k) {
keys[k] = false;
return s;
}).replace(/\/[^(\/:)]*:([^(\/:)]+)/g, function(s, k) {
keys[k] = true;
return s;
});
return child;
};
return keys;
};
// Set up inheritance for the model, collection, router, view and history.
MC.extend = Router.extend = History.extend = extend;
var isParamsMatched = function(keys, params) {
var pass = true;
})(this);
_.each(keys, function(value, key) {
// value true是否必选
if (value && !_.has(params, key) && pass) {
pass = pass && false;
}
});
_.each(params, function (value, key) {
if (!_.has(keys, key) && pass) {
pass = pass && false;
}
});
return pass;
};
_.extend(Module.prototype, Events, {
/**
* 初始化函数,空方法,可重写来完成初始化逻辑。
*
* @public
*/
initialize: function() {},
render: function(el) {
if (this.rendered === true) {
return;
}
if (_.isFunction(this.beforeRender)) {
this.on('beforeRender', this.beforeRender);
}
if (this.trigger('beforeRender') === false) {
return;
}
this.$el = $(el || _.result(this, 'el'));
if (!this.$el) {
throw 'ERROR: module container is not exist!';
}
_.each(this._routes, _.bind(function(value, route) {
containers[route] = this.$el.selector;
}, this));
this._initData();
this._bind();
if (_.isFunction(this.afterRender)) {
this.on('afterRender', this.afterRender);
}
this.trigger('afterRender');
this.rendered = true;
return this;
},
register: function(route) {
if (!_.isString(route)) {
return;
}
this._routes[route] = true;
if (this.rendered === true) {
containers[route] = this.$el.selector;
} else if (IS_DOM_READY === true) {
this.render();
}
this._initData();
},
// 见`_refresh`
refresh: function() {
this._refresh.apply(this, arguments);
},
// 有路由规则的模块,在匹配到路由变化时,会自动刷新。
// 无路由规则匹配的,按指定的URL发请求刷新内容。
// 无请求的模块,刷新只做页面重新渲染。
_refresh: function(data, container, callback) {
var finished, params;
// 无指定容器
if (_.isFunction(container)) {
callback = container;
container = '';
}
params = this._getReqParams(data, container);
// 完成后回调
finished = _.bind(function(res) {
var $con = $(params.selector);
if (_.isString(res)) {
// 刷新容器中数据
$con.html(res);
this.trigger('html', res);
}
if (_.isFunction(callback)) {
callback.apply(this, arguments);
}
this.trigger('refresh', params.data, $con.selector);
}, this);
if (params.url) {
this.ajax(params.url).done(function(res) {
finished(res);
});
} else {
finished();
}
},
ajax: function(settings) {
return ajax.call(this, settings);
},
_bind: function(events) {
if (!(events || (events = _.result(this, 'events')))) return this;
this._unbind();
for (var key in events) {
var method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
if (!method) continue;
var match = key.match(/^(\S+)\s*(.*)$/);
var eventName = match[1],
selector = match[2];
var data = {
selector: [this.$el.selector, selector].join(' ')
};
method = _.bind(method, this);
eventName += '.delegateEvents' + this.name;
if (selector === '') {
this.$el.on(eventName, data, method);
} else {
this.$el.on(eventName, selector, data, method);
}
}
return this;
},
_unbind: function() {
this.$el.off('.delegateEvents' + this.name);
return this;
},
destroy: function() {
this._unbind();
this.$el.html('');
this.$el = null;
// TODO
// delete MC.module(this.name);
this.trigger('destroy');
},
show: function() {
this.$el.show();
},
hide: function() {
this.$el.hide();
},
// 缓存最后请求地址
// `cache(data)` 或 `cache(url, src)`
cache: function() {
var args = [].slice.call(arguments),
data = args[0];
// 第一个参数为非空字符串,即是url
if (_.isString(data) && data) {
this._cache = {
url: data,
src: args[1]
};
// 未缓存或者有新数据
} else if (!this._cache || _.isObject(data)) {
this._cache = this._getUrlParams(data);
}
return _.clone(this._cache) || {};
},
clear: function() {
this.$el.html('');
this.trigger('html', '');
},
_initData: function () {
// 获取初始化数据
var data = this.$el.data('init') || {};
this.cache(data);
},
_initRoutes: function() {
var routes = [].concat(this.route || []),
obj = {};
_.each(routes, function(route) {
obj[route] = true;
});
this._routes = obj;
},
_handleClick: function(event) {
var link = event.currentTarget,
con = $(link).attr('data-con'),
url = '';
if (event.which > 1 || event.metaKey) {
return;
}
if (location.protocol !== link.protocol || location.host !== link.host) {
return;
}
if (link.hash && link.href.replace(link.hash, '') === location.href.replace(location.hash, '')) {
return;
}
MC.history.navigate(link.pathname, true);
fragment = MC.history.fragment;
// 如果在link上指定了特定URL的容器
if (con) {
containers[fragment] = con;
}
event.preventDefault();
return false;
},
_getReqParams: function(data, container, callback) {
var params = this.cache(data);
// 最新指定的容器,然后是特定url容器(来自DOM上的`data-con`属性),最后是路由注册到的容器
$con = $(container || containers[params.url] || containers[params.src]);
// 最最后是自身容器
$con = $con.length > 0 ? $con : this.$el;
// 缓存选择器名,避免缓存DOM带来的内存泄漏
params.selector = $con.selector;
return params;
},
_getUrlParams: function(data) {
var params = {};
// 后台不支持通过实际url请求到片段的,用独立的url
if (this.url) {
params.url = backfillParameters(this.url, data);
params.src = this.url;
} else {
_.some(this._routes, function(value, route) {
var names = extractParameterNames(route);
// 如果通过参数匹配到正确的route
if (isParamsMatched(names, data)) {
params.url = '/' + backfillParameters(route, data);
params.src = route;
return true;
}
});
}
return params;
}
});
MC.history.on('route', function(router, name, args) {
var route = args[0],
fragment = args[1],
url = '/' + fragment,
module = router.point(route);
if (module) {
module.cache(url, route);
}
ajax(url).done(function(res) {
var container = containers[route],
$con = container && $(container);
if ($con) {
$con.html(res);
module && module.trigger('html', res);
} else if (MC.history.options.silent) {
throw 'ERROR: no container';
}
});
});
// Helpers
// -------
var extend = function(protoProps, staticProps) {
var parent = this,
child;
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function() {
return parent.apply(this, arguments);
};
}
_.extend(child, parent, staticProps);
var Surrogate = function() {
this.constructor = child;
};
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
child.prototype.__super__ = Surrogate.prototype;
if (protoProps) {
_.extend(child.prototype, protoProps);
}
child.__super__ = parent.prototype;
return child;
};
var ajax = function(settings) {
settings = settings || {};
settings.url = typeof settings === 'string' ? settings : settings.url;
return $.ajax(settings);
};
var extractCeateParams = function(args) {
var params = [''];
args = [].slice.call(args);
if (_.isObject(args[0])) {
args.unshift(args[0].name || '');
}
_.each(args, function(arg, i) {
if ((i === 0 && _.isString(arg)) || (i > 0 && i < 3 && _.isObject(arg))) {
params[i] = arg;
}
});
// 添加name属性
_.defaults(params[1] || {}, {
name: params[0]
});
return params;
};
/**
* 创建一个实例
*
* @return {Object} 对象的实例
*/
var ceate = function() {
this._inc = this._inc || {};
var params = extractCeateParams(arguments),
name = params.shift(),
ret = this._inc;
if (params[0]) {
ret = new(extend.apply(this, params));
if (name) {
this._inc[name] = ret;
}
} else if (name) {
ret = this._inc[name];
}
return ret;
};
// 自动开启历史记录
MC.autoStart = true;
// 自动渲染所有模块
MC.autoRender = true;
// 唯一路由实例
var routerInc = null;
/**
* router方法,用于创建一个`router`的实例
*
* @return {Object} router对象的实例
*/
MC.router = function() {
if (routerInc) {
return routerInc;
}
routerInc = ceate.apply(Router, arguments);
if (MC.autoStart === true) {
MC.history.start({pushState: true, silent: true});
}
if (MC.autoRender === true) {
// 渲染所有模块
_.each(MC.module(), function (mod) {
mod.render();
});
}
return routerInc;
};
/**
* module方法,用于创建一个`module`的实例
*
* @return {Object} module对象的实例
*/
MC.module = function() {
return ceate.apply(Module, arguments);
};
})(this);
// TODO
// - 更新title

@@ -1,18 +0,48 @@

(function(exports){
var AppRouter = MC.Router.extend({
$(function(){
var module = MC.module();
MC.router({
routes: {
'detail/:id': 'renderDetail',
'': 'renderPage'
'detail/:mid': 'detail renderDetail',
'list(/:fid)(/:page)': 'list renderList',
'': 'list renderList'
},
renderPage: function () {
console.log('renderPage', arguments);
renderList: function (route, fragment, fid, page) {
module.menu.setCurrent(fid);
module.toolbar.refresh({
fid: fid
})
module.detail.clear();
},
renderDetail: function() {
console.log('renderDetail', arguments, MC.history.fragment)
renderDetail: function(route, fragment, mid) {
module.detail.mid(mid);
}
});
var app_router = new AppRouter();
MC.history.start({pushState: true, silent: true});
})(this);
module.toolbar.on('moveTo', function (params) {
var target = params.value;
var ids = module.list.getSelected();
this.doMoveTo({
ids: ids,
target: target
}, function () {
module.list.refresh();
module.menu.refresh();
if (_.contains(ids, module.detail.mid())) {
module.detail.clear();
}
});
});
// 刷新时高亮一下模块最后刷新时间
_.each(module, function (mod) {
mod.on('html', function () {
var el = this.$el.find('.last-refresh-info-time');
$(el).addClass('highlighted');
setTimeout(function () {
$(el).removeClass('highlighted');
}, 500);
});
});
});

@@ -5,24 +5,166 @@ /*

var list = [
{ id: '2_0:DzzzzyiWNpG$----1HOTIK3', subject: "订单1121368020,买家付款成功,订单金额:$314.56", from: 'transaction_service@service.alibaba.com'},
{ id: '2_0:DzzzzyiWOqq$----1HTA57n', subject: "通知:您的【产品名称: same image in photobank】在线批发产品信息未能发布,请修改", from: 'seller@aliexpress.com'},
{ id: '2_0:DzzzzyiWP43$----1HTA57y', subject: "allinnewname newxing has sent you a new message", from: 'feedback@service.alibaba.com' },
{ id: '2_0:DzzzzyiWP6p$----1HTA586', subject: "Amanda King给您发送了一封新站内信", from: 'wiky.chen@service.alibaba.com' }
];
var list = [{
id: 'DzzzzyiWNpG$----1HOTIK3',
subject: "订单1121368020,买家付款成功,订单金额:$314.56",
from: 'transaction_service@service.alibaba.com',
folder: 'inbox'
}, {
id: 'DzzzzyiWOqq$----1HTA57n',
subject: "通知:您的【产品名称: same image in photobank】在线批发产品信息未能发布,请修改",
from: 'seller@aliexpress.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP43$----1HTA57y',
subject: "allinnewname newxing has sent you a new message",
from: 'feedback@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP6p$----1HTA586',
subject: "Amanda King给您发送了一封新站内信",
from: 'wiky.chen@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWOqq$----1HOA52n',
subject: "通知:邮箱近期维护",
from: 'servers@aliexpress.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP43$----1HOR57B',
subject: "wiky chen has sent you a new message",
from: 'feedback@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWNpG$----1HOTID3',
subject: "订单121336323,买家付款成功,订单金额:$344.14",
from: 'service@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP6p$----1HTC546',
subject: "Amanda King has sent you a new message",
from: 'wiky.chen@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWNp3$----1HOTIK3',
subject: "订单1121363380,买家付款成功,订单金额:$2002.56",
from: 'transaction_service@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWO4q$----1HTA57n',
subject: "通知:您的【产品名称: same image in photobank】在线批发产品信息未能发布,请修改",
from: 'seller@aliexpress.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP43$----1HTA57y',
subject: "allinnewname xxxx has sent you a new message",
from: 'feedback@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP7p$----1HT3586',
subject: "Amanda King给您发送了一封新站内信",
from: 'wiky.chen@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWOqs$----1HOA22n',
subject: "通知:您的【产品名称photobank】发布失败",
from: 'servers@aliexpress.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP46$----1H4R57D',
subject: "wiky chen has sent you a new message",
from: 'feedback@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWPoG$----1HOTI52',
subject: "订单120336332,买家付款成功,订单金额:$1234.14",
from: 'service@service.alibaba.com',
folder: "inbox"
}, {
id: 'DzzzzyiWP6p$----1HTA356',
subject: "通知:您的【产品名称photobank】发布失败",
from: 'service@service.alibaba.com',
folder: "inbox"
}];
exports.index = function(req, res) {
res.renderPjax('layout', { title: 'home', list: list, pageType: 'list' })
var menu = ['inbox', 'spam', 'trash'];
var countPerPage = 3;
var getCount = function(list) {
var count = {};
list.forEach(function(o) {
count[o.folder] = count[o.folder] || 0;
count[o.folder] = count[o.folder] + 1;
});
return count;
};
exports.list = function (req, res) {
res.renderPjax('list', { title: 'home', list: list })
var getPageParams = function(params) {
var mid = params.mid,
fid = params.fid,
page = params.page,
countPerPage = params.countPerPage,
item = params.item;
var result = [],
count = {};
var listByFid = list.filter(function(o) {
return o.folder === fid;
}),
max = Math.ceil(listByFid.length / countPerPage),
min = Math.min(1, max),
page = Math.min(Math.max(min, parseInt(page)), max);
listByFid.some(function(o, i) {
if (i >= countPerPage * (page - 1) && o.folder === fid) {
result.push(o);
}
if (result.length === countPerPage) {
return true;
}
});
return {
menu: menu,
list: result,
pagination: {
current: page,
min: min,
max: max
},
fid: fid,
mid: mid || '',
item: item || null
};
}
exports.list = function(req, res) {
var params = getPageParams({
page: req.params['page'] || 1,
fid: req.params['fid'] || 'inbox',
countPerPage: countPerPage
}),
index;
params.title = 'list';
if (!req.xhr) {
index = 'layout';
params.pageType = 'list';
params.count = getCount(list);
} else {
index = 'list';
}
res.render(index, params)
};
exports.detail = function (req, res) {
var id = req.params['id'],
exports.detail = function(req, res) {
var mid = req.params['mid'],
fid = '',
page = 1,
item = '';
list.some(function (o) {
if(o.id === id) {
list.some(function(o) {
if (o.id === mid) {
item = o;
fid = item.folder;
return true;

@@ -32,7 +174,65 @@ }

var count = 0;
list.some(function(o) {
if (o.fid === fid) {
count ++;
if(o.id === mid) {
page = Math.ceil(count/countPerPage) || 1;
return true;
}
}
});
var params = getPageParams({
fid: fid,
mid: mid,
page: page,
countPerPage: countPerPage,
item: item
}),
index;
params.title = 'detail';
if (!req.xhr) {
res.renderPjax('layout', { title: 'detail', pageType: 'detail' , item: item })
index = 'layout';
params.pageType = 'detail';
params.count = getCount(list);
} else {
res.renderPjax('detail', { title: 'detail', item: item})
index = 'detail';
}
};
res.render(index, params);
};
exports.menu = function(req, res) {
var fid = req.params['fid'];
var params = {
fid: fid || 'inbox',
menu: menu,
count: getCount(list)
};
res.render('menu', params);
};
exports.actions = {
moveto: function(req, res) {
var data = req.query || req.body,
ids = [].concat(data.ids || []),
target = data.target;
if (ids.length > 0 && target) {
ids.forEach(function(id) {
list.some(function(o) {
if (o.id === id) {
o.folder = target;
}
});
});
res.json({
code: 200
});
}
res.json({
code: 304
});
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc