PageSourceSearch

https://totalcharm.ru/bitrix/js/main/core/core_uploader/file.js

js totalcharm.ru collected 2026-10-08 20:48:51 UTC 38,495 bytes, 1,295 lines download raw bytes

1;(function(window){
2	window.BX = window['BX'] || {};
3	if (window.BX["UploaderFile"])
4		return false;
5	var getOrientation = (function(){
6		var exif = {
7			tags : {
8				// 0x0100 : "ImageWidth",
9				// 0x0101 : "ImageHeight",
10				// 0x8769 : "ExifIFDPointer",
11				// 0x8825 : "GPSInfoIFDPointer",
12				// 0xA005 : "InteroperabilityIFDPointer",
13				// 0x0102 : "BitsPerSample",
14				// 0x0103 : "Compression",
15				// 0x0106 : "PhotometricInterpretation",
16				0x0112 : "Orientation",
17				// 0x0115 : "SamplesPerPixel",
18				// 0x011C : "PlanarConfiguration",
19				// 0x0212 : "YCbCrSubSampling",
20				// 0x0213 : "YCbCrPositioning",
21				// 0x011A : "XResolution",
22				// 0x011B : "YResolution",
23				// 0x0128 : "ResolutionUnit",
24				// 0x0111 : "StripOffsets",
25				// 0x0116 : "RowsPerStrip",
26				// 0x0117 : "StripByteCounts",
27				// 0x0201 : "JPEGInterchangeFormat",
28				// 0x0202 : "JPEGInterchangeFormatLength",
29				// 0x012D : "TransferFunction",
30				// 0x013E : "WhitePoint",
31				// 0x013F : "PrimaryChromaticities",
32				// 0x0211 : "YCbCrCoefficients",
33				// 0x0214 : "ReferenceBlackWhite",
34				// 0x0132 : "DateTime",
35				// 0x010E : "ImageDescription",
36				// 0x010F : "Make",
37				// 0x0110 : "Model",
38				// 0x0131 : "Software",
39				// 0x013B : "Artist",
40				// 0x8298 : "Copyright"
41			},
42			getStringFromDB : function (buffer, start, length) {
43				var outstr = "", n;
44				for (n = start; n < start+length; n++) {
45					outstr += String.fromCharCode(buffer.getUint8(n));
46				}
47				return outstr;
48			},
49			readTags : function(file, tiffStart, dirStart, strings, bigEnd) {
50				var entries = file.getUint16(dirStart, !bigEnd),
51					tags = {},
52					entryOffset, tag,
53					i,
54					l = 0;
55				for (i in strings)
56				{
57					if (strings.hasOwnProperty(i))
58						l++;
59				}
60
61				for (i = 0; i < entries; i++)
62				{
63					entryOffset = dirStart + i*12 + 2;
64					tag = strings[file.getUint16(entryOffset, !bigEnd)];
65					tags[tag] = exif.readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
66					l--;
67					if (l <= 0)
68						break;
69				}
70				return tags;
71			},
72			readTagValue : function(file, entryOffset, tiffStart, dirStart, bigEnd) {
73				var type = file.getUint16(entryOffset+2, !bigEnd),
74					numValues = file.getUint32(entryOffset+4, !bigEnd),
75					valueOffset = file.getUint32(entryOffset+8, !bigEnd) + tiffStart,
76					offset,
77					vals, val, n,
78					numerator, denominator;
79
80				switch (type)
81				{
82					case 1: // byte, 8-bit unsigned int
83					case 7: // undefined, 8-bit byte, value depending on field
84						if (numValues == 1) {
85							return file.getUint8(entryOffset + 8, !bigEnd);
86						} else {
87							offset = numValues > 4 ? valueOffset : (entryOffset + 8);
88							vals = [];
89							for (n=0;n<numValues;n++) {
90								vals[n] = file.getUint8(offset + n);
91							}
92							return vals;
93						}
94					case 2: // ascii, 8-bit byte
95						offset = numValues > 4 ? valueOffset : (entryOffset + 8);
96						return exif.getStringFromDB(file, offset, numValues-1);
97					case 3: // short, 16 bit int
98						if (numValues == 1) {
99							return file.getUint16(entryOffset + 8, !bigEnd);
100						} else {
101							offset = numValues > 2 ? valueOffset : (entryOffset + 8);
102							vals = [];
103							for (n=0;n<numValues;n++) {
104								vals[n] = file.getUint16(offset + 2*n, !bigEnd);
105							}
106							return vals;
107						}
108					case 4: // long, 32 bit int
109						if (numValues == 1) {
110							return file.getUint32(entryOffset + 8, !bigEnd);
111						} else {
112							vals = [];
113							for (n=0;n<numValues;n++) {
114								vals[n] = file.getUint32(valueOffset + 4*n, !bigEnd);
115							}
116							return vals;
117						}
118					case 5:    // rational = two long values, first is numerator, second is denominator
119						if (numValues == 1) {
120							numerator = file.getUint32(valueOffset, !bigEnd);
121							denominator = file.getUint32(valueOffset+4, !bigEnd);
122							val = new Number(numerator / denominator);
123							val.numerator = numerator;
124							val.denominator = denominator;
125							return val;
126						} else {
127							vals = [];
128							for (n=0;n<numValues;n++) {
129								numerator = file.getUint32(valueOffset + 8*n, !bigEnd);
130								denominator = file.getUint32(valueOffset+4 + 8*n, !bigEnd);
131								vals[n] = new Number(numerator / denominator);
132								vals[n].numerator = numerator;
133								vals[n].denominator = denominator;
134							}
135							return vals;
136						}
137					case 9: // slong, 32 bit signed int
138						if (numValues == 1) {
139							return file.getInt32(entryOffset + 8, !bigEnd);
140						} else {
141							vals = [];
142							for (n=0;n<numValues;n++) {
143								vals[n] = file.getInt32(valueOffset + 4*n, !bigEnd);
144							}
145							return vals;
146						}
147					case 10: // signed rational, two slongs, first is numerator, second is denominator
148						if (numValues == 1) {
149							return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset+4, !bigEnd);
150						} else {
151							vals = [];
152							for (n=0;n<numValues;n++) {
153								vals[n] = file.getInt32(valueOffset + 8*n, !bigEnd) / file.getInt32(valueOffset+4 + 8*n, !bigEnd);
154							}
155							return vals;
156						}
157				}
158			},
159			readData : function (file, start) {
160				if (exif.getStringFromDB(file, start, 4) != "Exif")
161				{
162					return false;
163				}
164
165				var bigEnd,
166					tiffOffset = start + 6;
167
168				// test for TIFF validity and endianness
169				if (file.getUint16(tiffOffset) == 0x4949)
170				{
171					bigEnd = false;
172				}
173				else if (file.getUint16(tiffOffset) == 0x4D4D)
174				{
175					bigEnd = true;
176				}
177				else
178				{
179					return false;
180				}
181
182				if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A)
183				{
184					return false;
185				}
186
187				var firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd);
188
189				if (firstIFDOffset < 0x00000008)
190				{
191					return false;
192				}
193
194				return exif.readTags(file, tiffOffset, tiffOffset + firstIFDOffset, exif.tags, bigEnd);
195			},
196			readBase64 : function (base64)
197			{
198				base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
199				var binary_string =  window.atob(base64), //decode base64
200					len = binary_string.length,
201					bytes = new Uint8Array(len);
202				for (var i = 0; i < len; i++) {
203					bytes[i] = binary_string.charCodeAt(i);
204				}
205				var dataView = new DataView(bytes.buffer);
206				if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8))
207				{
208					return false; // not a valid jpeg
209				}
210
211				var offset = 2,
212					length = bytes.buffer.byteLength,
213					marker,
214					result = false;
215				while (offset < length)
216				{
217					if (dataView.getUint8(offset) != 0xFF) {
218						break; // not a valid marker, something is wrong
219					}
220
221					marker = dataView.getUint8(offset + 1);
222
223					// we could implement handling for other markers here,
224					// but we're only looking for 0xFFE1 for EXIF data
225
226					if (marker == 225)
227					{
228						result = exif.readData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
229						break;
230					}
231					else
232					{
233						offset += 2 + dataView.getUint16(offset+2);
234					}
235				}
236				return result;
237			}
238		};
239		return function(base64){
240			if (BX.type.isString(base64))
241			{
242				try {
243					var tags = exif.readBase64(base64);
244					if(tags && tags["Orientation"])
245						return tags["Orientation"];
246				}
247				catch (e)
248				{
249				}
250			}
251			return false;
252		};
253	})(),
254		setOrientation = function(image, cnv, ctx, exifOrientation) {
255			var width = image.width,
256				height = image.height;
257			if ([5,6,7,8].indexOf(exifOrientation) >= 0)
258			{
259				width = image.height;
260				height = image.width;
261			}
262
263			BX.adjust(cnv, {props: {width: width, height: height}});
264
265			ctx.save();
266			switch(exifOrientation) {
267				case 2:
268					// $img.addClass('flip');
269					ctx.scale(-1, 1);
270					ctx.translate(-cnv.width, 0);
271					break;
272				case 3:
273					// $img.addClass('rotate-180');
274					ctx.translate(cnv.width, cnv.height);
275					ctx.rotate(Math.PI);
276					break;
277				case 4:
278					// $img.addClass('flip-and-rotate-180');
279					ctx.scale(-1, 1);
280					ctx.translate(0, cnv.height);
281					ctx.rotate(Math.PI);
282					break;
283				case 5:
284					// $img.addClass('flip-and-rotate-90');
285					ctx.scale(-1, 1);
286					ctx.translate(0, 0);
287					ctx.rotate(Math.PI / 2);
288					break;
289				case 6:
290					// $img.addClass('rotate-90');
291					ctx.translate(cnv.width, 0);
292					ctx.rotate(Math.PI / 2);
293					break;
294				case 7:
295					// $img.addClass('flip-and-rotate-90');
296					ctx.scale(-1, 1);
297					ctx.translate(-cnv.width, cnv.height);
298					ctx.rotate(Math.PI * 3 / 2);
299					break;
300				case 8:
301					// $img.addClass('rotate-270');
302					ctx.translate(0, cnv.height);
303					ctx.rotate(Math.PI * 3 / 2);
304					break;
305			}
306			ctx.drawImage(image, 0, 0);
307			ctx.restore();
308		};
309	var BX = window.BX,
310		statuses = { "new" : 0, ready : 1, preparing : 2, inprogress : 3, done : 4, failed : 5, stopped : 6, changed : 7, uploaded : 8},
311		cnvConstr = (function(){
312			var cnvConstructor = function(timelimit) {
313				this.timeLimit = (typeof timelimit === "number" && timelimit > 0 ? timelimit : 50);
314				this.status = statuses.ready;
315				this.queue = new BX.UploaderUtils.Hash();
316				this.id = (new Date()).getTime();
317			};
318			cnvConstructor.prototype = {
319				counter : 0,
320				active : null,
321				image : null,
322				getImage : function() {
323					if (!this.image)
324						this.image = new Image();
325					return this.image;
326				},
327				canvas : null,
328				getCanvas : function() {
329					if (!this.canvas)
330					{
331						this.canvas = BX.create('CANVAS', {style : {display: "none"}});
332						document.body.appendChild(this.canvas);
333					}
334
335					return this.canvas;
336				},
337				context : null,
338				getContext : function() {
339					if (!this.context && this.getCanvas()["getContext"])
340						this.context = this.getCanvas().getContext('2d');
341					return this.context;
342				},
343				reader : null,
344				getReader : function() {
345					if (!this.reader && window["FileReader"])
346						this.reader = new FileReader();
347					return this.reader;
348				},
349				load : function(file, callback, id, callbackFail) {
350					if (this.active !== null || (this.getReader() && this.getReader().readyState == 1))
351						return;
352
353					this.counter++;
354					this.active = file;
355					var image = this.getImage();
356					BX.unbindAll(image);
357					image.onload = function(){};
358					image.onerror = function(){};
359
360					/* Almost all browsers cache images from local resource except of FF on 06.03.2017. It appears that
361					FF collect src and does not abort image uploading when src is changed. And we had a bug when in
362					onload event we got e.target.src of one element but source of image was from '/bitrix/images/1.gif'. */
363					// TODO check if chrome and other browsers cache local files for now. If it does not then delete next 2 strings
364					if (!BX.browser.IsFirefox())
365						image.src = '/bitrix/images/1.gif';
366
367					/** For Garbage collector */
368					this.onload = null;
369					delete this.onload;
370					this.onerror = null;
371					delete this.onerror;
372
373					this.onload = BX.delegate(function(e){
374						if (e && e.target && e.target.src && e.target.src.substr(-20) == "/bitrix/images/1.gif")
375							return;
376						if (!!callback)
377						{
378							try {
379								callback(BX.proxy_context, this.getCanvas(), this.getContext(), getOrientation((((e && e.target && e.target.src) ? e.target.src : (BX.proxy_context || null)))));
380							}
381							catch (e)
382							{
383								BX.debug(e);
384							}
385						}
386						if (!!id)
387						{
388							this.queue.removeItem(id);
389							setTimeout(BX.proxy(function() {
390								this.active = null;
391								this.exec();
392							}, this), this.timeLimit);
393						}
394						else
395							this.active = null;
396					}, this);
397					this.onerror = BX.delegate(function(){
398						if (!!callbackFail)
399						{
400							try
401							{
402								callbackFail(BX.proxy_context);
403							}
404							catch (e)
405							{
406								BX.debug(e);
407							}
408						}
409						if (!!id)
410						{
411							this.queue.removeItem(id);
412							setTimeout(BX.proxy(function() {
413								this.active = null;
414								this.exec();
415							}, this), this.timeLimit);
416						}
417						else
418							this.active = null;
419					}, this);
420
421					image.name = file.name;
422
423					image.onload = this.onload;
424					image.onerror = this.onerror;
425
426					var res = Object.prototype.toString.call(file);
427					if (file["tmp_url"])
428					{
429						image.src = file["tmp_url"] + (file["tmp_url"].indexOf("?") > 0 ? '&' : '?') + 'imageUploader' + this.id + this.counter;
430					}
431					else if (res !== '[object File]' && res !== '[object Blob]')
432					{
433						this.onerror(null);
434					}
435					else if (window["URL"])
436					{
437						image.src = window["URL"]["createObjectURL"](file);
438					}
439					else if (this.getReader() !== null)
440					{
441						this.__readerOnLoad = null;
442						delete this.__readerOnLoad;
443						this.__readerOnLoad = BX.delegate(function(e) {
444							this.__readerOnLoad = null;
445							delete this.__readerOnLoad;
446							image.src = e.target.result;
447						}, this);
448						this.getReader().onloadend = this.__readerOnLoad;
449						this.getReader().onerror = BX.proxy(function(e) { this.onerror(null); }, this);
450						this.getReader().readAsDataURL(file);
451					}
452				},
453				push : function(file, callback, failCallback) {
454					var id = BX.UploaderUtils.getId();
455					this.queue.setItem(id, [id, file, callback, failCallback]);
456					this.exec();
457				},
458				exec : function() {
459					var item = this.queue.getFirst();
460					if (!!item)
461						this.load(item[1], item[2], item[0], item[3]);
462				},
463				pack : function(fileType) {
464					return  BX.UploaderUtils.dataURLToBlob(this.getCanvas().toDataURL(fileType));
465				}
466			};
467			return cnvConstructor;
468		})();
469	BX.UploaderFileCnvConstr = cnvConstr;
470	BX.UploaderFileFileLoader = (function(){
471		var d = function(timelimit) {
472			this.timeLimit = (typeof timelimit === "number" && timelimit > 0 ? timelimit : 50);
473			this.status = statuses.ready;
474			this.queue = new BX.UploaderUtils.Hash();
475			this._exec = BX.delegate(this.exec, this);
476		};
477		d.prototype = {
478			xhr : null,
479			goToNext : function(id)
480			{
481				delete this.xhr;
482				this.xhr = null;
483				this.queue.removeItem(id);
484				this.status = statuses.ready;
485				setTimeout(this._exec, this.timeLimit);
486			},
487			load : function(id, path, onsuccess, onfailure)
488			{
489				if (this.status != statuses.ready)
490					return;
491				this.status = statuses.inprogress;
492				var _this = this;
493				this.xhr = BX.ajax({
494					'method': 'GET',
495					'data' : '',
496					'url': path,
497					'onsuccess': function(blob){if (blob === null){onfailure(blob);} else {onsuccess(blob);} _this.goToNext(id);},
498					'onfailure': function(blob){onfailure(blob); _this.goToNext(id);},
499					'start': false,
500					'preparePost':false,
501					'processData':false
502				});
503				this.xhr.withCredentials = true;
504				this.xhr.responseType = 'blob';
505
506				this.xhr.send();
507			},
508			push : function(path, onsuccess, onfailure)
509			{
510				var id = BX.UploaderUtils.getId();
511				this.queue.setItem(id, [id, path, onsuccess, onfailure]);
512				this.exec();
513			},
514			exec : function()
515			{
516				var item = this.queue.getFirst();
517				if (!!item)
518					this.load(item[0], item[1], item[2], item[3]);
519			}
520		};
521		return d;
522	})();
523	var prvw = new cnvConstr(), upld = new cnvConstr(), edtr = new cnvConstr(), canvas = BX.create('CANVAS'), ctx;
524	/**
525	 * @return {BX.UploaderFile}
526	 * @file file
527	 * @params array
528	 * @limits array
529	 * @caller {BX.Uploader}
530	 * You should work with params["fields"] in case you want to change visual part
531	 */
532	var mobileNames = {};
533	BX.UploaderFile = function (file, params, limits, caller)
534	{
535		this.dialogName = (this.dialogName ? this.dialogName : "BX.UploaderFile");
536		this.file = file;
537		this.id = (file['id'] || 'file' + BX.UploaderUtils.getId());
538		this.name = file.name;
539		this.isNode = false;
540		if (BX.type.isDomNode(file))
541		{
542			this.isNode = true;
543			this.name = BX.UploaderUtils.getFileNameOnly(file.value);
544			if (/\[(.+?)\]/.test(file.name))
545			{
546				var tmp = /\[(.+?)\]/.exec(file.name);
547				this.id = tmp[1];
548			}
549			this.file.bxuHandler = this;
550		}
551		else if (file["tmp_url"] && !file["name"])
552		{
553			this.name = BX.UploaderUtils.getFileNameOnly(file["tmp_url"]);
554		}
555		this.preview = '<span id="' + this.id + 'Canvas" class="bx-bxu-canvas"></span>';
556		this.nameWithoutExt = (this.name.lastIndexOf('.') > 0 ? this.name.substr(0, this.name.lastIndexOf('.')) : this.name);
557		this.ext = this.name.substr(this.nameWithoutExt.length + 1);
558
559		if (/iPhone|iPad|iPod/i.test(navigator.userAgent) && this.nameWithoutExt == "image")
560		{
561			var nameWithoutExt = 'mobile_' + BX.date.format("Ymd_His");
562			mobileNames[nameWithoutExt] = (mobileNames[nameWithoutExt] || 0);
563			this.nameWithoutExt = nameWithoutExt + (mobileNames[nameWithoutExt] > 0 ? ("_" + mobileNames[nameWithoutExt]) : "");
564			this.name = this.nameWithoutExt + (BX.type.isNotEmptyString(this.ext) ? ("." + this.ext) : "");
565			mobileNames[nameWithoutExt]++;
566		}
567
568		this.size = '';
569		if (file.size)
570			this.size = BX.UploaderUtils.getFormattedSize(file.size, 0);
571		this.type = file.type;
572		this.status = statuses["new"];
573		this.limits = limits;
574		this.caller = caller;
575		this.fields = {
576			thumb : {
577				tagName : 'SPAN',
578				template : '<div class="someclass">#preview#<div>#name#</div>',
579				editorTemplate : '<div class="someeditorclass"><div>#name#</div>',
580				className : "bx-bxu-thumb-thumb",
581				placeHolder : null
582			},
583			preview : {
584				params : { width : 400, height : 400 },
585				template : "#preview#",
586				editorParams : { width : 1024, height : 860 },
587				editorTemplate : '<span>#preview#</span>',
588				className : "bx-bxu-thumb-preview",
589				placeHolder : null,
590				events : {
591					click : BX.delegate(this.clickFile, this)
592				},
593				type : "html"
594			},
595			name : {
596				template : "#name#",
597				editorTemplate : '<span><input type="text" name="name" value="#name#" /></span>',
598				className : "bx-bxu-thumb-name",
599				placeHolder : null
600			},
601			type : {
602				template : "#type#",
603				editorTemplate : '#type#',
604				className : "bx-bxu-thumb-type",
605				placeHolder : null
606			}
607		};
608
609		if (!!params["fields"])
610		{
611			var ij, key;
612			for (var ii in params["fields"])
613			{
614				if (params["fields"].hasOwnProperty(ii))
615				{
616					if (!!this.fields[ii])
617					{
618						for (ij in params["fields"][ii])
619						{
620							if (params["fields"][ii].hasOwnProperty(ij))
621							{
622								this.fields[ii][ij] = params["fields"][ii][ij];
623							}
624						}
625					}
626					else
627						this.fields[ii] = params["fields"][ii];
628					key = ii + '';
629					if (key.toLowerCase() != "thumb" && key.toLowerCase() != "preview")
630					{
631						this[key.toLowerCase()] = (!!params["fields"][ii]["value"] ? params["fields"][ii]["value"] : "");
632						this.log(key.toLowerCase() + ': ' + this[key.toLowerCase()]);
633					}
634				}
635			}
636		}
637
638		BX.onCustomEvent(this, "onFileIsCreated", [this.id, this, this.caller]);
639		BX.onCustomEvent(this.caller, "onFileIsCreated", [this.id, this, this.caller]);
640
641		this.makePreview();
642		this.preparationStatus = statuses.done;
643		return this;
644	};
645	BX.UploaderFile.prototype = {
646		getId: function()
647		{
648			return this.id;
649		},
650
651		log : function(text)
652		{
653			BX.UploaderUtils.log('file ' + this.name, text);
654		},
655		makeThumb : function()
656		{
657			var template = this.fields.thumb.template, name, ii, events = {}, node, jj;
658			for (ii in this.fields)
659			{
660				if (this.fields.hasOwnProperty(ii))
661				{
662					if (this.fields[ii].template && this.fields[ii].template.indexOf('#' + ii + '#') >= 0)
663					{
664						name = this.id + ii.toUpperCase().substr(0, 1) + ii.substr(1);
665						node = this.setProps(ii, this[ii], true);
666						template = template.replace('#' + ii + '#', '<span id="' + name + '" class="' + this.fields[ii]["className"] + '">' + (
667							BX.type.isNotEmptyString(node.html) ? node.html.replace("#", "<\x19>") : node.html) + '</span>');
668						for (jj in node.events)
669						{
670							if (node.events.hasOwnProperty(jj))
671							{
672								events[jj] = node.events[jj];
673							}
674						}
675						if (!!this.fields[ii].events)
676							events[name] = this.fields[ii].events;
677					}
678				}
679			}
680			var res, patt = [], repl = [], tmp;
681			while ((res = /#([^\\<\\>\\"\\']+?)#/gi.exec(template)) && !!res)
682			{
683				if (this[res[1]] !== undefined)
684				{
685					template = template.replace(res[0], BX.type.isNotEmptyString(this[res[1]]) ? this[res[1]].replace("#", "<\x19>") : this[res[1]]);
686				}
687				else
688				{
689					tmp = "<\x18" + patt.length + ">";
690					patt.push(tmp);
691					repl.push(res[0]);
692					template = template.replace(res[0], tmp);
693				}
694			}
695			while ((res = patt.shift()) && res)
696			{
697				tmp = repl.shift();
698				template = template.replace(res, tmp);
699			}
700			template = template.replace("<\x19>", "#");
701			if (!!this.fields.thumb.tagName)
702			{
703				res = BX.create(this.fields.thumb.tagName, {
704					attrs : {
705						id : (this.id + 'Thumb'),
706						className : this.fields.thumb.className
707					},
708					events : this.fields.thumb.events,
709					html : template
710					}
711				);
712			}
713			else
714			{
715				res = template;
716			}
717			this.__makeThumbEventsObj = events;
718			this.__makeThumbEvents = BX.delegate(function()
719			{
720				var ii, jj;
721				for (ii in events)
722				{
723					if (events.hasOwnProperty(ii) && BX(ii))
724					{
725						for (jj in events[ii])
726						{
727							if (events[ii].hasOwnProperty(jj))
728							{
729								BX.bind(BX(ii), jj, events[ii][jj]);
730							}
731						}
732					}
733				}
734				this.__makeThumbEvents = null;
735				delete this.__makeThumbEvents;
736			}, this);
737			BX.addCustomEvent(this, "onFileIsAppended", this.__makeThumbEvents);
738
739			if (BX.type.isDomNode(this.file))
740			{
741				if (BX.type.isString(template))
742				{
743					this.__bindFileNode = BX.delegate(function(id)
744					{
745						var node = BX(id + 'Item');
746						if (node.tagName == 'TR')
747							node.cells[0].appendChild(this.file);
748						else if (node.tagName == 'TABLE')
749							node.rows[0].cells[0].appendChild(this.file);
750						else
751							BX(id + 'Item').appendChild(this.file);
752						this.__bindFileNode = null;
753						delete this.__bindFileNode;
754					}, this);
755					BX.addCustomEvent(this, "onFileIsAppended", this.__bindFileNode);
756				}
757				else
758				{
759					res.appendChild(this.file);
760				}
761			}
762			return res;
763		},
764		checkProps : function()
765		{
766			var el2 = BX.UploaderUtils.FormToArray({elements : [BX.proxy_context]}), ii;
767			for (ii in el2.data)
768			{
769				if (el2.data.hasOwnProperty(ii))
770					this[ii] = el2.data[ii];
771			}
772		},
773		setProps : function(name, val, bReturn)
774		{
775			if (typeof name == "string")
776			{
777				if (name == "size")
778					val = BX.UploaderUtils.getFormattedSize(this.file.size, 0);
779				if (typeof this[name] != "undefined" && typeof this.fields[name] != "undefined")
780				{
781					this[name] = val;
782					var template = this.fields[name].template.
783							replace('#' + name + '#', this.fields[name]["type"] === "html" ? (val || '') : BX.util.htmlspecialchars(val || '')).
784							replace(/#id#/gi, this.id),
785						fii, fjj, el, result = {html : template, events : {}};
786
787					this.hiddenForm = (!!this.hiddenForm ? this.hiddenForm : BX.create("FORM", { style : { display : "none" } } ));
788					this._checkProps = (!!this._checkProps ? this._checkProps : BX.delegate(this.checkProps, this));
789					this.hiddenForm.innerHTML = template;
790					if (this.hiddenForm.elements.length > 0)
791					{
792						for (fii = 0; fii < this.hiddenForm.elements.length; fii++)
793						{
794							el = this.hiddenForm.elements[fii];
795							if (typeof this[el.name] != "undefined")
796							{
797								if (!el.hasAttribute("id"))
798									el.setAttribute("id", this.id + name + BX.UploaderUtils.getId());
799								result.events[el.id] = {
800									blur : this._checkProps
801								}
802
803							}
804						}
805						result.html = this.hiddenForm.innerHTML;
806					}
807					if (BX(this.hiddenForm))
808						BX.remove(this.hiddenForm);
809					this.hiddenForm = null;
810					delete this.hiddenForm;
811					if (bReturn)
812						return result;
813					var node = this.getPH(name);
814					if (!!node)
815					{
816						node.innerHTML = result.html;
817						for (fii in result.events)
818						{
819							if (result.events.hasOwnProperty(fii))
820							{
821								for (fjj in result.events[fii])
822								{
823									if (result.events[fii].hasOwnProperty(fjj))
824									{
825										BX.bind(BX(fii), fjj, result.events[fii][fjj]);
826									}
827								}
828							}
829						}
830					}
831				}
832			}
833			else if (!!name)
834			{
835				for (var ij in name)
836				{
837					if (name.hasOwnProperty(ij))
838					{
839						if (this.fields.hasOwnProperty(ij) && ij !== "preview")
840							this.setProps(ij, name[ij]);
841					}
842				}
843			}
844			return true;
845		},
846		getProps : function(name)
847		{
848			if (name == "canvas")
849			{
850				return BX(this.id + "ProperCanvas");
851			}
852			else if (typeof name == "string")
853			{
854				return this[name];
855			}
856			var data = {};
857			for (var ii in this.fields)
858			{
859				if (this.fields.hasOwnProperty(ii) && (ii !== "preview" && ii !== "thumb"))
860				{
861					data[ii] = this[ii];
862				}
863			}
864			data["size"] = this.file["size"];
865			data["type"] = this["type"];
866			if (!!this.copies)
867			{
868				var copy;
869				data["canvases"] = {};
870				while ((copy = this.copies.getNext()) && !!copy)
871				{
872					data["canvases"][copy.id] = { width : copy.width, height : copy.height, name : copy.id };
873				}
874			}
875			return data;
876		},
877		getThumbs : function()
878		{
879			return null;
880		},
881		getPH : function(name)
882		{
883			name = (typeof name === "string" ? name : "");
884			name = name.toLowerCase();
885			if (this.fields.hasOwnProperty(name))
886			{
887				var id = name.substr(0, 1).toUpperCase() + name.substr(1);
888				this.fields[name]["placeHolder"] = BX(this.id  + id);
889				return this.fields[name]["placeHolder"];
890			}
891			return null;
892		},
893		clickFile : function ()
894		{
895			return false;
896		},
897		makePreview: function()
898		{
899			this.status = statuses.ready;
900			BX.onCustomEvent(this, "onFileIsInited", [this.id, this, this.caller]);
901			BX.onCustomEvent(this.caller, "onFileIsInited", [this.id, this, this.caller]);
902
903			this.log('is initialized as a file');
904		},
905		preparationStatus : statuses.ready,
906		deleteFile: function()
907		{
908			var ii, events = this.__makeThumbEventsObj;
909			for (ii in this.fields)
910			{
911				if (this.fields.hasOwnProperty(ii))
912				{
913					if (!!this.fields[ii]["placeHolder"])
914					{
915						this.fields[ii]["placeHolder"] = null;
916						BX.unbindAll(this.fields[ii]["placeHolder"]);
917						delete this.fields[ii]["placeHolder"];
918					}
919				}
920			}
921
922			for (ii in events)
923			{
924				if (events.hasOwnProperty(ii) && BX(ii))
925				{
926					BX.unbindAll(BX(ii));
927				}
928			}
929
930			var removedFile = this.file;
931			this.file = null;
932			delete this.file;
933
934			BX.remove(this.canvas);
935			this.canvas = null;
936			delete this.canvas;
937
938			BX.onCustomEvent(this.caller, "onFileIsDeleted", [this.id, this, this.caller, removedFile]);
939			BX.onCustomEvent(this, "onFileIsDeleted", [this, this.caller]);
940		}
941	};
942	BX.UploaderImage = function(file, params, limits, caller)
943	{
944		this.dialogName = "BX.UploaderImage";
945		BX.UploaderImage.superclass.constructor.apply(this, arguments);
946		this.isImage = true;
947		this.copies = new BX.UploaderUtils.Hash();
948		this.caller = caller;
949
950		if (!this.isNode && BX.Uploader.getInstanceName() == "BX.Uploader")
951		{
952			if (!!params["copies"])
953			{
954				var copies = params["copies"], copy;
955				for (var ii in copies)
956				{
957					if (copies.hasOwnProperty(ii) && !!copies[ii])
958					{
959						copy = { width : parseInt(copies[ii]['width']), height : parseInt(copies[ii]["height"]), id : ii };
960						if (copy['width'] > 0 && copy["height"] > 0)
961						{
962							this.copies.setItem(ii, copy);
963						}
964					}
965				}
966			}
967			this.preparationStatus = statuses["new"];
968			BX.addCustomEvent(this, "onFileHasToBePrepared", BX.delegate(function()
969			{
970				this.preparationStatus = statuses.inprogress;
971				if (this.status !== statuses["new"])
972				{
973					upld.push(this.file, BX.delegate(this.makeCopies, this));
974				}
975			}, this));
976			BX.addCustomEvent(this, "onUploadDone", BX.delegate(function()
977			{
978				var copy;
979				while ((copy = this.copies.getNext()) && !!copy)
980				{
981					copy.file = null;
982					delete copy.file;
983				}
984				this.preparationStatus = statuses["new"];
985			}, this));
986			this.canvas = BX.create('CANVAS', {attrs : { id : this.id + "ProperCanvas" } } );
987		}
988		else
989		{
990			this.preparationStatus = statuses.done;
991			this.canvas = null;
992		}
993		return this;
994	};
995	BX.extend(BX.UploaderImage, BX.UploaderFile);
996	BX.UploaderImage.prototype.makePreviewImageWork = function(image, cnv, ctx, exifOrientation)
997	{
998		exifOrientation = parseInt(exifOrientation);
999
1000		var result = null,
1001			width = cnv.width,
1002			height = cnv.height;
1003
1004		if (this.file)
1005		{
1006			this.file.width = cnv.width;
1007			this.file.height = cnv.height;
1008		}
1009
1010		if (!!this.canvas)
1011		{
1012			setOrientation(image, cnv, ctx, exifOrientation);
1013			if (this.file)
1014			{
1015				this.file.width = cnv.width;
1016				this.file.height = cnv.height;
1017				if (exifOrientation)
1018				{
1019					this.file.exif = {
1020						Orientation : exifOrientation
1021					}
1022				}
1023			}
1024			this.applyFile(cnv, false);
1025			result = this.canvas;
1026		}
1027		else if (BX(this.id + 'Canvas'))
1028		{
1029			var res2 = BX.UploaderUtils.scaleImage({width : width, height : height}, this.fields.preview.params),
1030				props = {
1031					props : { width : res2.destin.width, height : res2.destin.height, src : image.src },
1032					attrs : {
1033						className : (this.file.width > this.file.height ? "landscape" : "portrait")
1034					}
1035				};
1036			switch (exifOrientation)
1037			{
1038				case 2:
1039					props.attrs.className += ' flip'; break;
1040				case 3:
1041					props.attrs.className += ' rotate-180'; break;
1042				case 4:
1043					props.attrs.className += ' flip-and-rotate-180'; break;
1044				case 5:
1045					props.attrs.className += ' flip-and-rotate-270'; break;
1046				case 6:
1047					props.attrs.className += ' rotate-90'; break;
1048				case 7:
1049					props.attrs.className += ' flip-and-rotate-90'; break;
1050				case 8:
1051					props.attrs.className += ' rotate-270'; break;
1052			}
1053			result = BX.create("IMG", props);
1054		}
1055
1056		BX.onCustomEvent(this, "onFileCanvasIsLoaded", [this.id, this, this.caller, image]);
1057		BX.onCustomEvent(this.caller, "onFileCanvasIsLoaded", [this.id, this, this.caller, image]);
1058
1059		if (BX(this.id + 'Canvas'))
1060			BX(this.id + 'Canvas').appendChild(result);
1061
1062		return result;
1063	};
1064
1065	BX.UploaderImage.prototype.makePreviewImageLoadHandler = function(image, canvas, context, exifOrientation){
1066		this.makePreviewImageWork(image, canvas, context, exifOrientation);
1067		this.status = statuses.ready;
1068
1069		BX.onCustomEvent(this, "onFileIsInited", [this.id, this, this.caller]);
1070		BX.onCustomEvent(this.caller, "onFileIsInited", [this.id, this, this.caller]);
1071		this.log('is initialized as an image with preview');
1072		if (this.preparationStatus == statuses.inprogress)
1073			this.makeCopies(image, canvas, context, exifOrientation);
1074		if (this["_makePreviewImageLoadHandler"])
1075		{
1076			this._makePreviewImageLoadHandler = null;
1077			delete this._makePreviewImageLoadHandler;
1078		}
1079		if (this["_makePreviewImageFailedHandler"])
1080		{
1081			this._makePreviewImageFailedHandler = null;
1082			delete this._makePreviewImageFailedHandler;
1083		}
1084	};
1085
1086	BX.UploaderImage.prototype.makePreviewImageFailedHandler = function(){
1087		this.status = statuses.ready;
1088		this.preparationStatus = statuses.done;
1089
1090		BX.onCustomEvent(this, "onFileIsInited", [this.id, this, this.caller]);
1091		BX.onCustomEvent(this.caller, "onFileIsInited", [this.id, this, this.caller]);
1092
1093		this.log('is initialized without canvas');
1094		if (this["_makePreviewImageLoadHandler"])
1095		{
1096			this._makePreviewImageLoadHandler = null;
1097			delete this._makePreviewImageLoadHandler;
1098		}
1099		if (this["_makePreviewImageFailedHandler"])
1100		{
1101			this._makePreviewImageFailedHandler = null;
1102			delete this._makePreviewImageFailedHandler;
1103		}
1104	};
1105	BX.UploaderImage.prototype.makePreview = function()
1106	{
1107		if (!this.isNode)
1108		{
1109			this._makePreviewImageLoadHandler = BX.delegate(this.makePreviewImageLoadHandler, this);
1110			this._makePreviewImageFailedHandler = BX.delegate(this.makePreviewImageFailedHandler, this);
1111			prvw.push(this.file, this._makePreviewImageLoadHandler, this._makePreviewImageFailedHandler);
1112		}
1113		else
1114		{
1115			this.status = statuses.ready;
1116			BX.onCustomEvent(this, "onFileIsInited", [this.id, this, this.caller]);
1117			BX.onCustomEvent(this.caller, "onFileIsInited", [this.id, this, this.caller]);
1118
1119			this.log('is initialized as an image without preview');
1120			if (this.caller.queue.placeHolder)
1121			{
1122				this._onFileHasGotPreview = BX.delegate(function(id, item) {
1123
1124					BX.removeCustomEvent(this, "onFileHasGotPreview", this._onFileHasGotPreview);
1125					BX.removeCustomEvent(this, "onFileHasNotGotPreview", this._onFileHasNotGotPreview);
1126
1127					this._makePreviewImageLoadHandler = BX.delegate(function(image){
1128						image = this.makePreviewImageWork(image);
1129						BX.onCustomEvent(this, "onFileHasPreview", [item.id, item, image]);
1130						delete this._makePreviewImageLoadHandler;
1131						delete this._makePreviewImageFailedHandler;
1132					}, this);
1133					this._makePreviewImageFailedHandler = BX.delegate(function(image){
1134						delete this._makePreviewImageLoadHandler;
1135						delete this._makePreviewImageFailedHandler;
1136					}, this);
1137					prvw.push({tmp_url : item.file.url}, this._makePreviewImageLoadHandler, this._makePreviewImageFailedHandler);
1138				}, this);
1139				this._onFileHasNotGotPreview = BX.delegate(function(id){
1140					if (id == this.id)
1141					{
1142						BX.removeCustomEvent(this, "onFileHasGotPreview", this._onFileHasGotPreview);
1143						BX.removeCustomEvent(this, "onFileHasNotGotPreview", this._onFileHasNotGotPreview);
1144					}
1145				}, this);
1146				BX.addCustomEvent(this, "onFileHasGotPreview", this._onFileHasGotPreview);
1147				BX.addCustomEvent(this, "onFileHasNotGotPreview", this._onFileHasNotGotPreview);
1148				BX.onCustomEvent(this.caller, "onFileNeedsPreview", [this.id, this, this.caller]);
1149			}
1150		}
1151		return true;
1152	};
1153	BX.UploaderImage.prototype.checkPreview = function()
1154	{
1155		// TODO check preview
1156	};
1157	BX.UploaderImage.prototype.applyFile = function(cnv, params)
1158	{
1159		this.checkPreview();
1160
1161		if (!!params && params.data )
1162			this.setProps(params.data);
1163
1164		var realScale = BX.UploaderUtils.scaleImage(cnv, {width : this.limits["uploadFileWidth"], height : this.limits["uploadFileHeight"]}),
1165			prvwScale = BX.UploaderUtils.scaleImage(cnv, this.fields.preview.params),
1166			prvwProps = {
1167				props : { width : prvwScale.destin.width, height : prvwScale.destin.height },
1168				attrs : {
1169					className : "bx-bxu-proper-canvas"+(prvwScale.destin.width > prvwScale.destin.height ? " landscape" : " portrait")
1170				}
1171			};
1172
1173		if (realScale.bNeedCreatePicture || !!params)
1174		{
1175			BX.adjust(canvas, { props : { width : realScale.destin.width, height : realScale.destin.height } } );
1176			ctx = canvas.getContext('2d');
1177			ctx.drawImage(cnv,
1178				realScale.source.x, realScale.source.y, realScale.source.width, realScale.source.height,
1179				realScale.destin.x, realScale.destin.y, realScale.destin.width, realScale.destin.height
1180			);
1181
1182			var dataURI = canvas.toDataURL(this.file.type);
1183			this.file = BX.UploaderUtils.dataURLToBlob(dataURI);
1184		}
1185
1186		this.file.name = this.name;
1187		this.file.width = realScale.destin.width;
1188		this.file.height = realScale.destin.height;
1189
1190		BX.adjust(this.canvas, prvwProps);
1191
1192		ctx = this.canvas.getContext('2d');
1193		ctx.drawImage(cnv,
1194			prvwScale.source.x, prvwScale.source.y, prvwScale.source.width, prvwScale.source.height,
1195			prvwScale.destin.x, prvwScale.destin.y, prvwScale.destin.width, prvwScale.destin.height
1196		);
1197
1198		ctx = null;
1199		cnv = null;
1200
1201		this.setProps('size');
1202		this.status = statuses.changed;
1203	};
1204	BX.UploaderImage.prototype.clickFile = function()
1205	{
1206		if (!this.canvas || !BX["CanvasEditor"] || this.status == statuses["new"])
1207			return false;
1208		if (!this.__showEditor)
1209		{
1210			this.__showEditor = BX.delegate(this.showEditor, this);
1211			this.eFunc = {
1212				"apply" : BX.delegate(this.applyFile, this),
1213				"delete" : BX.delegate(this.deleteFile, this),
1214				"clear" : BX.delegate(function()
1215				{
1216					BX.removeCustomEvent(editor, "onApplyCanvas", this.eFunc["apply"]);
1217					BX.removeCustomEvent(editor, "onDeleteCanvas", this.eFunc["delete"]);
1218					BX.removeCustomEvent(editor, "onClose", this.eFunc["clear"]);
1219				}, this)
1220			};
1221		}
1222		var template = this.fields.thumb.editorTemplate, name;
1223		for (var ii in this.fields)
1224		{
1225			if (this.fields.hasOwnProperty(ii))
1226			{
1227				name = ii.substr(0, 1).toUpperCase() + ii.substr(1);
1228				template = template.replace('#' + ii + '#',
1229					(ii === "preview" ? "" :
1230						('<span id="' + this.id + name + 'Editor" class="' + this.fields[ii]["className"] + '">' +
1231						this.fields[ii]["editorTemplate"].replace('#' + ii + '#', (!!this[ii] ? BX.util.htmlspecialchars(this[ii]) : '')) + '</span>')));
1232			}
1233		}
1234
1235		BX.adjust(edtr.getCanvas(), { props : { width : this.file.width, height : this.file.height } } );
1236		edtr.getContext().drawImage(this.canvas,
1237			0, 0, this.canvas.width, this.canvas.height,
1238			0, 0, edtr.getCanvas().width, edtr.getCanvas().height);
1239		var editor = BX.CanvasEditor.show(edtr.getCanvas(), {title : this.name, template : template});
1240
1241		BX.addCustomEvent(editor, "onApplyCanvas", this.eFunc["apply"]);
1242		BX.addCustomEvent(editor, "onDeleteCanvas", this.eFunc["delete"]);
1243		BX.addCustomEvent(editor, "onClose", this.eFunc["clear"]);
1244		BX.onCustomEvent(this, "onCanvasEditorIsCreated", [editor, this]);
1245
1246		edtr.push(this.file, this.__showEditor);
1247		this.editor = editor;
1248		return false;
1249	};
1250	BX.UploaderImage.prototype.showEditor = function(image, canvas, context, exifOrientation)
1251	{
1252		BX.adjust(canvas, { props : { width : this.file.width, height : this.file.height } } );
1253		setOrientation(image, canvas, context, exifOrientation);
1254		this.editor.copyCanvas(canvas);
1255	};
1256	BX.UploaderImage.prototype.makeCopies = function(image, cnv, ctx, exifOrientation)
1257	{
1258		var copy, res, dataURI, result,
1259			context = canvas.getContext('2d');
1260		setOrientation(image, canvas, context, exifOrientation);
1261		while ((copy = this.copies.getNext()) && !!copy)
1262		{
1263			res = BX.UploaderUtils.scaleImage(canvas, copy);
1264			BX.adjust(cnv, {props : { width : res.destin.width, height : res.destin.height } } );
1265			ctx.drawImage(canvas,
1266				res.source.x, res.source.y, res.source.width, res.source.height,
1267				res.destin.x, res.destin.y, res.destin.width, res.destin.height
1268			);
1269
1270			dataURI = cnv.toDataURL(this.file.type);
1271			result = BX.UploaderUtils.dataURLToBlob(dataURI);
1272			result.width = cnv.width;
1273			result.height = cnv.height;
1274			result.name = this.name;
1275			result.thumb = copy.id;
1276			result.canvases = this.copies.length;
1277			result.canvas = this.copies.pointer - 1;
1278			copy.file = result;
1279		}
1280		this.preparationStatus = statuses.done;
1281		BX.onCustomEvent(this, 'onFileIsPrepared');
1282	};
1283	BX.UploaderImage.prototype.getThumbs = function(name)
1284	{
1285		if (name == "getCount")
1286			return this.copies.length;
1287
1288		var res = (typeof name == "string" ? this.copies.getItem(name) : this.copies.getNext());
1289
1290		if (!!res)
1291			return res.file;
1292		return null;
1293	};
1294	return true;
1295}(window));

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.