JavaScript设计模式-21.命令模式
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>javascript高级语法21-命令模式</title> 6 </head> 7 <body> 8 <input type="text" id="flow" /> 9 <input type="button" id="" value="添加新流程" onclick="API.addFlow()" /><br> 10 <input type="button" value="回退" onclick="API.re()" /> 11 <input type="button" value="重做" onclick="API.again()" /> 12 <div id="div01"></div> 13 14 <script id="uuid.js"> 15 //生成uuid的轮子 16 /* 17 uuid.js - Version 0.2 18 JavaScript Class to create a UUID like identifier 19 */ 20 21 // On creation of a UUID object, set it's initial value 22 function UUID(){ 23 this.id = this.createUUID(); 24 } 25 // When asked what this Object is, lie and return it's value 26 UUID.prototype.valueOf = function(){ return this.id; } 27 UUID.prototype.toString = function(){ return this.id; } 28 UUID.prototype.createUUID = function(){ 29 var dg = new Date(1582, 10, 15, 0, 0, 0, 0); 30 var dc = new Date(); 31 var t = dc.getTime() - dg.getTime(); 32 var h = '-'; 33 var tl = UUID.getIntegerBits(t,0,31); 34 var tm = UUID.getIntegerBits(t,32,47); 35 var thv = UUID.getIntegerBits(t,48,59) + '1'; // version 1, security version is 2 36 var csar = UUID.getIntegerBits(UUID.rand(4095),0,7); 37 var csl = UUID.getIntegerBits(UUID.rand(4095),0,7); 38 var n = UUID.getIntegerBits(UUID.rand(8191),0,7) + 39 UUID.getIntegerBits(UUID.rand(8191),8,15) + 40 UUID.getIntegerBits(UUID.rand(8191),0,7) + 41 UUID.getIntegerBits(UUID.rand(8191),8,15) + 42 UUID.getIntegerBits(UUID.rand(8191),0,15); // this last number is two octets long 43 return tl + h + tm + h + thv + h + csar + csl + h + n; 44 } 45 46 UUID.getIntegerBits = function(val,start,end){ 47 var base16 = UUID.returnBase(val,16); 48 var quadArray = new Array(); 49 var quadString = ''; 50 var i = 0; 51 for(i=0;i<base16.length;i++){ 52 quadArray.push(base16.substring(i,i+1)); 53 } 54 for(i=Math.floor(start/4);i<=Math.floor(end/4);i++){ 55 if(!quadArray[i] || quadArray[i] == '') quadString += '0'; 56 else quadString += quadArray[i]; 57 } 58 return quadString; 59 } 60 61 UUID.returnBase = function(number, base){ 62 63 var convert = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']; 64 if (number < base) var output = convert[number]; 65 else { 66 var MSD = '' + Math.floor(number / base); 67 var LSD = number - MSD*base; 68 if (MSD >= base) var output = this.returnBase(MSD,base) + convert[LSD]; 69 else var output = convert[MSD] + convert[LSD]; 70 } 71 return output; 72 } 73 UUID.rand = function(max){ 74 return Math.floor(Math.random() * max); 75 } 76 77 </script> 78 <script id="keymaster.js"> 79 //获取键盘事件的轮子 80 (function(a) { 81 function h(a, b) { 82 var c = a.length; 83 while (c--) if (a[c] === b) return c; 84 return -1 85 } 86 function i(a) { 87 var b, g, i, j, k; 88 b = a.keyCode; 89 if (b == 93 || b == 224) b = 91; 90 if (b in d) { 91 d[b] = !0; 92 for (i in f) f[i] == b && (l[i] = !0); 93 return 94 } 95 if (!l.filter.call(this, a)) return; 96 if (!(b in c)) return; 97 for (j = 0; j < c[b].length; j++) { 98 g = c[b][j]; 99 if (g.scope == e || g.scope == "all") { 100 k = g.mods.length > 0; 101 for (i in d) if (!d[i] && h(g.mods, +i) > -1 || d[i] && h(g.mods, +i) == -1) k = !1; 102 (g.mods.length == 0 && !d[16] && !d[18] && !d[17] && !d[91] || k) && g.method(a, g) === !1 && (a.preventDefault ? a.preventDefault() : a.returnValue = !1, a.stopPropagation && a.stopPropagation(), a.cancelBubble && (a.cancelBubble = !0)) 103 } 104 } 105 } 106 function j(a) { 107 var b = a.keyCode, 108 c; 109 if (b == 93 || b == 224) b = 91; 110 if (b in d) { 111 d[b] = !1; 112 for (c in f) f[c] == b && (l[c] = !1) 113 } 114 } 115 function k() { 116 for (b in d) d[b] = !1; 117 for (b in f) l[b] = !1 118 } 119 function l(a, b, d) { 120 var e, h, i, j; 121 d === undefined && (d = b, b = "all"), a = a.replace(/\s/g, ""), e = a.split(","), e[e.length - 1] == "" && (e[e.length - 2] += ","); 122 for (i = 0; i < e.length; i++) { 123 h = [], a = e[i].split("+"); 124 if (a.length > 1) { 125 h = a.slice(0, a.length - 1); 126 for (j = 0; j < h.length; j++) h[j] = f[h[j]]; 127 a = [a[a.length - 1]] 128 } 129 a = a[0], a = g[a] || a.toUpperCase().charCodeAt(0), a in c || (c[a] = []), c[a].push({ 130 shortcut: e[i], 131 scope: b, 132 method: d, 133 key: e[i], 134 mods: h 135 }) 136 } 137 } 138 function m(a) { 139 var b = (a.target || a.srcElement).tagName; 140 return b != "INPUT" && b != "SELECT" && b != "TEXTAREA" 141 } 142 function n(a) { 143 e = a || "all" 144 } 145 function o() { 146 return e || "all" 147 } 148 function p(a) { 149 var b, d, e; 150 for (b in c) { 151 d = c[b]; 152 for (e = 0; e < d.length;) d[e].scope === a ? d.splice(e, 1) : e++ 153 } 154 } 155 function q(a, b, c) { 156 a.addEventListener ? a.addEventListener(b, c, !1) : a.attachEvent && a.attachEvent("on" + b, function() { 157 c(window.event) 158 }) 159 } 160 var b, c = {}, 161 d = { 162 16: !1, 163 18: !1, 164 17: !1, 165 91: !1 166 }, 167 e = "all", 168 f = { 169 "⇧": 16, 170 shift: 16, 171 "⌥": 18, 172 alt: 18, 173 option: 18, 174 "⌃": 17, 175 ctrl: 17, 176 control: 17, 177 "⌘": 91, 178 command: 91 179 }, 180 g = { 181 backspace: 8, 182 tab: 9, 183 clear: 12, 184 enter: 13, 185 "return": 13, 186 esc: 27, 187 escape: 27, 188 space: 32, 189 left: 37, 190 up: 38, 191 right: 39, 192 down: 40, 193 del: 46, 194 "delete": 46, 195 home: 36, 196 end: 35, 197 pageup: 33, 198 pagedown: 34, 199 ",": 188, 200 ".": 190, 201 "/": 191, 202 "`": 192, 203 "-": 189, 204 "=": 187, 205 ";": 186, 206 "'": 222, 207 "[": 219, 208 "]": 221, 209 "\\": 220 210 }; 211 for (b = 1; b < 20; b++) f["f" + b] = 111 + b; 212 for (b in f) l[b] = !1; 213 q(document, "keydown", i), q(document, "keyup", j), q(window, "focus", k), a.key = l, a.key.setScope = n, a.key.getScope = o, a.key.deleteScope = p, a.key.filter = m, typeof module != "undefined" && (module.exports = key) 214 })(this); 215 </script> 216 <script> 217 /*命令模式: 218 * 用于消除调用者和接收者之间的耦合的模式 219 * 并且可以对(调用这个过程进行留痕操作) 220 * 不能乱用这个模式,它会使简单调用的写法变得非常复杂和难以理解 221 * 当你的业务出现了回退操作,重做操作等需求的时候考虑用这个模式 222 */ 223 /*需求: 224 有一个添加流程的按钮,单机的时候添加一个新的文本当做流程的描述 225 有返回 重做两个按钮,完成相应任务。 226 */ 227 function manager(){ 228 this.addFlow = function(id,text){ 229 //1.得到目标节点 230 var div = document.getElementById("div01"); 231 var newFlow = document.createElement("div"); 232 newFlow.setAttribute("id",id); 233 newFlow.innerHTML = text; 234 div.appendChild(newFlow); 235 } 236 } 237 //为对象建立命令访问库 238 manager.prototype.excute = (function(){ 239 //命令对象 240 return function(command){ 241 return this[command.method](command.id,command.value) 242 } 243 })() 244 //初始化主类 245 var ma = new manager(); 246 //用于存储调用对象命令的集合。 247 var commands = new Array(); 248 //集合的游标 249 var index = commands.length; 250 //客户端 251 252 var API = function(){ 253 this.addFlow = function(){ 254 //把调用封装起来 255 var command = { 256 method:"addFlow", 257 id:new UUID().createUUID(), 258 value:document.getElementById("flow").value, 259 } 260 //把调用对象保存起来,用于回退和重做 261 commands.push(command); 262 //重新定位游标 263 index = commands.length; 264 //调用 265 ma.excute(command); 266 267 } 268 //返回 269 this.re = function(){ 270 if(index-1<0){ 271 alert("已经到最后一步了!") 272 }else{ 273 all = document.getElementById("div01").childNodes; 274 document.getElementById("div01").removeChild(all[all.length-1]); 275 index = index - 1; 276 } 277 } 278 //重做 279 this.again = function(){ 280 if(index>=commands.length){ 281 alert("已经到了最新一步了,不能继续重做了!") 282 }else{ 283 var command = commands[index]; 284 ma.excute(command); 285 index = index + 1; 286 } 287 } 288 } 289 //实例化api 290 var API = new API(); 291 292 //添加支持键盘事件 293 key("ctrl+z",function(){ 294 API.re(); 295 }) 296 key("ctrl+shift+z",function(){ 297 API.again(); 298 }) 299 </script> 300 </body> 301 </html>