1(function () { 2 //Some common utility functions 3 var util = { 4 mixin : function (dest, src) { 5 for (var p in src)dest[p] = src[p]; 6 }, byId : function (id) { 7 if (typeof id == 'string')return document.getElementById(id); else return id; 8 }, create : function (tag, attrs) { 9 var node = document.createElement(tag); 10 this.mixin(node, attrs); 11 return node; 12 }, connect : function (node, evtType, context, callback) { 13 //TODO: use event listeners instead 14 var self = this; 15 16 function handler(evt) { 17 evt = self.fixEvent(evt); 18 context[callback](evt); 19 } 20 21 if (node.attachEvent)node.attachEvent('on' + evtType, handler); else node.addEventListener(evtType, handler, false); 22 }, style : function (node, args) { 23 if (typeof args == 'string') { 24 var value = node.style[args]; 25 if (!value) { 26 s = window.getComputedStyle ? getComputedStyle(node) : node.currentStyle; 27 value = s[args]; 28 } 29 return value; 30 } else this.mixin(node.style, args); 31 }, each : function (arr, callback) { 32 for (var i = 0; i < arr.length; i++) 33 callback(arr[i], i); 34 }, indexOf : function (arr, value) { 35 for (var i = 0; i < arr.length; i++) 36 if (value == arr[i])return i; 37 return -1; 38 }, addCss : function (node, css) { 39 if (!node)return; 40 var cn = node.className || '', arr = cn.split(' '), i = util.indexOf(arr, css); 41 if (i < 0)arr.push(css); 42 node.className = arr.join(' '); 43 }, rmCss : function (node, css) { 44 if (!node)return; 45 var cn = node.className || '', arr = cn.split(' '), i = util.indexOf(arr, css); 46 if (i >= 0)arr.splice(i, 1); 47 node.className = arr.join(' '); 48 }, fixEvent: function (evt) { 49 evt = evt || event; 50 if (!evt.target)evt.target = evt.srcElement; 51 if (!evt.keyCode)evt.keyCode = evt.which || evt.charCode; 52 if (!evt.pageX) {//only for IE 53 evt.pageX = evt.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 54 evt.pageY = evt.clientY + document.body.scrollTop + document.documentElement.scrollTop; 55 } 56 return evt; 57 } 58 }; 59 window.ICropper = function (container, options) { 60 // summary: 61 // Constructor of the Image Cropper, the container could be a dom node or id. 62 container = util.byId(container); 63 if (options.keepSquare) { 64 //keepSquare is deprecated. Should use ratio=1 instead. 65 options.ratio = 1; 66 delete options.keepSquare; 67 } 68 for (var p in options) { 69 if (options[p])this[p] = options[p]; 70 } 71 this.domNode = container || util.create('div'); 72 this._init(); 73 } 74 ICropper.prototype = { 75 76 //The image url 77 image : '' 78 79 //The minimal size of the cropping area 80 , minWidth : 20, minHeight: 20 81 82 //The default gap between crop region border and container border 83 , gap : 50 84 85 //the initial crop region width and height 86 , initialSize : 0 87 88 //whether to keep crop region as a square 89 //DEPRECATED: use ratio=1 instead. 90 , keepSquare : false 91 92 //whether to keep the ratio of width:height, 0 means not set 93 , ratio : 0 94 95 //array: the nodes to show previews of cropped image 96 , preview : null, domNode: null, cropNode: null, imageNode: null 97 98 //Public APIs 99 //------------------------------------------------------------ 100 , setImage : function (url) { 101 // summary: 102 // Set the image to be cropped. The container size will fit the image. 103 var img = new Image(); 104 img.src = url; 105 this.image = url; 106 if (!this.imageNode) { 107 this.imageNode = util.create('img'); 108 this.domNode.appendChild(this.imageNode); 109 var self = this; 110 //TODO: onerror? 111 this.imageNode.onload = function () { 112 self._setSize(this.offsetWidth, this.offsetHeight); 113 } 114 } 115 this.imageNode.src = url; 116 }, bindPreview : function (node) { 117 // summary: 118 // Bind a node as the preview area. e.g: a real size avatar
119 node = util.byId(node); 120 util.style(node, {overflow: 'hidden'}); 121 var width = parseInt(util.style(node, 'width')) 122 , height = parseInt(util.style(node, 'height')); 123 var previewImage = util.create('img', {src: this.image}); 124 node.appendChild(previewImage); 125 var _oldOnChange = this.onChange; 126 var self = this; 127 this.onChange = function (info) { 128 _oldOnChange.call(this, info); 129 var r = info.w / info.h 130 , w2 = height * r 131 , h2 = width / r; 132 if (w2 >= width)w2 = width; 133 if (h2 >= height)h2 = height; 134 util.style(node, {width: w2 + 'px', height: h2 + 'px'}); 135 var rateX = w2 / info.w 136 , rateY = h2 / info.h; 137 util.style(previewImage, { 138 width: info.cw * rateX + 'px', height: info.ch * rateY + 'px', marginLeft: -info.l * rateX + 'px', marginTop: -info.t * rateY + 'px' 139 }); 140 } 141 }, getInfo : function () { 142 // summary: 143 // Get the cropping infomation. Such as being used by server side for real cropping. 144 return { 145 w : this.cropNode.offsetWidth - 2 //2 is hard code border width 146 , h : this.cropNode.offsetHeight - 2, l: parseInt(util.style(this.cropNode, 'left')), t: parseInt(util.style(this.cropNode, 'top')), cw: this.domNode.offsetWidth //container width 147 , ch: this.domNode.offsetHeight //container height 148 }; 149 }, onChange : function () { 150 //Event: 151 // When the cropping size is changed. 152 }, onComplete : function () { 153 //Event: 154 // When mouseup. 155 }, destroy : function () { 156 //TODO: destroy self to release memory 157 } 158 159 //Private APIs 160 //------------------------------------------------------------ 161 , _init : function () { 162 util.addCss(this.domNode, 'icropper'); 163 this._buildRendering(); 164 this._updateUI(); 165 util.connect(this.cropNode, 'mousedown', this, '_onMouseDown'); 166 util.connect(document, 'mouseup', this, '_onMouseUp'); 167 util.connect(document, 'mousemove', this, '_onMouseMove'); 168 this.image && this.setImage(this.image); 169 if (this.preview) { 170 var self = this; 171 util.each(this.preview, function (node) { 172 self.bindPreview(node); 173 }); 174 } 175 }, _buildRendering: function () { 176 this._archors = {}; 177 this._blockNodes = {}; 178 this.cropNode = util.create('div', {className: 'crop-node no-select'}); 179 this.domNode.appendChild(this.cropNode); 180 //Create archors 181 var arr = ['lt', 't', 'rt', 'r', 'rb', 'b', 'lb', 'l']; 182 for (var i = 0; i < 8; i++) { 183 var n = util.create('div', {className: 'archor archor-' + arr[i]}); 184 this.cropNode.appendChild(n); 185 this._archors[arr[i]] = n; 186 } 187 //Create blocks for showing dark areas 188 arr = ['l', 't', 'r', 'b']; 189 for (var i = 0; i < 4; i++) { 190 var n = util.create('div', {className: 'block block-' + arr[i]}); 191 this.domNode.appendChild(n); 192 this._blockNodes[arr[i]] = n; 193 } 194 }, _setSize : function (w, h) { 195 this.domNode.style.width = w + 'px'; 196 this.domNode.style.height = h + 'px'; 197 var w2, h2; 198 if (this.initialSize) { 199// var m = Math.min(w, h, this.initialSize); 200// w2 = h2 = m - 2 + 'px'; 201 if (w > h) { 202 w2 = this.initialSize; 203 if (this.ratio > 1) { 204 h2 = w2 / this.ratio; 205 } else { 206 h2 = w2 * this.ratio; 207 } 208 } else { 209 h2 = this.initialSize; 210 if (this.ratio > 1) { 211 w2 = h2 / this.ratio; 212 } else { 213 w2 = h2 * this.ratio; 214 } 215 } 216 w2 += 'px'; 217 h2 += 'px'; 218 } else { 219 w2 = w - this.gap * 2 - 2; 220 h2 = h - this.gap * 2 - 2; 221 if (this.ratio) { 222 var _w2 = h2 * this.ratio, _h2 = w2 / this.ratio; 223 if (w2 > _w2)w2 = _w2; 224 if (h2 > _h2)h2 = _h2; 225 } 226 w2 += 'px'; 227 h2 += 'px'; 228 } 229 console.log(w2, h2); 230 var s = this.cropNode.style; 231 if (typeof this.defCrop != 'undefined' && this.defCrop != 0) { 232 config = this.defCrop.split(","); 233 s.width = config[2] * w / 100 + "px"; 234 s.height = config[3] * h / 100 + "px"; 235 s.left = config[0] * w / 100 + "px"; 236 s.top = config[1] * h / 100 + "px"; 237 console.log(s.height); 238 } else { 239 s.width = w2; 240 s.height = h2; 241 var l = (w - this.cropNode.offsetWidth) / 2 242 , t = (h - this.cropNode.offsetHeight) / 2; 243 if (l < 0) l = 0; 244 if (t < 0) t = 0; 245 s.left = l + 'px'; 246 s.top = t + 'px'; 247 } 248 this._posArchors(); 249 this._posBlocks(); 250 this.onChange(this.getInfo()); 251 }, _updateUI : function () { 252 this._posArchors(); 253 this._posBlocks(); 254 }, _posArchors : function () { 255 var a = this._archors, w = this.cropNode.offsetWidth, h = this.cropNode.offsetHeight; 256 w = w / 2 - 4 + 'px'; 257 h = h / 2 - 4 + 'px'; 258 a.t.style.left = a.b.style.left = w; 259 a.l.style.top = a.r.style.top = h; 260 }, _posBlocks : function () { 261 var p = this.startedPos, b = this._blockNodes; 262 var l = parseInt(util.style(this.cropNode, 'left')); 263 var t = parseInt(util.style(this.cropNode, 'top')); 264 var w = this.cropNode.offsetWidth; 265 var ww = this.domNode.offsetWidth; 266 var h = this.cropNode.offsetHeight; 267 var hh = this.domNode.offsetHeight; 268 b = this._blockNodes; 269 b.t.style.height = b.l.style.top = b.r.style.top = t + 'px'; 270 b.l.style.height = b.r.style.height = h + 'px'; 271 b.l.style.width = l + 'px';
272 w = ww - w - l; 273 h = hh - h - t; 274 //fix IE 275 if (w < 0)w = 0; 276 if (h < 0)h = 0; 277 b.r.style.width = w + 'px'; 278 b.b.style.height = h + 'px'; 279 }, _onMouseDown : function (e) { 280 var n = this.cropNode, s = n.style; 281 this.dragging = (e.target == n) ? 'move' : e.target.className; 282 if (this.dragging != 'move') { 283 var arr = this.dragging.split(' '); 284 this.dragging = arr.pop().split('-')[1]; 285 } 286 this.startedPos = { 287 x : e.pageX, y: e.pageY, h: n.offsetHeight - 2 //2 is border width 288 , w: n.offsetWidth - 2, l: parseInt(util.style(n, 'left')), t: parseInt(util.style(n, 'top')) 289 } 290 var c = util.style(e.target, 'cursor'); 291 util.style(document.body, { 292 cursor: c 293 }); 294 util.style(this.cropNode, { 295 cursor: c 296 }); 297 util.addCss(document.body, 'no-select'); 298 util.addCss(document.body, 'unselectable');//for IE 299 }, _onMouseUp : function (e) { 300 this.dragging = false; 301 util.style(document.body, { 302 cursor: 'default' 303 }); 304 util.style(this.cropNode, { 305 cursor: 'move' 306 }); 307 util.rmCss(document.body, 'no-select'); 308 util.rmCss(document.body, 'unselectable'); 309 this.onComplete && this.onComplete(this.getInfo()); 310 }, _onMouseMove : function (e) { 311 if (!this.dragging) return; 312 if (this.dragging == 'move') this._doMove(e); else this._doResize(e); 313 this._updateUI(); 314 this.onChange && this.onChange(this.getInfo()); 315 }, _doMove : function (e) { 316 var s = this.cropNode.style, p0 = this.startedPos; 317 var l = p0.l + e.pageX - p0.x; 318 var t = p0.t + e.pageY - p0.y; 319 if (l < 0) l = 0; 320 if (t < 0) t = 0; 321 var maxL = this.domNode.offsetWidth - this.cropNode.offsetWidth; 322 var maxT = this.domNode.offsetHeight - this.cropNode.offsetHeight; 323 if (l > maxL) l = maxL; 324 if (t > maxT) t = maxT; 325 s.left = l + 'px'; 326 s.top = t + 'px' 327 }, _doResize : function (e) { 328 var m = this.dragging 329 , s = this.cropNode.style 330 , cw = this.cropNode.offsetWidth 331 , ch = this.cropNode.offsetHeight 332 , p0 = this.startedPos; 333 //delta x and delta y 334 var dx = e.pageX - p0.x, dy = e.pageY - p0.y; 335 var ratio = this.ratio; 336 if (!ratio && e.shiftKey)ratio = 1;//Shiftkey is only available when there's no ratio set. 337 if (ratio) { 338 if (m == 'l') { 339 dy = dx / ratio; 340 if (p0.l + dx < 0) { 341 dx = -p0.l; 342 dy = dx / ratio; 343 } 344 if (p0.t + dy < 0) { 345 dy = -p0.t; 346 dx = dy * ratio; 347 } 348 m = 'lt'; 349 } else if (m == 'r') { 350 dy = dx / ratio; 351 m = 'rb'; 352 } else if (m == 'b') { 353 dx = dy * ratio; 354 m = 'rb'; 355 } else if (m == 'lt') { 356 dx = Math.abs(dx) > Math.abs(dy) ? dx : dy; 357 dy = dx / ratio; 358 if (p0.l + dx < 0) { 359 dx = -p0.l; 360 dy = dx / ratio; 361 } 362 if (p0.t + dy < 0) { 363 dy = -p0.t; 364 dx = dy * ratio; 365 } 366 } else if (m == 'lb') { 367 dx = Math.abs(dx) > Math.abs(dy) ? dx : -dy; 368 dy = -dx / ratio; 369 if (p0.l + dx < 0) { 370 dx = -p0.l; 371 dy = p0.l; 372 } 373 } else if (m == 'rt' || m == 't') { 374 dx = -dy * ratio; 375 m = 'rt'; 376 if (p0.t + dy < 0) { 377 dy = -p0.t; 378 dx = -dy; 379 } 380 } 381 } 382 if (/l/.test(m)) { 383 dx = Math.min(dx, p0.w - this.minWidth); 384 if (p0.l + dx >= 0) { 385 s.left = p0.l + dx + 'px'; 386 s.width = p0.w - dx + 'px'; 387 } else { 388 s.left = 0; 389 s.width = p0.l + p0.w + 'px'; 390 } 391 } 392 if (/t/.test(m)) { 393 dy = Math.min(dy, p0.h - this.minHeight); 394 if (p0.t + dy >= 0) { 395 s.top = p0.t + dy + 'px'; 396 s.height = p0.h - dy + 'px'; 397 } else { 398 s.top = 0; 399 s.height = p0.t + p0.h + 'px'; 400 } 401 } 402 if (/r/.test(m)) { 403 dx = Math.max(dx, this.minWidth - p0.w); 404 if (p0.l + p0.w + dx <= this.domNode.offsetWidth) { 405 s.width = p0.w + dx + 'px'; 406 } else { 407 s.width = this.domNode.offsetWidth - p0.l - 2 + 'px'; 408 } 409 } 410 if (/b/.test(m)) { 411 dy = Math.max(dy, this.minHeight - p0.h); 412 if (p0.t + p0.h + dy <= this.domNode.offsetHeight) { 413 s.height = p0.h + dy + 'px'; 414 } else { 415 s.height = this.domNode.offsetHeight - p0.t - 2 + 'px'; 416 } 417 } 418 if (ratio) { 419 var w = parseInt(s.width), h = parseInt(s.height); 420 var w2 = h * ratio, h2 = w / ratio; 421 if (w2 < w) { 422 if (/l/.test(m)) { 423 s.left = parseInt(s.left) + w - w2 + 'px'; 424 } 425 w = w2; 426 } 427 if (h2 < h) { 428 if (/t/.test(m)) { 429 s.top = parseInt(s.top) + h - h2 + 'px'; 430 } 431 h = h2; 432 } 433 s.width = w + 'px'; 434 s.height = h + 'px'; 435 } 436 } 437 } 438})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.