UNPKG

nuijs

Version:

nui框架

610 lines (577 loc) 18 kB
/** * @author Aniu[2017-02-27 23:46] * @update Aniu[2018-01-02 21:25] * @version 1.0.2 * @description 路由 */ Nui.define(function(require) { var component = require('../core/component'); var template = require('../core/template'); var events = require('../core/events'); var request = require('../core/request'); var statics = { _paths: {}, _request: {}, _init: function() { var self = this; Nui.doc.on('click', '.nui-router-back', function() { return self.back() }).on('click', '.nui-router-forward', function() { return self.forward() }) }, _setPaths: function(rule, paths) { if (!this._paths[rule]) { this._paths[rule] = paths } }, _replace: function(hash) { //IE8-中 A标签href属性会被添加上域名,将其移除 return hash.replace(location.protocol + '//' + location.host, '') //移除空白 .replace(/\s+/g, '') //移除 #!前缀 .replace(/^\#\!?/, '') //开头添加斜杠 .replace(/^([^\/])/, '/$1') //移除末尾斜杠 .replace(/\/$/, ''); }, _getWrapper: function(object) { var opts = object._options, getWrapper = opts.getWrapper; var wrapper = '<div class="nui-router-wrapper"></div>'; if (getWrapper) { if (typeof getWrapper === 'string') { wrapper = getWrapper } else if (typeof getWrapper === 'function') { wrapper = getWrapper.call(opts, object) } } return $(wrapper).appendTo(object.container) }, _split: function(url) { var ret = { url: url, params: {} } var match = url.match(/\?[^\/\s]+$/); if (match) { var params = match[0]; ret.url = url.replace(params, ''); params = params.replace('?', '').split('&'); Nui.each(params, function(v, k) { var arr = v.split('='); ret.params[arr[0]] = arr[1] }) } return ret; }, _change: function() { var self = this, hashTemp = location.hash, ret = this._split(hashTemp), hash = self._replace(ret.url), query = ret.params; self.isRender = false; delete self._active; delete self._options; Nui.each(self._paths, function(v) { if (hash === v.path || hash.indexOf(v.path + '/') === 0) { var _hash = hash.replace(v.path, '').replace(/^\//, ''); var params = _hash ? _hash.split('/') : []; var object = self.__instances[v.id], opts = object._options; var match = params.length === v.params.length || opts.level === 3; if (match) { var render = function() { //router.location强制刷新或者公共容器才会重新渲染 var isRender = object._isRender === true || !object._wrapper; var changed; delete object._isRender; if (opts.wrapper && !object._wrapper) { if (typeof opts.wrapper !== 'boolean') { object._wrapper = object.container.children(opts.wrapper) } else { object._wrapper = self._getWrapper(object) } object._inner = object._wrapper.children(); } else if (!opts.wrapper && !self._wrapper) { self._wrapper = self._getWrapper(object); self._inner = self._wrapper.children(); } if (isRender) { if (object.rendered) { opts.data = Nui.extend(true, {}, object._defaultOptions.data) } //取消前一个页面的所有请求 Nui.each(self._request, function(v, i) { var obj = self.__instances[i]; if (!obj._options.wrapper || obj === object) { Nui.each(v, function(xhr, url) { xhr.abort() }) delete self._request[i] } }) } self._active = { path: v.path + '/', url: hash + '/', params: {}, query: query } self._options = opts Nui.each(v.params, function(val, key) { self._active.params[val] = params[key] }) Nui.each(query, function(val, key) { self._active.params[val] = query[key] }) opts.data = Nui.extend(true, opts.data, self._active); opts.element = object._inner || self._inner; var wrapper = object._wrapper || self._wrapper; if (!opts.element || !opts.element.length) { opts.element = wrapper } var callback = function() { if (typeof opts.onAfter === 'function') { opts.onAfter.call(opts, object) } if (Nui.bsie7) { self._setHistory(hashTemp) } self.isRender = true object._emitListener('change') } var showWrapper = function() { var siblings = wrapper.siblings('.nui-router-wrapper').hide(); wrapper.show() if (opts.element !== wrapper) { wrapper.children().show(); siblings.children().hide(); } } var sendData; if (object._sendData === true) { if (typeof object.sendData === 'object' && typeof opts.onData === 'function') { opts.data = Nui.extend(true, opts.data, object.sendData); opts.onData.call(opts, object.sendData, object); delete object._sendData; } else if (typeof object.sendData === 'function') { sendData = object.sendData; } } if (typeof opts.onChange === 'function') { changed = opts.onChange.call(opts, object, isRender) } //true不渲染,但是执行onAfter if (typeof changed === 'boolean') { if (changed === true) { showWrapper() callback() } else { self.isRender = true } return false } showWrapper() if (isRender) { opts.element.off(); object.render.call(object); if (sendData) { sendData(opts) } if (typeof opts.onInit === 'function') { opts.onInit.call(opts, object); } events.call(opts); object.rendered = true; } else { if (sendData) { sendData(opts) } } callback() if (object.sendData !== undefined) { delete object.sendData; } } //异步渲染 if (typeof opts.async === 'function') { self.isRender = true; opts.async.call(opts, function(_options) { delete object._options.async; delete object._defaultOptions.async; object._defaultOptions = Nui.extend(true, {}, object._defaultOptions, _options) object.option(_options, true) opts = object._options; render() }) } else { render() } return false } } }) if (!self.isRender) { self._entry(hash) } self._oldhash = hashTemp; }, //检测当前路由地址是否存在,不存在则跳转到入口页面 _entry: function(hash) { var entry = false Nui.each(this.__instances, function(v) { if (v._options.entry === true) { if (v.target) { v._render(v.target.eq(0)); } else if (v.path) { v._render(v.path); } entry = true; return false } }) if (!entry && typeof this._error === 'function') { this._error(hash) } }, _bindHashchange: function() { var self = this; if (Nui.bsie7) { var hashchange = function(ret) { var hash = location.hash; if (self._oldhash !== hash) { return !ret } return false } setInterval(function() { if (hashchange()) { self._change() } }, 100); hashchange(true) } else { Nui.win.on('hashchange', function() { self._change() }) } }, _$ready: null, _$fn: null, init: null, start: function(value) { this._change() }, reload: function() { var active = this.active(true); if (active) { active.self._isRender = true; this._change() } }, location: function(url, data, render) { var self = this; if (url) { if (arguments.length <= 2 && typeof data === 'boolean') { render = data; data = undefined; } var temp, object, query = ''; var match = url.match(/\?[^\/\s]+$/); if (match) { query = match[0] } url = this._replace(url.replace(/\?[^\/\s]+$/, '')); Nui.each(this._paths, function(val, rule) { if (rule === url || (url.indexOf(val.path) === 0 && (temp = url.replace(new RegExp('^' + val.path), '').replace(/^\//, '')) && temp.split('/').length === val.params.length)) { object = self.__instances[val.id]; return false } }) if (object) { if (data !== undefined) { object.sendData = data; object._sendData = true; } object._isRender = render; object._render(url + query) } } else { self.start() } }, active: function(isOptions) { if (isOptions) { return this._options } return this._active }, forward: function(index) { history.forward(index); return false }, back: function(index) { history.back(index); return false }, error: function(callback) { this._error = callback } } if (Nui.bsie7) { statics._history = []; statics._setHistory = function(hash) { if (!this._isHistory) { var last = this._history.slice(-1); if (!last.length || last[0].hash !== hash) { Nui.each(this._history, function(val) { val.active = false }); this._history.push({ hash: hash, active: true }) } } this._isHistory = false; } Nui.each(['forward', 'back'], function(v) { var value = v === 'forward' ? 1 : -1; statics[v] = function() { var self = this, len = self._history.length; statics._isHistory = true; Nui.each(self._history, function(val, i) { var index = i + value; if (val.active) { //历史记录在起始或者末尾时,调用原生的记录 if (index === -1 || index === len) { window.history[v](); return false } var _history = self._history[index]; if (_history) { location.hash = _history.hash; _history.active = true; } val.active = false; return false } }) return false } }) } return this.extend(component, { _static: statics, _options: { path: '', template: '', container: null, data: {}, entry: false, wrapper: false, reload: false, level: 2, onBefore: null, onChange: null, onData: null, onRender: null, onInit: null, onAfter: null }, _init: function() { var self = this, router = self.constructor; if (self._exec() && !router._bind) { router._bind = true; router._bindHashchange(); } }, _exec: function() { var self = this, opts = self._options; if (opts.path && (self.container = self._jquery(opts.container))) { self._initPath(); self._setPaths(); if (self._getTarget()) { self._event() } else if (opts.relTarget) { self.target = self._jquery(opts.relTarget) } return self } }, _setPaths: function() { var self = this, opts = self._options, router = self.constructor; var paths = self._getPathData(); var len = paths.params.length; if ((!len && opts.level === 1) || opts.level !== 1) { router._setPaths(paths.rule, paths) } if (len && opts.level > 0) { var params = [], split = '/:', param, sub; while (param = paths.params.shift()) { params.push(param); sub = params.join(split); router._setPaths(paths.rule + split + sub, Nui.extend({}, paths, { params: sub.split(split) })) } } }, _initPath: function() { var self = this, opts = self._options, router = self.constructor; self.path = router._replace(opts.path) }, _getPathData: function() { var self = this, path = self.path, opts = self._options, index = path.indexOf('/:'); var paths = { id: self.__id, params: [], rule: path, path: path } if (index !== -1) { paths.params = path.substr(index + 2).split('/:'); self.path = paths.path = path.substr(0, index); if (opts.level > 0) { paths.rule = paths.path; } } return paths }, _render: function(url, reload) { var isClick = url instanceof jQuery; var self = this, opts = self._options, href = isClick ? url.attr('href') : url, router = self.constructor; if (href) { var trigger = false; var change = function(callback) { trigger = true; var hash = '#!' + router._replace(href); var _hash = location.hash; if (typeof callback === 'function') { hash = callback(hash) || hash; } if (reload) { self._isRender = true } if (_hash === hash && self._isRender === true) { router._change() } else { location.hash = hash } } if (typeof opts.onBefore === 'function' && opts.onBefore.call(opts, change, reload) === false) { return false } if (!trigger) { change() } } }, _event: function() { var self = this, opts = self._options;; self._on('click', Nui.doc, self.target, function(e, elem) { self._render(elem, opts.reload); e.preventDefault() }) return self }, _reset: function() { var self = this, opts = self._options, element = opts.element, router = self.constructor; self._off(); if (element) { component.destroy(element.off()); if (self._wrapper) { if (self._wrapper !== element) { self._wrapper.off() } if (opts.wrapper === true) { self._wrapper.remove() } delete self._wrapper; delete self._inner; } } Nui.each(router._paths, function(val, i) { if (val.id === self.__id) { delete router._paths[i]; delete router._request[i]; } }) return self }, render: function() { var self = this, opts = self._options, tmpl = opts.template, element = opts.element; if (element) { self._callback('RenderBefore'); component.destroy(element); if (tmpl) { if (typeof tmpl === 'string') { element.html(template.render(tmpl, opts.data)); } else { element.html(template.render.call(tmpl, tmpl.main, opts.data)); } } component.init(element); self._callback('Render') } }, request: function() { var self = this, _class = self.constructor, args = arguments, type = args[0], method, url; if (type) { if (typeof type === 'string' && request[type]) { args = Array.prototype.slice.call(arguments, 1); url = args[0]; method = request[type] } else if (typeof type === 'object') { url = type.url; method = request } var _request = _class._request[self.__id]; if (!_request) { _request = _class._request[self.__id] = {} } if (method && url) { var xhr = method.apply(request, args); var callback = function() { delete _request[url] } _request[url] = xhr; xhr.then(callback, callback); return xhr } } }, destroy: function() { var self = this, router = self.constructor; component.exports.destroy.call(self); if ($.isEmptyObject(router._paths)) { router._options = {}; //所有路由全部销毁时,删除公共wrapper数据 if (router._wrapper) { router._wrapper.off().remove(); delete router._inner; delete router._wrapper } } } }) })