From 08f85d421121b09d7bd98951dc7986c21db03585 Mon Sep 17 00:00:00 2001 From: okalintu Date: Mon, 26 Sep 2016 20:20:31 +0300 Subject: [PATCH] sori pojat... --- infoscreen/models.py | 125 +- infoscreen/static/html/abb_create.html | 7 + .../html/generic_external_image_create.html | 11 + .../static/html/generic_image_create.html | 10 + infoscreen/static/html/infoscreen_admin.html | 39 + infoscreen/static/js/infoadmin_controllers.js | 129 + .../static/js/infoscreen_controllers.js | 8 +- .../js/ng-file-upload-bower-12.2.11/.versions | 4 + .../FileAPI.flash.swf | Bin 0 -> 71214 bytes .../ng-file-upload-bower-12.2.11/FileAPI.js | 4313 +++++++++++++++++ .../FileAPI.min.js | 6 + .../js/ng-file-upload-bower-12.2.11/LICENSE | 20 + .../js/ng-file-upload-bower-12.2.11/README.md | 5 + .../ng-file-upload-bower-12.2.11/bower.json | 14 + .../ng-file-upload-all.js | 2888 +++++++++++ .../ng-file-upload-all.min.js | 4 + .../ng-file-upload-shim.js | 421 ++ .../ng-file-upload-shim.min.js | 2 + .../ng-file-upload.js | 2466 ++++++++++ .../ng-file-upload.min.js | 3 + .../ng-file-upload-bower-12.2.11/package.js | 12 + infoscreen/templates/infoscreen_admin.html | 17 + infoscreen/views.py | 110 +- sikweb/urls.py | 26 +- 24 files changed, 10619 insertions(+), 21 deletions(-) create mode 100644 infoscreen/static/html/abb_create.html create mode 100644 infoscreen/static/html/generic_external_image_create.html create mode 100644 infoscreen/static/html/generic_image_create.html create mode 100644 infoscreen/static/html/infoscreen_admin.html create mode 100644 infoscreen/static/js/infoadmin_controllers.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/.versions create mode 100755 infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.flash.swf create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.js create mode 100755 infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.min.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/LICENSE create mode 100755 infoscreen/static/js/ng-file-upload-bower-12.2.11/README.md create mode 100755 infoscreen/static/js/ng-file-upload-bower-12.2.11/bower.json create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-all.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-all.min.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-shim.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-shim.min.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload.min.js create mode 100644 infoscreen/static/js/ng-file-upload-bower-12.2.11/package.js create mode 100644 infoscreen/templates/infoscreen_admin.html diff --git a/infoscreen/models.py b/infoscreen/models.py index edc3869..f459318 100644 --- a/infoscreen/models.py +++ b/infoscreen/models.py @@ -1,5 +1,7 @@ from django.db import models +from django import forms from django.utils import timezone +from datetime import datetime from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType @@ -11,54 +13,120 @@ class InfoItem(models.Model): def get_template_url(self): raise NotImplementedError("inheriting classes must implement get_template_url") - def get_edit_template_url(self): - raise NotImplementedError("inheriting classes must implement get_template_url") + @staticmethod + def get_create_template_url(): + raise NotImplementedError("inheriting classes must implement get_create_template_url") + + @classmethod + def create_from_dict(cls,d): + item = cls() + item.update_from_dict(d) + return item + + def update_from_dict(self,d): + try: + expire_date = d.pop('expire_date', None) + self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S") + except: + pass + dmap = { + 'name': 'name', + } + for k,v in d.items(): + try: + self.__setattr__(dmap[k],v) + except KeyError: + pass + self.save() + def get_dict(self): return { + 'id': self.id, 'name': self.name, + 'item_type': ContentType.objects.get_for_model(self).id, 'template_url': self.get_template_url(), - 'edit_template_url': self.get_edit_template_url(), + 'create_template_url': self.get_create_template_url(), 'options': {} } + + def delete(self): + # since generic foreignkeys suck. delete infoitems pointing here manually + InfoInstance.objects.filter(item_id=self.id, item_type=ContentType.objects.get_for_model(self)).delete() + super().delete() + + @classmethod + def get_subclasses(cls): + for subclass in cls.__subclasses__(): + yield from subclass.get_subclasses() + yield subclass + def __str__(self): return self.name + class ABBInfoItem(InfoItem): def get_template_url(self): return "/static/html/abb.html" - def get_edit_template_url(self): - return "/static/html/generic_edit.html" + @staticmethod + def get_create_template_url(): + return "/static/html/abb_create.html" class ImageInfoItem(InfoItem): img = models.ImageField(upload_to="infoimages/") def get_template_url(self): #get param to avoid angular from optimizing same template with different options - return "/static/html/generic_image.html?img={}".format(self.name) + return "/static/html/generic_image.html?img={}".format(self.name) - def get_edit_template_url(self): - return "/static/html/generic_image_edit.html" + @staticmethod + def get_create_template_url(): + return "/static/html/generic_image_create.html" def get_dict(self): d = super().get_dict() d["options"] = {'img': self.img.url} return d - + class ExternalImageInfoItem(InfoItem): url = models.TextField() def get_template_url(self): return "/static/html/generic_image.html?img={}".format(self.name) - def get_edit_template_url(self): - return "/static/html/generic_external_image_edit.html" + @staticmethod + def get_create_template_url(): + return "/static/html/generic_external_image_create.html" def get_dict(self): d = super().get_dict() d["options"] = {'img': self.url} return d + @classmethod + def create_from_dict(cls,d): + item = cls() + item.update_from_dict(d) + return item + + def update_from_dict(self,d): + try: + expire_date = d.pop('expire_date', None) + self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S") + except: + pass + + dmap = { + 'name': 'name', + 'url': 'url', + } + for k,v in d.items(): + try: + self.__setattr__(dmap[k],v) + except KeyError: + pass + self.save() + class InfoInstance(models.Model): rotation = models.ForeignKey('Rotation', related_name='instances') duration = models.FloatField(default=15.0) # seconds @@ -67,8 +135,27 @@ class InfoInstance(models.Model): item_type = models.ForeignKey(ContentType,on_delete=models.CASCADE) item = GenericForeignKey('item_type','item_id') + @classmethod + def create_from_dict(cls,d): + try: + rotation = Rotation.objects.get(pk=int(d["rotation_id"])) + ct = ContentType.objects.get_for_id(int(d["item_type"])) + item = ct.get_object_for_this_type(pk=int(d["item_id"])) + duration = float(d["duration"]) + except: + raise RuntimeError("invalid parameters supplied supplied") + try: + return cls.objects.create( + rotation=rotation, + item=item, + duration=duration + ) + except: + raise RuntimeError("error while adding instance to db") + def get_dict(self): return { + 'id':self.id, 'item': self.item.get_dict(), 'duration': self.duration, } @@ -77,7 +164,7 @@ class InfoInstance(models.Model): class Rotation(models.Model): name = models.CharField(max_length=255) - + def get_dict(self): # exclude expired items from rotation (note: using tricky syntax to avoid excluding items with no expire_date) now = timezone.now() @@ -86,9 +173,15 @@ class Rotation(models.Model): instance_list = list(map(lambda i:i.get_dict(), filtered)) return { + 'id':self.id, 'name': self.name, 'instances': instance_list, } + def get_list(self): + return { + 'id':self.id, + 'name':self.name, + } def __str__(self): return self.name @@ -107,3 +200,11 @@ class ABBJob(models.Model): def __str__(self): return self.title + +class ImageUploadForm(forms.Form): + ''' + Form used to handle imageuploads to + infoscreen app + ''' + name = forms.CharField() + image = forms.ImageField() diff --git a/infoscreen/static/html/abb_create.html b/infoscreen/static/html/abb_create.html new file mode 100644 index 0000000..da30aae --- /dev/null +++ b/infoscreen/static/html/abb_create.html @@ -0,0 +1,7 @@ +
+
+ + +
+ +
diff --git a/infoscreen/static/html/generic_external_image_create.html b/infoscreen/static/html/generic_external_image_create.html new file mode 100644 index 0000000..6363240 --- /dev/null +++ b/infoscreen/static/html/generic_external_image_create.html @@ -0,0 +1,11 @@ +
+
+ + +
+
+ + +
+ +
diff --git a/infoscreen/static/html/generic_image_create.html b/infoscreen/static/html/generic_image_create.html new file mode 100644 index 0000000..6a76c50 --- /dev/null +++ b/infoscreen/static/html/generic_image_create.html @@ -0,0 +1,10 @@ +
+
+ + +
+
+ +
+ +
diff --git a/infoscreen/static/html/infoscreen_admin.html b/infoscreen/static/html/infoscreen_admin.html new file mode 100644 index 0000000..df26394 --- /dev/null +++ b/infoscreen/static/html/infoscreen_admin.html @@ -0,0 +1,39 @@ +
+
+

Info items

+ + + + + + + + +
ItemSet durationAdd to rotationDelete
{{i.name}}
+

Create new item

+ Item type + +
+
+
+

Rotations

+ + + + + + +
RotationSelect
{{r.name}}
+

Rotation: {{selected_rot.name}}

+ + + + + +
InstanceDurationDelete
{{i.item.name}}{{i.duration}}s +
+ +
0){ + self.getRotation(self.rotations[0].id); + } + }); + } + this.deleteInstance = function(id){ + $http.delete("/infoscreen/instance/"+id).then(function(response){ + self.getRotation(self.selected_rot.id); + }); + } +}]); +app.service("ItemList", ["$http",'InstanceList', function($http, InstanceList){ + var self = this; + self.items = [] + this.loadItems = function(){ + $http.get("/infoscreen/items").then(function(response){ + self.items = response.data; + }); + }; + this.getItems = function(){ + return self.items; + }; + this.deleteItem = function(type_id, item_id){ + $http.delete("/infoscreen/delete_item/"+type_id+"/"+item_id).then(function(){ + self.loadItems(); + // refresh instances because deleting item may cascade to instances + InstanceList.getRotation(InstanceList.selected_rot.id); + }); + }; + this.refreshCB = function(response){ + self.loadItems(); + }; +}]); + +app.controller('infoadmin_ctrl', function($scope, $http, ItemList, InstanceList){ + // init items + $scope.rotations = []; + $scope.infoitems = []; + $scope.selected_rot= {}; + // helpers + $scope.deleteItem = ItemList.deleteItem; + $scope.selectRotation = InstanceList.getRotation; + $scope.createInstance = InstanceList.createInstance; + $scope.deleteInstance = InstanceList.deleteInstance; + // fetch data + $scope.$watch(InstanceList.listInstances, function(instances){ + $scope.selected_rot = instances; + }); + $scope.$watch(InstanceList.listRotations, function(rotations){ + $scope.rotations = rotations; + }); + InstanceList.getRotations(); + + $http.get("/infoscreen/types").then(function(response){ + $scope.infotypes = response.data; + }); + $scope.$watch(ItemList.getItems, function(items){ + $scope.infoitems = items + }); + ItemList.loadItems(); +}); +app.controller('infoadmin_externalimage_create', function($scope, $http,ItemList){ + $scope.item = {} + $scope.send = function(){ + $http.post("/infoscreen/create_external_image", $scope.item).then(ItemList.loadItems) + } +}); +app.controller('infoadmin_abbitem_create', function($scope, $http,ItemList){ + $scope.item = {} + $scope.send = function(){ + $http.post("/infoscreen/create_abbitem", $scope.item).then(ItemList.loadItems) + } +}); + +app.controller('infoadmin_image_create', ['$scope', 'Upload', '$timeout',"ItemList", function ($scope, Upload, $timeout,ItemList) { + $scope.send = function(file) { + file.upload = Upload.upload({ + url: '/infoscreen/create_image', + data: {name: $scope.name, image: file}, + }).then(function (response) { + $timeout(function () { + file.result = response.data; + ItemList.loadItems(); + $scope.name= "" + $scope.img = {} + }); + },function (response) { + if (response.status > 0) + $scope.errorMsg = response.status + ': ' + response.data; + }, function (evt) { + file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)); + }); + } +}]); diff --git a/infoscreen/static/js/infoscreen_controllers.js b/infoscreen/static/js/infoscreen_controllers.js index 3733d80..259abf0 100644 --- a/infoscreen/static/js/infoscreen_controllers.js +++ b/infoscreen/static/js/infoscreen_controllers.js @@ -1,3 +1,4 @@ + var app = angular.module('infoApp', ['ngAnimate', 'ngRoute']); app.controller('infoscreen_main', function($scope,$http,$timeout){ var templates = []; @@ -27,7 +28,7 @@ app.controller('infoscreen_main', function($scope,$http,$timeout){ for (key in temp.item.options){ $scope[key] = temp.item.options[key] } - } + } }; $timeout($scope.next, temp.duration * 1000); } @@ -38,7 +39,7 @@ app.controller('ABBController', function($scope, $http){ $scope.jobs = response.data; }) }); -app.controller('timetableCtrl', +app.controller('timetableCtrl', function($scope, $http, $interval) { function load(){ $http.get('/static/js/hsl.json') @@ -89,7 +90,7 @@ app.controller('timetableCtrl', if(e){ $scope.arr.push(z); } - + } } function pad(num, size) { @@ -122,4 +123,3 @@ app.controller('timetableCtrl', var z=$interval(load,60000); } ); - diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/.versions b/infoscreen/static/js/ng-file-upload-bower-12.2.11/.versions new file mode 100644 index 0000000..79b42c0 --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/.versions @@ -0,0 +1,4 @@ +angular:angular@1.2.24 +danialf:ng-file-upload@12.2.10 +meteor@1.1.10 +underscore@1.0.4 diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.flash.swf b/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.flash.swf new file mode 100755 index 0000000000000000000000000000000000000000..65de396c797dcf7a63cc5b05f8424f4f985ba242 GIT binary patch literal 71214 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 EeqW}N^ literal 0 HcmV?d00001 diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.js b/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.js new file mode 100644 index 0000000..5caa110 --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.js @@ -0,0 +1,4313 @@ +/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git + * FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF. + */ + +/* + * JavaScript Canvas to Blob 2.0.5 + * https://github.com/blueimp/JavaScript-Canvas-to-Blob + * + * Copyright 2012, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + * + * Based on stackoverflow user Stoive's code snippet: + * http://stackoverflow.com/q/4998908 + */ + +/*jslint nomen: true, regexp: true */ +/*global window, atob, Blob, ArrayBuffer, Uint8Array */ + +(function (window) { + 'use strict'; + var CanvasPrototype = window.HTMLCanvasElement && + window.HTMLCanvasElement.prototype, + hasBlobConstructor = window.Blob && (function () { + try { + return Boolean(new Blob()); + } catch (e) { + return false; + } + }()), + hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array && + (function () { + try { + return new Blob([new Uint8Array(100)]).size === 100; + } catch (e) { + return false; + } + }()), + BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || + window.MozBlobBuilder || window.MSBlobBuilder, + dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob && + window.ArrayBuffer && window.Uint8Array && function (dataURI) { + var byteString, + arrayBuffer, + intArray, + i, + mimeString, + bb; + if (dataURI.split(',')[0].indexOf('base64') >= 0) { + // Convert base64 to raw binary data held in a string: + byteString = atob(dataURI.split(',')[1]); + } else { + // Convert base64/URLEncoded data component to raw binary data: + byteString = decodeURIComponent(dataURI.split(',')[1]); + } + // Write the bytes of the string to an ArrayBuffer: + arrayBuffer = new ArrayBuffer(byteString.length); + intArray = new Uint8Array(arrayBuffer); + for (i = 0; i < byteString.length; i += 1) { + intArray[i] = byteString.charCodeAt(i); + } + // Separate out the mime component: + mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; + // Write the ArrayBuffer (or ArrayBufferView) to a blob: + if (hasBlobConstructor) { + return new Blob( + [hasArrayBufferViewSupport ? intArray : arrayBuffer], + {type: mimeString} + ); + } + bb = new BlobBuilder(); + bb.append(arrayBuffer); + return bb.getBlob(mimeString); + }; + if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) { + if (CanvasPrototype.mozGetAsFile) { + CanvasPrototype.toBlob = function (callback, type, quality) { + if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) { + callback(dataURLtoBlob(this.toDataURL(type, quality))); + } else { + callback(this.mozGetAsFile('blob', type)); + } + }; + } else if (CanvasPrototype.toDataURL && dataURLtoBlob) { + CanvasPrototype.toBlob = function (callback, type, quality) { + callback(dataURLtoBlob(this.toDataURL(type, quality))); + }; + } + } + window.dataURLtoBlob = dataURLtoBlob; +})(window); + +/*jslint evil: true */ +/*global window, URL, webkitURL, ActiveXObject */ + +(function (window, undef){ + 'use strict'; + + var + gid = 1, + noop = function (){}, + + document = window.document, + doctype = document.doctype || {}, + userAgent = window.navigator.userAgent, + + // https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48 + apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL), + + Blob = window.Blob, + File = window.File, + FileReader = window.FileReader, + FormData = window.FormData, + + + XMLHttpRequest = window.XMLHttpRequest, + jQuery = window.jQuery, + + html5 = !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary))) + && !(/safari\//i.test(userAgent) && !/chrome\//i.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25 + + cors = html5 && ('withCredentials' in (new XMLHttpRequest)), + + chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice || Blob.prototype.mozSlice || Blob.prototype.slice), + + // https://github.com/blueimp/JavaScript-Canvas-to-Blob + dataURLtoBlob = window.dataURLtoBlob, + + + _rimg = /img/i, + _rcanvas = /canvas/i, + _rimgcanvas = /img|canvas/i, + _rinput = /input/i, + _rdata = /^data:[^,]+,/, + + _toString = {}.toString, + + + Math = window.Math, + + _SIZE_CONST = function (pow){ + pow = new window.Number(Math.pow(1024, pow)); + pow.from = function (sz){ return Math.round(sz * this); }; + return pow; + }, + + _elEvents = {}, // element event listeners + _infoReader = [], // list of file info processors + + _readerEvents = 'abort progress error load loadend', + _xhrPropsExport = 'status statusText readyState response responseXML responseText responseBody'.split(' '), + + currentTarget = 'currentTarget', // for minimize + preventDefault = 'preventDefault', // and this too + + _isArray = function (ar) { + return ar && ('length' in ar); + }, + + /** + * Iterate over a object or array + */ + _each = function (obj, fn, ctx){ + if( obj ){ + if( _isArray(obj) ){ + for( var i = 0, n = obj.length; i < n; i++ ){ + if( i in obj ){ + fn.call(ctx, obj[i], i, obj); + } + } + } + else { + for( var key in obj ){ + if( obj.hasOwnProperty(key) ){ + fn.call(ctx, obj[key], key, obj); + } + } + } + } + }, + + /** + * Merge the contents of two or more objects together into the first object + */ + _extend = function (dst){ + var args = arguments, i = 1, _ext = function (val, key){ dst[key] = val; }; + for( ; i < args.length; i++ ){ + _each(args[i], _ext); + } + return dst; + }, + + /** + * Add event listener + */ + _on = function (el, type, fn){ + if( el ){ + var uid = api.uid(el); + + if( !_elEvents[uid] ){ + _elEvents[uid] = {}; + } + + var isFileReader = (FileReader && el) && (el instanceof FileReader); + _each(type.split(/\s+/), function (type){ + if( jQuery && !isFileReader){ + jQuery.event.add(el, type, fn); + } else { + if( !_elEvents[uid][type] ){ + _elEvents[uid][type] = []; + } + + _elEvents[uid][type].push(fn); + + if( el.addEventListener ){ el.addEventListener(type, fn, false); } + else if( el.attachEvent ){ el.attachEvent('on'+type, fn); } + else { el['on'+type] = fn; } + } + }); + } + }, + + + /** + * Remove event listener + */ + _off = function (el, type, fn){ + if( el ){ + var uid = api.uid(el), events = _elEvents[uid] || {}; + + var isFileReader = (FileReader && el) && (el instanceof FileReader); + _each(type.split(/\s+/), function (type){ + if( jQuery && !isFileReader){ + jQuery.event.remove(el, type, fn); + } + else { + var fns = events[type] || [], i = fns.length; + + while( i-- ){ + if( fns[i] === fn ){ + fns.splice(i, 1); + break; + } + } + + if( el.addEventListener ){ el.removeEventListener(type, fn, false); } + else if( el.detachEvent ){ el.detachEvent('on'+type, fn); } + else { el['on'+type] = null; } + } + }); + } + }, + + + _one = function(el, type, fn){ + _on(el, type, function _(evt){ + _off(el, type, _); + fn(evt); + }); + }, + + + _fixEvent = function (evt){ + if( !evt.target ){ evt.target = window.event && window.event.srcElement || document; } + if( evt.target.nodeType === 3 ){ evt.target = evt.target.parentNode; } + return evt; + }, + + + _supportInputAttr = function (attr){ + var input = document.createElement('input'); + input.setAttribute('type', "file"); + return attr in input; + }, + + /** + * FileAPI (core object) + */ + api = { + version: '2.0.7', + + cors: false, + html5: true, + media: false, + formData: true, + multiPassResize: true, + + debug: false, + pingUrl: false, + multiFlash: false, + flashAbortTimeout: 0, + withCredentials: true, + + staticPath: './dist/', + + flashUrl: 0, // @default: './FileAPI.flash.swf' + flashImageUrl: 0, // @default: './FileAPI.flash.image.swf' + + postNameConcat: function (name, idx){ + return name + (idx != null ? '['+ idx +']' : ''); + }, + + ext2mime: { + jpg: 'image/jpeg' + , tif: 'image/tiff' + , txt: 'text/plain' + }, + + // Fallback for flash + accept: { + 'image/*': 'art bm bmp dwg dxf cbr cbz fif fpx gif ico iefs jfif jpe jpeg jpg jps jut mcf nap nif pbm pcx pgm pict pm png pnm qif qtif ras rast rf rp svf tga tif tiff xbm xbm xpm xwd' + , 'audio/*': 'm4a flac aac rm mpa wav wma ogg mp3 mp2 m3u mod amf dmf dsm far gdm imf it m15 med okt s3m stm sfx ult uni xm sid ac3 dts cue aif aiff wpl ape mac mpc mpp shn wv nsf spc gym adplug adx dsp adp ymf ast afc hps xs' + , 'video/*': 'm4v 3gp nsv ts ty strm rm rmvb m3u ifo mov qt divx xvid bivx vob nrg img iso pva wmv asf asx ogm m2v avi bin dat dvr-ms mpg mpeg mp4 mkv avc vp3 svq3 nuv viv dv fli flv wpl' + }, + + uploadRetry : 0, + networkDownRetryTimeout : 5000, // milliseconds, don't flood when network is down + + chunkSize : 0, + chunkUploadRetry : 0, + chunkNetworkDownRetryTimeout : 2000, // milliseconds, don't flood when network is down + + KB: _SIZE_CONST(1), + MB: _SIZE_CONST(2), + GB: _SIZE_CONST(3), + TB: _SIZE_CONST(4), + + EMPTY_PNG: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIW2NkAAIAAAoAAggA9GkAAAAASUVORK5CYII=', + + expando: 'fileapi' + (new Date).getTime(), + + uid: function (obj){ + return obj + ? (obj[api.expando] = obj[api.expando] || api.uid()) + : (++gid, api.expando + gid) + ; + }, + + log: function (){ + // ngf fix for IE8 #1071 + if( api.debug && api._supportConsoleLog ){ + if( api._supportConsoleLogApply ){ + console.log.apply(console, arguments); + } + else { + console.log([].join.call(arguments, ' ')); + } + } + }, + + /** + * Create new image + * + * @param {String} [src] + * @param {Function} [fn] 1. error -- boolean, 2. img -- Image element + * @returns {HTMLElement} + */ + newImage: function (src, fn){ + var img = document.createElement('img'); + if( fn ){ + api.event.one(img, 'error load', function (evt){ + fn(evt.type == 'error', img); + img = null; + }); + } + img.src = src; + return img; + }, + + /** + * Get XHR + * @returns {XMLHttpRequest} + */ + getXHR: function (){ + var xhr; + + if( XMLHttpRequest ){ + xhr = new XMLHttpRequest; + } + else if( window.ActiveXObject ){ + try { + xhr = new ActiveXObject('MSXML2.XMLHttp.3.0'); + } catch (e) { + xhr = new ActiveXObject('Microsoft.XMLHTTP'); + } + } + + return xhr; + }, + + isArray: _isArray, + + support: { + dnd: cors && ('ondrop' in document.createElement('div')), + cors: cors, + html5: html5, + chunked: chunked, + dataURI: true, + accept: _supportInputAttr('accept'), + multiple: _supportInputAttr('multiple') + }, + + event: { + on: _on + , off: _off + , one: _one + , fix: _fixEvent + }, + + + throttle: function(fn, delay) { + var id, args; + + return function _throttle(){ + args = arguments; + + if( !id ){ + fn.apply(window, args); + id = setTimeout(function (){ + id = 0; + fn.apply(window, args); + }, delay); + } + }; + }, + + + F: function (){}, + + + parseJSON: function (str){ + var json; + if( window.JSON && JSON.parse ){ + json = JSON.parse(str); + } + else { + json = (new Function('return ('+str.replace(/([\r\n])/g, '\\$1')+');'))(); + } + return json; + }, + + + trim: function (str){ + str = String(str); + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); + }, + + /** + * Simple Defer + * @return {Object} + */ + defer: function (){ + var + list = [] + , result + , error + , defer = { + resolve: function (err, res){ + defer.resolve = noop; + error = err || false; + result = res; + + while( res = list.shift() ){ + res(error, result); + } + }, + + then: function (fn){ + if( error !== undef ){ + fn(error, result); + } else { + list.push(fn); + } + } + }; + + return defer; + }, + + queue: function (fn){ + var + _idx = 0 + , _length = 0 + , _fail = false + , _end = false + , queue = { + inc: function (){ + _length++; + }, + + next: function (){ + _idx++; + setTimeout(queue.check, 0); + }, + + check: function (){ + (_idx >= _length) && !_fail && queue.end(); + }, + + isFail: function (){ + return _fail; + }, + + fail: function (){ + !_fail && fn(_fail = true); + }, + + end: function (){ + if( !_end ){ + _end = true; + fn(); + } + } + } + ; + return queue; + }, + + + /** + * For each object + * + * @param {Object|Array} obj + * @param {Function} fn + * @param {*} [ctx] + */ + each: _each, + + + /** + * Async for + * @param {Array} array + * @param {Function} callback + */ + afor: function (array, callback){ + var i = 0, n = array.length; + + if( _isArray(array) && n-- ){ + (function _next(){ + callback(n != i && _next, array[i], i++); + })(); + } + else { + callback(false); + } + }, + + + /** + * Merge the contents of two or more objects together into the first object + * + * @param {Object} dst + * @return {Object} + */ + extend: _extend, + + + /** + * Is file? + * @param {File} file + * @return {Boolean} + */ + isFile: function (file){ + return _toString.call(file) === '[object File]'; + }, + + + /** + * Is blob? + * @param {Blob} blob + * @returns {Boolean} + */ + isBlob: function (blob) { + return this.isFile(blob) || (_toString.call(blob) === '[object Blob]'); + }, + + + /** + * Is canvas element + * + * @param {HTMLElement} el + * @return {Boolean} + */ + isCanvas: function (el){ + return el && _rcanvas.test(el.nodeName); + }, + + + getFilesFilter: function (filter){ + filter = typeof filter == 'string' ? filter : (filter.getAttribute && filter.getAttribute('accept') || ''); + return filter ? new RegExp('('+ filter.replace(/\./g, '\\.').replace(/,/g, '|') +')$', 'i') : /./; + }, + + + + /** + * Read as DataURL + * + * @param {File|Element} file + * @param {Function} fn + */ + readAsDataURL: function (file, fn){ + if( api.isCanvas(file) ){ + _emit(file, fn, 'load', api.toDataURL(file)); + } + else { + _readAs(file, fn, 'DataURL'); + } + }, + + + /** + * Read as Binary string + * + * @param {File} file + * @param {Function} fn + */ + readAsBinaryString: function (file, fn){ + if( _hasSupportReadAs('BinaryString') ){ + _readAs(file, fn, 'BinaryString'); + } else { + // Hello IE10! + _readAs(file, function (evt){ + if( evt.type == 'load' ){ + try { + // dataURL -> binaryString + evt.result = api.toBinaryString(evt.result); + } catch (e){ + evt.type = 'error'; + evt.message = e.toString(); + } + } + fn(evt); + }, 'DataURL'); + } + }, + + + /** + * Read as ArrayBuffer + * + * @param {File} file + * @param {Function} fn + */ + readAsArrayBuffer: function(file, fn){ + _readAs(file, fn, 'ArrayBuffer'); + }, + + + /** + * Read as text + * + * @param {File} file + * @param {String} encoding + * @param {Function} [fn] + */ + readAsText: function(file, encoding, fn){ + if( !fn ){ + fn = encoding; + encoding = 'utf-8'; + } + + _readAs(file, fn, 'Text', encoding); + }, + + + /** + * Convert image or canvas to DataURL + * + * @param {Element} el Image or Canvas element + * @param {String} [type] mime-type + * @return {String} + */ + toDataURL: function (el, type){ + if( typeof el == 'string' ){ + return el; + } + else if( el.toDataURL ){ + return el.toDataURL(type || 'image/png'); + } + }, + + + /** + * Canvert string, image or canvas to binary string + * + * @param {String|Element} val + * @return {String} + */ + toBinaryString: function (val){ + return window.atob(api.toDataURL(val).replace(_rdata, '')); + }, + + + /** + * Read file or DataURL as ImageElement + * + * @param {File|String} file + * @param {Function} fn + * @param {Boolean} [progress] + */ + readAsImage: function (file, fn, progress){ + if( api.isFile(file) ){ + if( apiURL ){ + /** @namespace apiURL.createObjectURL */ + var data = apiURL.createObjectURL(file); + if( data === undef ){ + _emit(file, fn, 'error'); + } + else { + api.readAsImage(data, fn, progress); + } + } + else { + api.readAsDataURL(file, function (evt){ + if( evt.type == 'load' ){ + api.readAsImage(evt.result, fn, progress); + } + else if( progress || evt.type == 'error' ){ + _emit(file, fn, evt, null, { loaded: evt.loaded, total: evt.total }); + } + }); + } + } + else if( api.isCanvas(file) ){ + _emit(file, fn, 'load', file); + } + else if( _rimg.test(file.nodeName) ){ + if( file.complete ){ + _emit(file, fn, 'load', file); + } + else { + var events = 'error abort load'; + _one(file, events, function _fn(evt){ + if( evt.type == 'load' && apiURL ){ + /** @namespace apiURL.revokeObjectURL */ + apiURL.revokeObjectURL(file.src); + } + + _off(file, events, _fn); + _emit(file, fn, evt, file); + }); + } + } + else if( file.iframe ){ + _emit(file, fn, { type: 'error' }); + } + else { + // Created image + var img = api.newImage(file.dataURL || file); + api.readAsImage(img, fn, progress); + } + }, + + + /** + * Make file by name + * + * @param {String} name + * @return {Array} + */ + checkFileObj: function (name){ + var file = {}, accept = api.accept; + + if( typeof name == 'object' ){ + file = name; + } + else { + file.name = (name + '').split(/\\|\//g).pop(); + } + + if( file.type == null ){ + file.type = file.name.split('.').pop(); + } + + _each(accept, function (ext, type){ + ext = new RegExp(ext.replace(/\s/g, '|'), 'i'); + if( ext.test(file.type) || api.ext2mime[file.type] ){ + file.type = api.ext2mime[file.type] || (type.split('/')[0] +'/'+ file.type); + } + }); + + return file; + }, + + + /** + * Get drop files + * + * @param {Event} evt + * @param {Function} callback + */ + getDropFiles: function (evt, callback){ + var + files = [] + , dataTransfer = _getDataTransfer(evt) + , entrySupport = _isArray(dataTransfer.items) && dataTransfer.items[0] && _getAsEntry(dataTransfer.items[0]) + , queue = api.queue(function (){ callback(files); }) + ; + + _each((entrySupport ? dataTransfer.items : dataTransfer.files) || [], function (item){ + queue.inc(); + + try { + if( entrySupport ){ + _readEntryAsFiles(item, function (err, entryFiles){ + if( err ){ + api.log('[err] getDropFiles:', err); + } else { + files.push.apply(files, entryFiles); + } + queue.next(); + }); + } + else { + _isRegularFile(item, function (yes){ + yes && files.push(item); + queue.next(); + }); + } + } + catch( err ){ + queue.next(); + api.log('[err] getDropFiles: ', err); + } + }); + + queue.check(); + }, + + + /** + * Get file list + * + * @param {HTMLInputElement|Event} input + * @param {String|Function} [filter] + * @param {Function} [callback] + * @return {Array|Null} + */ + getFiles: function (input, filter, callback){ + var files = []; + + if( callback ){ + api.filterFiles(api.getFiles(input), filter, callback); + return null; + } + + if( input.jquery ){ + // jQuery object + input.each(function (){ + files = files.concat(api.getFiles(this)); + }); + input = files; + files = []; + } + + if( typeof filter == 'string' ){ + filter = api.getFilesFilter(filter); + } + + if( input.originalEvent ){ + // jQuery event + input = _fixEvent(input.originalEvent); + } + else if( input.srcElement ){ + // IE Event + input = _fixEvent(input); + } + + + if( input.dataTransfer ){ + // Drag'n'Drop + input = input.dataTransfer; + } + else if( input.target ){ + // Event + input = input.target; + } + + if( input.files ){ + // Input[type="file"] + files = input.files; + + if( !html5 ){ + // Partial support for file api + files[0].blob = input; + files[0].iframe = true; + } + } + else if( !html5 && isInputFile(input) ){ + if( api.trim(input.value) ){ + files = [api.checkFileObj(input.value)]; + files[0].blob = input; + files[0].iframe = true; + } + } + else if( _isArray(input) ){ + files = input; + } + + return api.filter(files, function (file){ return !filter || filter.test(file.name); }); + }, + + + /** + * Get total file size + * @param {Array} files + * @return {Number} + */ + getTotalSize: function (files){ + var size = 0, i = files && files.length; + while( i-- ){ + size += files[i].size; + } + return size; + }, + + + /** + * Get image information + * + * @param {File} file + * @param {Function} fn + */ + getInfo: function (file, fn){ + var info = {}, readers = _infoReader.concat(); + + if( api.isFile(file) ){ + (function _next(){ + var reader = readers.shift(); + if( reader ){ + if( reader.test(file.type) ){ + reader(file, function (err, res){ + if( err ){ + fn(err); + } + else { + _extend(info, res); + _next(); + } + }); + } + else { + _next(); + } + } + else { + fn(false, info); + } + })(); + } + else { + fn('not_support_info', info); + } + }, + + + /** + * Add information reader + * + * @param {RegExp} mime + * @param {Function} fn + */ + addInfoReader: function (mime, fn){ + fn.test = function (type){ return mime.test(type); }; + _infoReader.push(fn); + }, + + + /** + * Filter of array + * + * @param {Array} input + * @param {Function} fn + * @return {Array} + */ + filter: function (input, fn){ + var result = [], i = 0, n = input.length, val; + + for( ; i < n; i++ ){ + if( i in input ){ + val = input[i]; + if( fn.call(val, val, i, input) ){ + result.push(val); + } + } + } + + return result; + }, + + + /** + * Filter files + * + * @param {Array} files + * @param {Function} eachFn + * @param {Function} resultFn + */ + filterFiles: function (files, eachFn, resultFn){ + if( files.length ){ + // HTML5 or Flash + var queue = files.concat(), file, result = [], deleted = []; + + (function _next(){ + if( queue.length ){ + file = queue.shift(); + api.getInfo(file, function (err, info){ + (eachFn(file, err ? false : info) ? result : deleted).push(file); + _next(); + }); + } + else { + resultFn(result, deleted); + } + })(); + } + else { + resultFn([], files); + } + }, + + + upload: function (options){ + options = _extend({ + jsonp: 'callback' + , prepare: api.F + , beforeupload: api.F + , upload: api.F + , fileupload: api.F + , fileprogress: api.F + , filecomplete: api.F + , progress: api.F + , complete: api.F + , pause: api.F + , imageOriginal: true + , chunkSize: api.chunkSize + , chunkUploadRetry: api.chunkUploadRetry + , uploadRetry: api.uploadRetry + }, options); + + + if( options.imageAutoOrientation && !options.imageTransform ){ + options.imageTransform = { rotate: 'auto' }; + } + + + var + proxyXHR = new api.XHR(options) + , dataArray = this._getFilesDataArray(options.files) + , _this = this + , _total = 0 + , _loaded = 0 + , _nextFile + , _complete = false + ; + + + // calc total size + _each(dataArray, function (data){ + _total += data.size; + }); + + // Array of files + proxyXHR.files = []; + _each(dataArray, function (data){ + proxyXHR.files.push(data.file); + }); + + // Set upload status props + proxyXHR.total = _total; + proxyXHR.loaded = 0; + proxyXHR.filesLeft = dataArray.length; + + // emit "beforeupload" event + options.beforeupload(proxyXHR, options); + + // Upload by file + _nextFile = function (){ + var + data = dataArray.shift() + , _file = data && data.file + , _fileLoaded = false + , _fileOptions = _simpleClone(options) + ; + + proxyXHR.filesLeft = dataArray.length; + + if( _file && _file.name === api.expando ){ + _file = null; + api.log('[warn] FileAPI.upload() — called without files'); + } + + if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){ + // Mark active job + _complete = false; + + // Set current upload file + proxyXHR.currentFile = _file; + + // Prepare file options + if (_file && options.prepare(_file, _fileOptions) === false) { + _nextFile.call(_this); + return; + } + _fileOptions.file = _file; + + _this._getFormData(_fileOptions, data, function (form){ + if( !_loaded ){ + // emit "upload" event + options.upload(proxyXHR, options); + } + + var xhr = new api.XHR(_extend({}, _fileOptions, { + + upload: _file ? function (){ + // emit "fileupload" event + options.fileupload(_file, xhr, _fileOptions); + } : noop, + + progress: _file ? function (evt){ + if( !_fileLoaded ){ + // For ignore the double calls. + _fileLoaded = (evt.loaded === evt.total); + + // emit "fileprogress" event + options.fileprogress({ + type: 'progress' + , total: data.total = evt.total + , loaded: data.loaded = evt.loaded + }, _file, xhr, _fileOptions); + + // emit "progress" event + options.progress({ + type: 'progress' + , total: _total + , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0 + }, _file, xhr, _fileOptions); + } + } : noop, + + complete: function (err){ + _each(_xhrPropsExport, function (name){ + proxyXHR[name] = xhr[name]; + }); + + if( _file ){ + data.total = (data.total || data.size); + data.loaded = data.total; + + if( !err ) { + // emulate 100% "progress" + this.progress(data); + + // fixed throttle event + _fileLoaded = true; + + // bytes loaded + _loaded += data.size; // data.size != data.total, it's desirable fix this + proxyXHR.loaded = _loaded; + } + + // emit "filecomplete" event + options.filecomplete(err, xhr, _file, _fileOptions); + } + + // upload next file + setTimeout(function () {_nextFile.call(_this);}, 0); + } + })); // xhr + + + // ... + proxyXHR.abort = function (current){ + if (!current) { dataArray.length = 0; } + this.current = current; + xhr.abort(); + }; + + // Start upload + xhr.send(form); + }); + } + else { + var successful = proxyXHR.status == 200 || proxyXHR.status == 201 || proxyXHR.status == 204; + options.complete(successful ? false : (proxyXHR.statusText || 'error'), proxyXHR, options); + // Mark done state + _complete = true; + } + }; + + + // Next tick + setTimeout(_nextFile, 0); + + + // Append more files to the existing request + // first - add them to the queue head/tail + proxyXHR.append = function (files, first) { + files = api._getFilesDataArray([].concat(files)); + + _each(files, function (data) { + _total += data.size; + proxyXHR.files.push(data.file); + if (first) { + dataArray.unshift(data); + } else { + dataArray.push(data); + } + }); + + proxyXHR.statusText = ""; + + if( _complete ){ + _nextFile.call(_this); + } + }; + + + // Removes file from queue by file reference and returns it + proxyXHR.remove = function (file) { + var i = dataArray.length, _file; + while( i-- ){ + if( dataArray[i].file == file ){ + _file = dataArray.splice(i, 1); + _total -= _file.size; + } + } + return _file; + }; + + return proxyXHR; + }, + + + _getFilesDataArray: function (data){ + var files = [], oFiles = {}; + + if( isInputFile(data) ){ + var tmp = api.getFiles(data); + oFiles[data.name || 'file'] = data.getAttribute('multiple') !== null ? tmp : tmp[0]; + } + else if( _isArray(data) && isInputFile(data[0]) ){ + _each(data, function (input){ + oFiles[input.name || 'file'] = api.getFiles(input); + }); + } + else { + oFiles = data; + } + + _each(oFiles, function add(file, name){ + if( _isArray(file) ){ + _each(file, function (file){ + add(file, name); + }); + } + else if( file && (file.name || file.image) ){ + files.push({ + name: name + , file: file + , size: file.size + , total: file.size + , loaded: 0 + }); + } + }); + + if( !files.length ){ + // Create fake `file` object + files.push({ file: { name: api.expando } }); + } + + return files; + }, + + + _getFormData: function (options, data, fn){ + var + file = data.file + , name = data.name + , filename = file.name + , filetype = file.type + , trans = api.support.transform && options.imageTransform + , Form = new api.Form + , queue = api.queue(function (){ fn(Form); }) + , isOrignTrans = trans && _isOriginTransform(trans) + , postNameConcat = api.postNameConcat + ; + + // Append data + _each(options.data, function add(val, name){ + if( typeof val == 'object' ){ + _each(val, function (v, i){ + add(v, postNameConcat(name, i)); + }); + } + else { + Form.append(name, val); + } + }); + + (function _addFile(file/**Object*/){ + if( file.image ){ // This is a FileAPI.Image + queue.inc(); + + file.toData(function (err, image){ + // @todo: error + filename = filename || (new Date).getTime()+'.png'; + + _addFile(image); + queue.next(); + }); + } + else if( api.Image && trans && (/^image/.test(file.type) || _rimgcanvas.test(file.nodeName)) ){ + queue.inc(); + + if( isOrignTrans ){ + // Convert to array for transform function + trans = [trans]; + } + + api.Image.transform(file, trans, options.imageAutoOrientation, function (err, images){ + if( isOrignTrans && !err ){ + if( !dataURLtoBlob && !api.flashEngine ){ + // Canvas.toBlob or Flash not supported, use multipart + Form.multipart = true; + } + + Form.append(name, images[0], filename, trans[0].type || filetype); + } + else { + var addOrigin = 0; + + if( !err ){ + _each(images, function (image, idx){ + if( !dataURLtoBlob && !api.flashEngine ){ + Form.multipart = true; + } + + if( !trans[idx].postName ){ + addOrigin = 1; + } + + Form.append(trans[idx].postName || postNameConcat(name, idx), image, filename, trans[idx].type || filetype); + }); + } + + if( err || options.imageOriginal ){ + Form.append(postNameConcat(name, (addOrigin ? 'original' : null)), file, filename, filetype); + } + } + + queue.next(); + }); + } + else if( filename !== api.expando ){ + Form.append(name, file, filename); + } + })(file); + + queue.check(); + }, + + + reset: function (inp, notRemove){ + var parent, clone; + + if( jQuery ){ + clone = jQuery(inp).clone(true).insertBefore(inp).val('')[0]; + if( !notRemove ){ + jQuery(inp).remove(); + } + } else { + parent = inp.parentNode; + clone = parent.insertBefore(inp.cloneNode(true), inp); + clone.value = ''; + + if( !notRemove ){ + parent.removeChild(inp); + } + + _each(_elEvents[api.uid(inp)], function (fns, type){ + _each(fns, function (fn){ + _off(inp, type, fn); + _on(clone, type, fn); + }); + }); + } + + return clone; + }, + + + /** + * Load remote file + * + * @param {String} url + * @param {Function} fn + * @return {XMLHttpRequest} + */ + load: function (url, fn){ + var xhr = api.getXHR(); + if( xhr ){ + xhr.open('GET', url, true); + + if( xhr.overrideMimeType ){ + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + + _on(xhr, 'progress', function (/**Event*/evt){ + /** @namespace evt.lengthComputable */ + if( evt.lengthComputable ){ + fn({ type: evt.type, loaded: evt.loaded, total: evt.total }, xhr); + } + }); + + xhr.onreadystatechange = function(){ + if( xhr.readyState == 4 ){ + xhr.onreadystatechange = null; + if( xhr.status == 200 ){ + url = url.split('/'); + /** @namespace xhr.responseBody */ + var file = { + name: url[url.length-1] + , size: xhr.getResponseHeader('Content-Length') + , type: xhr.getResponseHeader('Content-Type') + }; + file.dataURL = 'data:'+file.type+';base64,' + api.encode64(xhr.responseBody || xhr.responseText); + fn({ type: 'load', result: file }, xhr); + } + else { + fn({ type: 'error' }, xhr); + } + } + }; + xhr.send(null); + } else { + fn({ type: 'error' }); + } + + return xhr; + }, + + encode64: function (str){ + var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', outStr = '', i = 0; + + if( typeof str !== 'string' ){ + str = String(str); + } + + while( i < str.length ){ + //all three "& 0xff" added below are there to fix a known bug + //with bytes returned by xhr.responseText + var + byte1 = str.charCodeAt(i++) & 0xff + , byte2 = str.charCodeAt(i++) & 0xff + , byte3 = str.charCodeAt(i++) & 0xff + , enc1 = byte1 >> 2 + , enc2 = ((byte1 & 3) << 4) | (byte2 >> 4) + , enc3, enc4 + ; + + if( isNaN(byte2) ){ + enc3 = enc4 = 64; + } else { + enc3 = ((byte2 & 15) << 2) | (byte3 >> 6); + enc4 = isNaN(byte3) ? 64 : byte3 & 63; + } + + outStr += b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4); + } + + return outStr; + } + + } // api + ; + + + function _emit(target, fn, name, res, ext){ + var evt = { + type: name.type || name + , target: target + , result: res + }; + _extend(evt, ext); + fn(evt); + } + + + function _hasSupportReadAs(as){ + return FileReader && !!FileReader.prototype['readAs'+as]; + } + + + function _readAs(file, fn, as, encoding){ + if( api.isBlob(file) && _hasSupportReadAs(as) ){ + var Reader = new FileReader; + + // Add event listener + _on(Reader, _readerEvents, function _fn(evt){ + var type = evt.type; + if( type == 'progress' ){ + _emit(file, fn, evt, evt.target.result, { loaded: evt.loaded, total: evt.total }); + } + else if( type == 'loadend' ){ + _off(Reader, _readerEvents, _fn); + Reader = null; + } + else { + _emit(file, fn, evt, evt.target.result); + } + }); + + + try { + // ReadAs ... + if( encoding ){ + Reader['readAs'+as](file, encoding); + } + else { + Reader['readAs'+as](file); + } + } + catch (err){ + _emit(file, fn, 'error', undef, { error: err.toString() }); + } + } + else { + _emit(file, fn, 'error', undef, { error: 'FileReader_not_support_'+as }); + } + } + + + function _isRegularFile(file, callback){ + // http://stackoverflow.com/questions/8856628/detecting-folders-directories-in-javascript-filelist-objects + if( !file.type && (file.size % 4096) === 0 && (file.size <= 102400) ){ + if( FileReader ){ + try { + var Reader = new FileReader(); + + _one(Reader, _readerEvents, function (evt){ + var isFile = evt.type != 'error'; + callback(isFile); + if( isFile ){ + Reader.abort(); + } + }); + + Reader.readAsDataURL(file); + } catch( err ){ + callback(false); + } + } + else { + callback(null); + } + } + else { + callback(true); + } + } + + + function _getAsEntry(item){ + var entry; + if( item.getAsEntry ){ entry = item.getAsEntry(); } + else if( item.webkitGetAsEntry ){ entry = item.webkitGetAsEntry(); } + return entry; + } + + + function _readEntryAsFiles(entry, callback){ + if( !entry ){ + // error + callback('invalid entry'); + } + else if( entry.isFile ){ + // Read as file + entry.file(function(file){ + // success + file.fullPath = entry.fullPath; + callback(false, [file]); + }, function (err){ + // error + callback('FileError.code: '+err.code); + }); + } + else if( entry.isDirectory ){ + var reader = entry.createReader(), result = []; + + reader.readEntries(function(entries){ + // success + api.afor(entries, function (next, entry){ + _readEntryAsFiles(entry, function (err, files){ + if( err ){ + api.log(err); + } + else { + result = result.concat(files); + } + + if( next ){ + next(); + } + else { + callback(false, result); + } + }); + }); + }, function (err){ + // error + callback('directory_reader: ' + err); + }); + } + else { + _readEntryAsFiles(_getAsEntry(entry), callback); + } + } + + + function _simpleClone(obj){ + var copy = {}; + _each(obj, function (val, key){ + if( val && (typeof val === 'object') && (val.nodeType === void 0) ){ + val = _extend({}, val); + } + copy[key] = val; + }); + return copy; + } + + + function isInputFile(el){ + return _rinput.test(el && el.tagName); + } + + + function _getDataTransfer(evt){ + return (evt.originalEvent || evt || '').dataTransfer || {}; + } + + + function _isOriginTransform(trans){ + var key; + for( key in trans ){ + if( trans.hasOwnProperty(key) ){ + if( !(trans[key] instanceof Object || key === 'overlay' || key === 'filter') ){ + return true; + } + } + } + return false; + } + + + // Add default image info reader + api.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){ + if( !file.__dimensions ){ + var defer = file.__dimensions = api.defer(); + + api.readAsImage(file, function (evt){ + var img = evt.target; + defer.resolve(evt.type == 'load' ? false : 'error', { + width: img.width + , height: img.height + }); + img.src = api.EMPTY_PNG; + img = null; + }); + } + + file.__dimensions.then(callback); + }); + + + /** + * Drag'n'Drop special event + * + * @param {HTMLElement} el + * @param {Function} onHover + * @param {Function} onDrop + */ + api.event.dnd = function (el, onHover, onDrop){ + var _id, _type; + + if( !onDrop ){ + onDrop = onHover; + onHover = api.F; + } + + if( FileReader ){ + // Hover + _on(el, 'dragenter dragleave dragover', onHover.ff = onHover.ff || function (evt){ + var + types = _getDataTransfer(evt).types + , i = types && types.length + , debounceTrigger = false + ; + + while( i-- ){ + if( ~types[i].indexOf('File') ){ + evt[preventDefault](); + + if( _type !== evt.type ){ + _type = evt.type; // Store current type of event + + if( _type != 'dragleave' ){ + onHover.call(evt[currentTarget], true, evt); + } + + debounceTrigger = true; + } + + break; // exit from "while" + } + } + + if( debounceTrigger ){ + clearTimeout(_id); + _id = setTimeout(function (){ + onHover.call(evt[currentTarget], _type != 'dragleave', evt); + }, 50); + } + }); + + + // Drop + _on(el, 'drop', onDrop.ff = onDrop.ff || function (evt){ + evt[preventDefault](); + + _type = 0; + onHover.call(evt[currentTarget], false, evt); + + api.getDropFiles(evt, function (files){ + onDrop.call(evt[currentTarget], files, evt); + }); + }); + } + else { + api.log("Drag'n'Drop -- not supported"); + } + }; + + + /** + * Remove drag'n'drop + * @param {HTMLElement} el + * @param {Function} onHover + * @param {Function} onDrop + */ + api.event.dnd.off = function (el, onHover, onDrop){ + _off(el, 'dragenter dragleave dragover', onHover.ff); + _off(el, 'drop', onDrop.ff); + }; + + + // Support jQuery + if( jQuery && !jQuery.fn.dnd ){ + jQuery.fn.dnd = function (onHover, onDrop){ + return this.each(function (){ + api.event.dnd(this, onHover, onDrop); + }); + }; + + jQuery.fn.offdnd = function (onHover, onDrop){ + return this.each(function (){ + api.event.dnd.off(this, onHover, onDrop); + }); + }; + } + + // @export + window.FileAPI = _extend(api, window.FileAPI); + + + // Debug info + api.log('FileAPI: ' + api.version); + api.log('protocol: ' + window.location.protocol); + api.log('doctype: [' + doctype.name + '] ' + doctype.publicId + ' ' + doctype.systemId); + + + // @detect 'x-ua-compatible' + _each(document.getElementsByTagName('meta'), function (meta){ + if( /x-ua-compatible/i.test(meta.getAttribute('http-equiv')) ){ + api.log('meta.http-equiv: ' + meta.getAttribute('content')); + } + }); + + + // configuration + try { + api._supportConsoleLog = !!console.log; + api._supportConsoleLogApply = !!console.log.apply; + } catch (err) {} + + if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; } + if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; } + if( !api.flashWebcamUrl ){ api.flashWebcamUrl = api.staticPath + 'FileAPI.flash.camera.swf'; } +})(window, void 0); + +/*global window, FileAPI, document */ + +(function (api, document, undef) { + 'use strict'; + + var + min = Math.min, + round = Math.round, + getCanvas = function () { return document.createElement('canvas'); }, + support = false, + exifOrientation = { + 8: 270 + , 3: 180 + , 6: 90 + , 7: 270 + , 4: 180 + , 5: 90 + } + ; + + try { + support = getCanvas().toDataURL('image/png').indexOf('data:image/png') > -1; + } + catch (e){} + + + function Image(file){ + if( file instanceof Image ){ + var img = new Image(file.file); + api.extend(img.matrix, file.matrix); + return img; + } + else if( !(this instanceof Image) ){ + return new Image(file); + } + + this.file = file; + this.size = file.size || 100; + + this.matrix = { + sx: 0, + sy: 0, + sw: 0, + sh: 0, + dx: 0, + dy: 0, + dw: 0, + dh: 0, + resize: 0, // min, max OR preview + deg: 0, + quality: 1, // jpeg quality + filter: 0 + }; + } + + + Image.prototype = { + image: true, + constructor: Image, + + set: function (attrs){ + api.extend(this.matrix, attrs); + return this; + }, + + crop: function (x, y, w, h){ + if( w === undef ){ + w = x; + h = y; + x = y = 0; + } + return this.set({ sx: x, sy: y, sw: w, sh: h || w }); + }, + + resize: function (w, h, strategy){ + if( /min|max/.test(h) ){ + strategy = h; + h = w; + } + + return this.set({ dw: w, dh: h || w, resize: strategy }); + }, + + preview: function (w, h){ + return this.resize(w, h || w, 'preview'); + }, + + rotate: function (deg){ + return this.set({ deg: deg }); + }, + + filter: function (filter){ + return this.set({ filter: filter }); + }, + + overlay: function (images){ + return this.set({ overlay: images }); + }, + + clone: function (){ + return new Image(this); + }, + + _load: function (image, fn){ + var self = this; + + if( /img|video/i.test(image.nodeName) ){ + fn.call(self, null, image); + } + else { + api.readAsImage(image, function (evt){ + fn.call(self, evt.type != 'load', evt.result); + }); + } + }, + + _apply: function (image, fn){ + var + canvas = getCanvas() + , m = this.getMatrix(image) + , ctx = canvas.getContext('2d') + , width = image.videoWidth || image.width + , height = image.videoHeight || image.height + , deg = m.deg + , dw = m.dw + , dh = m.dh + , w = width + , h = height + , filter = m.filter + , copy // canvas copy + , buffer = image + , overlay = m.overlay + , queue = api.queue(function (){ image.src = api.EMPTY_PNG; fn(false, canvas); }) + , renderImageToCanvas = api.renderImageToCanvas + ; + + // Normalize angle + deg = deg - Math.floor(deg/360)*360; + + // For `renderImageToCanvas` + image._type = this.file.type; + + while(m.multipass && min(w/dw, h/dh) > 2 ){ + w = (w/2 + 0.5)|0; + h = (h/2 + 0.5)|0; + + copy = getCanvas(); + copy.width = w; + copy.height = h; + + if( buffer !== image ){ + renderImageToCanvas(copy, buffer, 0, 0, buffer.width, buffer.height, 0, 0, w, h); + buffer = copy; + } + else { + buffer = copy; + renderImageToCanvas(buffer, image, m.sx, m.sy, m.sw, m.sh, 0, 0, w, h); + m.sx = m.sy = m.sw = m.sh = 0; + } + } + + + canvas.width = (deg % 180) ? dh : dw; + canvas.height = (deg % 180) ? dw : dh; + + canvas.type = m.type; + canvas.quality = m.quality; + + ctx.rotate(deg * Math.PI / 180); + renderImageToCanvas(ctx.canvas, buffer + , m.sx, m.sy + , m.sw || buffer.width + , m.sh || buffer.height + , (deg == 180 || deg == 270 ? -dw : 0) + , (deg == 90 || deg == 180 ? -dh : 0) + , dw, dh + ); + dw = canvas.width; + dh = canvas.height; + + // Apply overlay + overlay && api.each([].concat(overlay), function (over){ + queue.inc(); + // preload + var img = new window.Image, fn = function (){ + var + x = over.x|0 + , y = over.y|0 + , w = over.w || img.width + , h = over.h || img.height + , rel = over.rel + ; + + // center | right | left + x = (rel == 1 || rel == 4 || rel == 7) ? (dw - w + x)/2 : (rel == 2 || rel == 5 || rel == 8 ? dw - (w + x) : x); + + // center | bottom | top + y = (rel == 3 || rel == 4 || rel == 5) ? (dh - h + y)/2 : (rel >= 6 ? dh - (h + y) : y); + + api.event.off(img, 'error load abort', fn); + + try { + ctx.globalAlpha = over.opacity || 1; + ctx.drawImage(img, x, y, w, h); + } + catch (er){} + + queue.next(); + }; + + api.event.on(img, 'error load abort', fn); + img.src = over.src; + + if( img.complete ){ + fn(); + } + }); + + if( filter ){ + queue.inc(); + Image.applyFilter(canvas, filter, queue.next); + } + + queue.check(); + }, + + getMatrix: function (image){ + var + m = api.extend({}, this.matrix) + , sw = m.sw = m.sw || image.videoWidth || image.naturalWidth || image.width + , sh = m.sh = m.sh || image.videoHeight || image.naturalHeight || image.height + , dw = m.dw = m.dw || sw + , dh = m.dh = m.dh || sh + , sf = sw/sh, df = dw/dh + , strategy = m.resize + ; + + if( strategy == 'preview' ){ + if( dw != sw || dh != sh ){ + // Make preview + var w, h; + + if( df >= sf ){ + w = sw; + h = w / df; + } else { + h = sh; + w = h * df; + } + + if( w != sw || h != sh ){ + m.sx = ~~((sw - w)/2); + m.sy = ~~((sh - h)/2); + sw = w; + sh = h; + } + } + } + else if( strategy ){ + if( !(sw > dw || sh > dh) ){ + dw = sw; + dh = sh; + } + else if( strategy == 'min' ){ + dw = round(sf < df ? min(sw, dw) : dh*sf); + dh = round(sf < df ? dw/sf : min(sh, dh)); + } + else { + dw = round(sf >= df ? min(sw, dw) : dh*sf); + dh = round(sf >= df ? dw/sf : min(sh, dh)); + } + } + + m.sw = sw; + m.sh = sh; + m.dw = dw; + m.dh = dh; + m.multipass = api.multiPassResize; + return m; + }, + + _trans: function (fn){ + this._load(this.file, function (err, image){ + if( err ){ + fn(err); + } + else { + try { + this._apply(image, fn); + } catch (err){ + api.log('[err] FileAPI.Image.fn._apply:', err); + fn(err); + } + } + }); + }, + + + get: function (fn){ + if( api.support.transform ){ + var _this = this, matrix = _this.matrix; + + if( matrix.deg == 'auto' ){ + api.getInfo(_this.file, function (err, info){ + // rotate by exif orientation + matrix.deg = exifOrientation[info && info.exif && info.exif.Orientation] || 0; + _this._trans(fn); + }); + } + else { + _this._trans(fn); + } + } + else { + fn('not_support_transform'); + } + + return this; + }, + + + toData: function (fn){ + return this.get(fn); + } + + }; + + + Image.exifOrientation = exifOrientation; + + + Image.transform = function (file, transform, autoOrientation, fn){ + function _transform(err, img){ + // img -- info object + var + images = {} + , queue = api.queue(function (err){ + fn(err, images); + }) + ; + + if( !err ){ + api.each(transform, function (params, name){ + if( !queue.isFail() ){ + var ImgTrans = new Image(img.nodeType ? img : file), isFn = typeof params == 'function'; + + if( isFn ){ + params(img, ImgTrans); + } + else if( params.width ){ + ImgTrans[params.preview ? 'preview' : 'resize'](params.width, params.height, params.strategy); + } + else { + if( params.maxWidth && (img.width > params.maxWidth || img.height > params.maxHeight) ){ + ImgTrans.resize(params.maxWidth, params.maxHeight, 'max'); + } + } + + if( params.crop ){ + var crop = params.crop; + ImgTrans.crop(crop.x|0, crop.y|0, crop.w || crop.width, crop.h || crop.height); + } + + if( params.rotate === undef && autoOrientation ){ + params.rotate = 'auto'; + } + + ImgTrans.set({ type: ImgTrans.matrix.type || params.type || file.type || 'image/png' }); + + if( !isFn ){ + ImgTrans.set({ + deg: params.rotate + , overlay: params.overlay + , filter: params.filter + , quality: params.quality || 1 + }); + } + + queue.inc(); + ImgTrans.toData(function (err, image){ + if( err ){ + queue.fail(); + } + else { + images[name] = image; + queue.next(); + } + }); + } + }); + } + else { + queue.fail(); + } + } + + + // @todo: Оло-ло, нужно рефакторить это место + if( file.width ){ + _transform(false, file); + } else { + api.getInfo(file, _transform); + } + }; + + + // @const + api.each(['TOP', 'CENTER', 'BOTTOM'], function (x, i){ + api.each(['LEFT', 'CENTER', 'RIGHT'], function (y, j){ + Image[x+'_'+y] = i*3 + j; + Image[y+'_'+x] = i*3 + j; + }); + }); + + + /** + * Trabsform element to canvas + * + * @param {Image|HTMLVideoElement} el + * @returns {Canvas} + */ + Image.toCanvas = function(el){ + var canvas = document.createElement('canvas'); + canvas.width = el.videoWidth || el.width; + canvas.height = el.videoHeight || el.height; + canvas.getContext('2d').drawImage(el, 0, 0); + return canvas; + }; + + + /** + * Create image from DataURL + * @param {String} dataURL + * @param {Object} size + * @param {Function} callback + */ + Image.fromDataURL = function (dataURL, size, callback){ + var img = api.newImage(dataURL); + api.extend(img, size); + callback(img); + }; + + + /** + * Apply filter (caman.js) + * + * @param {Canvas|Image} canvas + * @param {String|Function} filter + * @param {Function} doneFn + */ + Image.applyFilter = function (canvas, filter, doneFn){ + if( typeof filter == 'function' ){ + filter(canvas, doneFn); + } + else if( window.Caman ){ + // http://camanjs.com/guides/ + window.Caman(canvas.tagName == 'IMG' ? Image.toCanvas(canvas) : canvas, function (){ + if( typeof filter == 'string' ){ + this[filter](); + } + else { + api.each(filter, function (val, method){ + this[method](val); + }, this); + } + this.render(doneFn); + }); + } + }; + + + /** + * For load-image-ios.js + */ + api.renderImageToCanvas = function (canvas, img, sx, sy, sw, sh, dx, dy, dw, dh){ + try { + return canvas.getContext('2d').drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh); + } catch (ex) { + api.log('renderImageToCanvas failed'); + throw ex; + } + }; + + + // @export + api.support.canvas = api.support.transform = support; + api.Image = Image; +})(FileAPI, document); + +/* + * JavaScript Load Image iOS scaling fixes 1.0.3 + * https://github.com/blueimp/JavaScript-Load-Image + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * iOS image scaling fixes based on + * https://github.com/stomita/ios-imagefile-megapixel + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, bitwise: true */ +/*global FileAPI, window, document */ + +(function (factory) { + 'use strict'; + factory(FileAPI); +}(function (loadImage) { + 'use strict'; + + // Only apply fixes on the iOS platform: + if (!window.navigator || !window.navigator.platform || + !(/iP(hone|od|ad)/).test(window.navigator.platform)) { + return; + } + + var originalRenderMethod = loadImage.renderImageToCanvas; + + // Detects subsampling in JPEG images: + loadImage.detectSubsampling = function (img) { + var canvas, + context; + if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images + canvas = document.createElement('canvas'); + canvas.width = canvas.height = 1; + context = canvas.getContext('2d'); + context.drawImage(img, -img.width + 1, 0); + // subsampled image becomes half smaller in rendering size. + // check alpha channel value to confirm image is covering edge pixel or not. + // if alpha value is 0 image is not covering, hence subsampled. + return context.getImageData(0, 0, 1, 1).data[3] === 0; + } + return false; + }; + + // Detects vertical squash in JPEG images: + loadImage.detectVerticalSquash = function (img, subsampled) { + var naturalHeight = img.naturalHeight || img.height, + canvas = document.createElement('canvas'), + context = canvas.getContext('2d'), + data, + sy, + ey, + py, + alpha; + if (subsampled) { + naturalHeight /= 2; + } + canvas.width = 1; + canvas.height = naturalHeight; + context.drawImage(img, 0, 0); + data = context.getImageData(0, 0, 1, naturalHeight).data; + // search image edge pixel position in case it is squashed vertically: + sy = 0; + ey = naturalHeight; + py = naturalHeight; + while (py > sy) { + alpha = data[(py - 1) * 4 + 3]; + if (alpha === 0) { + ey = py; + } else { + sy = py; + } + py = (ey + sy) >> 1; + } + return (py / naturalHeight) || 1; + }; + + // Renders image to canvas while working around iOS image scaling bugs: + // https://github.com/blueimp/JavaScript-Load-Image/issues/13 + loadImage.renderImageToCanvas = function ( + canvas, + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + destX, + destY, + destWidth, + destHeight + ) { + if (img._type === 'image/jpeg') { + var context = canvas.getContext('2d'), + tmpCanvas = document.createElement('canvas'), + tileSize = 1024, + tmpContext = tmpCanvas.getContext('2d'), + subsampled, + vertSquashRatio, + tileX, + tileY; + tmpCanvas.width = tileSize; + tmpCanvas.height = tileSize; + context.save(); + subsampled = loadImage.detectSubsampling(img); + if (subsampled) { + sourceX /= 2; + sourceY /= 2; + sourceWidth /= 2; + sourceHeight /= 2; + } + vertSquashRatio = loadImage.detectVerticalSquash(img, subsampled); + if (subsampled || vertSquashRatio !== 1) { + sourceY *= vertSquashRatio; + destWidth = Math.ceil(tileSize * destWidth / sourceWidth); + destHeight = Math.ceil( + tileSize * destHeight / sourceHeight / vertSquashRatio + ); + destY = 0; + tileY = 0; + while (tileY < sourceHeight) { + destX = 0; + tileX = 0; + while (tileX < sourceWidth) { + tmpContext.clearRect(0, 0, tileSize, tileSize); + tmpContext.drawImage( + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + -tileX, + -tileY, + sourceWidth, + sourceHeight + ); + context.drawImage( + tmpCanvas, + 0, + 0, + tileSize, + tileSize, + destX, + destY, + destWidth, + destHeight + ); + tileX += tileSize; + destX += destWidth; + } + tileY += tileSize; + destY += destHeight; + } + context.restore(); + return canvas; + } + } + return originalRenderMethod( + canvas, + img, + sourceX, + sourceY, + sourceWidth, + sourceHeight, + destX, + destY, + destWidth, + destHeight + ); + }; + +})); + +/*global window, FileAPI */ + +(function (api, window){ + "use strict"; + + var + document = window.document + , FormData = window.FormData + , Form = function (){ this.items = []; } + , encodeURIComponent = window.encodeURIComponent + ; + + + Form.prototype = { + + append: function (name, blob, file, type){ + this.items.push({ + name: name + , blob: blob && blob.blob || (blob == void 0 ? '' : blob) + , file: blob && (file || blob.name) + , type: blob && (type || blob.type) + }); + }, + + each: function (fn){ + var i = 0, n = this.items.length; + for( ; i < n; i++ ){ + fn.call(this, this.items[i]); + } + }, + + toData: function (fn, options){ + // allow chunked transfer if we have only one file to send + // flag is used below and in XHR._send + options._chunked = api.support.chunked && options.chunkSize > 0 && api.filter(this.items, function (item){ return item.file; }).length == 1; + + if( !api.support.html5 ){ + api.log('FileAPI.Form.toHtmlData'); + this.toHtmlData(fn); + } + else if( !api.formData || this.multipart || !FormData ){ + api.log('FileAPI.Form.toMultipartData'); + this.toMultipartData(fn); + } + else if( options._chunked ){ + api.log('FileAPI.Form.toPlainData'); + this.toPlainData(fn); + } + else { + api.log('FileAPI.Form.toFormData'); + this.toFormData(fn); + } + }, + + _to: function (data, complete, next, arg){ + var queue = api.queue(function (){ + complete(data); + }); + + this.each(function (file){ + next(file, data, queue, arg); + }); + + queue.check(); + }, + + + toHtmlData: function (fn){ + this._to(document.createDocumentFragment(), fn, function (file, data/**DocumentFragment*/){ + var blob = file.blob, hidden; + + if( file.file ){ + api.reset(blob, true); + // set new name + blob.name = file.name; + blob.disabled = false; + data.appendChild(blob); + } + else { + hidden = document.createElement('input'); + hidden.name = file.name; + hidden.type = 'hidden'; + hidden.value = blob; + data.appendChild(hidden); + } + }); + }, + + toPlainData: function (fn){ + this._to({}, fn, function (file, data, queue){ + if( file.file ){ + data.type = file.file; + } + + if( file.blob.toBlob ){ + // canvas + queue.inc(); + _convertFile(file, function (file, blob){ + data.name = file.name; + data.file = blob; + data.size = blob.length; + data.type = file.type; + queue.next(); + }); + } + else if( file.file ){ + // file + data.name = file.blob.name; + data.file = file.blob; + data.size = file.blob.size; + data.type = file.type; + } + else { + // additional data + if( !data.params ){ + data.params = []; + } + data.params.push(encodeURIComponent(file.name) +"="+ encodeURIComponent(file.blob)); + } + + data.start = -1; + data.end = data.file && data.file.FileAPIReadPosition || -1; + data.retry = 0; + }); + }, + + toFormData: function (fn){ + this._to(new FormData, fn, function (file, data, queue){ + if( file.blob && file.blob.toBlob ){ + queue.inc(); + _convertFile(file, function (file, blob){ + data.append(file.name, blob, file.file); + queue.next(); + }); + } + else if( file.file ){ + data.append(file.name, file.blob, file.file); + } + else { + data.append(file.name, file.blob); + } + + if( file.file ){ + data.append('_'+file.name, file.file); + } + }); + }, + + + toMultipartData: function (fn){ + this._to([], fn, function (file, data, queue, boundary){ + queue.inc(); + _convertFile(file, function (file, blob){ + data.push( + '--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '') + + (file.file ? '\r\nContent-Type: '+ (file.type || 'application/octet-stream') : '') + + '\r\n' + + '\r\n'+ (file.file ? blob : encodeURIComponent(blob)) + + '\r\n') + ); + queue.next(); + }, true); + }, api.expando); + } + }; + + + function _convertFile(file, fn, useBinaryString){ + var blob = file.blob, filename = file.file; + + if( filename ){ + if( !blob.toDataURL ){ + // The Blob is not an image. + api.readAsBinaryString(blob, function (evt){ + if( evt.type == 'load' ){ + fn(file, evt.result); + } + }); + return; + } + + var + mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' } + , type = mime[file.type] ? file.type : 'image/png' + , ext = mime[type] || '.png' + , quality = blob.quality || 1 + ; + + if( !filename.match(new RegExp(ext+'$', 'i')) ){ + // Does not change the current extension, but add a new one. + filename += ext.replace('?', ''); + } + + file.file = filename; + file.type = type; + + if( !useBinaryString && blob.toBlob ){ + blob.toBlob(function (blob){ + fn(file, blob); + }, type, quality); + } + else { + fn(file, api.toBinaryString(blob.toDataURL(type, quality))); + } + } + else { + fn(file, blob); + } + } + + + // @export + api.Form = Form; +})(FileAPI, window); + +/*global window, FileAPI, Uint8Array */ + +(function (window, api){ + "use strict"; + + var + noop = function (){} + , document = window.document + + , XHR = function (options){ + this.uid = api.uid(); + this.xhr = { + abort: noop + , getResponseHeader: noop + , getAllResponseHeaders: noop + }; + this.options = options; + }, + + _xhrResponsePostfix = { '': 1, XML: 1, Text: 1, Body: 1 } + ; + + + XHR.prototype = { + status: 0, + statusText: '', + constructor: XHR, + + getResponseHeader: function (name){ + return this.xhr.getResponseHeader(name); + }, + + getAllResponseHeaders: function (){ + return this.xhr.getAllResponseHeaders() || {}; + }, + + end: function (status, statusText){ + var _this = this, options = _this.options; + + _this.end = + _this.abort = noop; + _this.status = status; + + if( statusText ){ + _this.statusText = statusText; + } + + api.log('xhr.end:', status, statusText); + options.complete(status == 200 || status == 201 ? false : _this.statusText || 'unknown', _this); + + if( _this.xhr && _this.xhr.node ){ + setTimeout(function (){ + var node = _this.xhr.node; + try { node.parentNode.removeChild(node); } catch (e){} + try { delete window[_this.uid]; } catch (e){} + window[_this.uid] = _this.xhr.node = null; + }, 9); + } + }, + + abort: function (){ + this.end(0, 'abort'); + + if( this.xhr ){ + this.xhr.aborted = true; + this.xhr.abort(); + } + }, + + send: function (FormData){ + var _this = this, options = this.options; + + FormData.toData(function (data){ + // Start uploading + options.upload(options, _this); + _this._send.call(_this, options, data); + }, options); + }, + + _send: function (options, data){ + var _this = this, xhr, uid = _this.uid, onloadFuncName = _this.uid + "Load", url = options.url; + + api.log('XHR._send:', data); + + if( !options.cache ){ + // No cache + url += (~url.indexOf('?') ? '&' : '?') + api.uid(); + } + + if( data.nodeName ){ + var jsonp = options.jsonp; + + // prepare callback in GET + url = url.replace(/([a-z]+)=(\?)/i, '$1='+uid); + + // legacy + options.upload(options, _this); + + var + onPostMessage = function (evt){ + if( ~url.indexOf(evt.origin) ){ + try { + var result = api.parseJSON(evt.data); + if( result.id == uid ){ + complete(result.status, result.statusText, result.response); + } + } catch( err ){ + complete(0, err.message); + } + } + }, + + // jsonp-callack + complete = window[uid] = function (status, statusText, response){ + _this.readyState = 4; + _this.responseText = response; + _this.end(status, statusText); + + api.event.off(window, 'message', onPostMessage); + window[uid] = xhr = transport = window[onloadFuncName] = null; + } + ; + + _this.xhr.abort = function (){ + try { + if( transport.stop ){ transport.stop(); } + else if( transport.contentWindow.stop ){ transport.contentWindow.stop(); } + else { transport.contentWindow.document.execCommand('Stop'); } + } + catch (er) {} + complete(0, "abort"); + }; + + api.event.on(window, 'message', onPostMessage); + + window[onloadFuncName] = function (){ + try { + var + win = transport.contentWindow + , doc = win.document + , result = win.result || api.parseJSON(doc.body.innerHTML) + ; + complete(result.status, result.statusText, result.response); + } catch (e){ + api.log('[transport.onload]', e); + } + }; + + xhr = document.createElement('div'); + xhr.innerHTML = '

' + + '' + + (jsonp && (options.url.indexOf('=?') < 0) ? '' : '') + + '
' + ; + + // get form-data & transport + var + form = xhr.getElementsByTagName('form')[0] + , transport = xhr.getElementsByTagName('iframe')[0] + ; + + form.appendChild(data); + + api.log(form.parentNode.innerHTML); + + // append to DOM + document.body.appendChild(xhr); + + // keep a reference to node-transport + _this.xhr.node = xhr; + + // send + _this.readyState = 2; // loaded + form.submit(); + form = null; + } + else { + // Clean url + url = url.replace(/([a-z]+)=(\?)&?/i, ''); + + // html5 + if (this.xhr && this.xhr.aborted) { + api.log("Error: already aborted"); + return; + } + xhr = _this.xhr = api.getXHR(); + + if (data.params) { + url += (url.indexOf('?') < 0 ? "?" : "&") + data.params.join("&"); + } + + xhr.open('POST', url, true); + + if( api.withCredentials ){ + xhr.withCredentials = "true"; + } + + if( !options.headers || !options.headers['X-Requested-With'] ){ + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + } + + api.each(options.headers, function (val, key){ + xhr.setRequestHeader(key, val); + }); + + + if ( options._chunked ) { + // chunked upload + if( xhr.upload ){ + xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){ + if (!data.retry) { + // show progress only for correct chunk uploads + options.progress({ + type: evt.type + , total: data.size + , loaded: data.start + evt.loaded + , totalSize: data.size + }, _this, options); + } + }, 100), false); + } + + xhr.onreadystatechange = function (){ + var lkb = parseInt(xhr.getResponseHeader('X-Last-Known-Byte'), 10); + + _this.status = xhr.status; + _this.statusText = xhr.statusText; + _this.readyState = xhr.readyState; + + if( xhr.readyState == 4 ){ + try { + for( var k in _xhrResponsePostfix ){ + _this['response'+k] = xhr['response'+k]; + } + }catch(_){} + xhr.onreadystatechange = null; + + if (!xhr.status || xhr.status - 201 > 0) { + api.log("Error: " + xhr.status); + // some kind of error + // 0 - connection fail or timeout, if xhr.aborted is true, then it's not recoverable user action + // up - server error + if (((!xhr.status && !xhr.aborted) || 500 == xhr.status || 416 == xhr.status) && ++data.retry <= options.chunkUploadRetry) { + // let's try again the same chunk + // only applicable for recoverable error codes 500 && 416 + var delay = xhr.status ? 0 : api.chunkNetworkDownRetryTimeout; + + // inform about recoverable problems + options.pause(data.file, options); + + // smart restart if server reports about the last known byte + api.log("X-Last-Known-Byte: " + lkb); + if (lkb) { + data.end = lkb; + } else { + data.end = data.start - 1; + if (416 == xhr.status) { + data.end = data.end - options.chunkSize; + } + } + + setTimeout(function () { + _this._send(options, data); + }, delay); + } else { + // no mo retries + _this.end(xhr.status); + } + } else { + // success + data.retry = 0; + + if (data.end == data.size - 1) { + // finished + _this.end(xhr.status); + } else { + // next chunk + + // shift position if server reports about the last known byte + api.log("X-Last-Known-Byte: " + lkb); + if (lkb) { + data.end = lkb; + } + data.file.FileAPIReadPosition = data.end; + + setTimeout(function () { + _this._send(options, data); + }, 0); + } + } + + xhr = null; + } + }; + + data.start = data.end + 1; + data.end = Math.max(Math.min(data.start + options.chunkSize, data.size) - 1, data.start); + + // Retrieve a slice of file + var + file = data.file + , slice = (file.slice || file.mozSlice || file.webkitSlice).call(file, data.start, data.end + 1) + ; + + if( data.size && !slice.size ){ + setTimeout(function (){ + _this.end(-1); + }); + } else { + xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size); + xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + encodeURIComponent(data.name)); + xhr.setRequestHeader("Content-Type", data.type || "application/octet-stream"); + + xhr.send(slice); + } + + file = slice = null; + } else { + // single piece upload + if( xhr.upload ){ + // https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29 + xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){ + options.progress(evt, _this, options); + }, 100), false); + } + + xhr.onreadystatechange = function (){ + _this.status = xhr.status; + _this.statusText = xhr.statusText; + _this.readyState = xhr.readyState; + + if( xhr.readyState == 4 ){ + for( var k in _xhrResponsePostfix ){ + _this['response'+k] = xhr['response'+k]; + } + xhr.onreadystatechange = null; + + if (!xhr.status || xhr.status > 201) { + api.log("Error: " + xhr.status); + if (((!xhr.status && !xhr.aborted) || 500 == xhr.status) && (options.retry || 0) < options.uploadRetry) { + options.retry = (options.retry || 0) + 1; + var delay = api.networkDownRetryTimeout; + + // inform about recoverable problems + options.pause(options.file, options); + + setTimeout(function () { + _this._send(options, data); + }, delay); + } else { + //success + _this.end(xhr.status); + } + } else { + //success + _this.end(xhr.status); + } + + xhr = null; + } + }; + + if( api.isArray(data) ){ + // multipart + xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando); + var rawData = data.join('') +'--_'+ api.expando +'--'; + + /** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */ + if( xhr.sendAsBinary ){ + xhr.sendAsBinary(rawData); + } + else { + var bytes = Array.prototype.map.call(rawData, function(c){ return c.charCodeAt(0) & 0xff; }); + xhr.send(new Uint8Array(bytes).buffer); + + } + } else { + // FormData + xhr.send(data); + } + } + } + } + }; + + + // @export + api.XHR = XHR; +})(window, FileAPI); + +/** + * @class FileAPI.Camera + * @author RubaXa + * @support Chrome 21+, FF 18+, Opera 12+ + */ + +/*global window, FileAPI, jQuery */ +/** @namespace LocalMediaStream -- https://developer.mozilla.org/en-US/docs/WebRTC/MediaStream_API#LocalMediaStream */ +(function (window, api){ + "use strict"; + + var + URL = window.URL || window.webkitURL, + + document = window.document, + navigator = window.navigator, + + getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia, + + html5 = !!getMedia + ; + + + // Support "media" + api.support.media = html5; + + + var Camera = function (video){ + this.video = video; + }; + + + Camera.prototype = { + isActive: function (){ + return !!this._active; + }, + + + /** + * Start camera streaming + * @param {Function} callback + */ + start: function (callback){ + var + _this = this + , video = _this.video + , _successId + , _failId + , _complete = function (err){ + _this._active = !err; + clearTimeout(_failId); + clearTimeout(_successId); +// api.event.off(video, 'loadedmetadata', _complete); + callback && callback(err, _this); + } + ; + + getMedia.call(navigator, { video: true }, function (stream/**LocalMediaStream*/){ + // Success + _this.stream = stream; + +// api.event.on(video, 'loadedmetadata', function (){ +// _complete(null); +// }); + + // Set camera stream + video.src = URL.createObjectURL(stream); + + // Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia. + // See crbug.com/110938. + _successId = setInterval(function (){ + if( _detectVideoSignal(video) ){ + _complete(null); + } + }, 1000); + + _failId = setTimeout(function (){ + _complete('timeout'); + }, 5000); + + // Go-go-go! + video.play(); + }, _complete/*error*/); + }, + + + /** + * Stop camera streaming + */ + stop: function (){ + try { + this._active = false; + this.video.pause(); + this.stream.stop(); + } catch( err ){ } + }, + + + /** + * Create screenshot + * @return {FileAPI.Camera.Shot} + */ + shot: function (){ + return new Shot(this.video); + } + }; + + + /** + * Get camera element from container + * + * @static + * @param {HTMLElement} el + * @return {Camera} + */ + Camera.get = function (el){ + return new Camera(el.firstChild); + }; + + + /** + * Publish camera element into container + * + * @static + * @param {HTMLElement} el + * @param {Object} options + * @param {Function} [callback] + */ + Camera.publish = function (el, options, callback){ + if( typeof options == 'function' ){ + callback = options; + options = {}; + } + + // Dimensions of "camera" + options = api.extend({}, { + width: '100%' + , height: '100%' + , start: true + }, options); + + + if( el.jquery ){ + // Extract first element, from jQuery collection + el = el[0]; + } + + + var doneFn = function (err){ + if( err ){ + callback(err); + } + else { + // Get camera + var cam = Camera.get(el); + if( options.start ){ + cam.start(callback); + } + else { + callback(null, cam); + } + } + }; + + + el.style.width = _px(options.width); + el.style.height = _px(options.height); + + + if( api.html5 && html5 ){ + // Create video element + var video = document.createElement('video'); + + // Set dimensions + video.style.width = _px(options.width); + video.style.height = _px(options.height); + + // Clean container + if( window.jQuery ){ + jQuery(el).empty(); + } else { + el.innerHTML = ''; + } + + // Add "camera" to container + el.appendChild(video); + + // end + doneFn(); + } + else { + Camera.fallback(el, options, doneFn); + } + }; + + + Camera.fallback = function (el, options, callback){ + callback('not_support_camera'); + }; + + + /** + * @class FileAPI.Camera.Shot + */ + var Shot = function (video){ + var canvas = video.nodeName ? api.Image.toCanvas(video) : video; + var shot = api.Image(canvas); + shot.type = 'image/png'; + shot.width = canvas.width; + shot.height = canvas.height; + shot.size = canvas.width * canvas.height * 4; + return shot; + }; + + + /** + * Add "px" postfix, if value is a number + * + * @private + * @param {*} val + * @return {String} + */ + function _px(val){ + return val >= 0 ? val + 'px' : val; + } + + + /** + * @private + * @param {HTMLVideoElement} video + * @return {Boolean} + */ + function _detectVideoSignal(video){ + var canvas = document.createElement('canvas'), ctx, res = false; + try { + ctx = canvas.getContext('2d'); + ctx.drawImage(video, 0, 0, 1, 1); + res = ctx.getImageData(0, 0, 1, 1).data[4] != 255; + } + catch( e ){} + return res; + } + + + // @export + Camera.Shot = Shot; + api.Camera = Camera; +})(window, FileAPI); + +/** + * FileAPI fallback to Flash + * + * @flash-developer "Vladimir Demidov" + */ + +/*global window, ActiveXObject, FileAPI */ +(function (window, jQuery, api) { + "use strict"; + + var + document = window.document + , location = window.location + , navigator = window.navigator + , _each = api.each + ; + + + api.support.flash = (function (){ + var mime = navigator.mimeTypes, has = false; + + if( navigator.plugins && typeof navigator.plugins['Shockwave Flash'] == 'object' ){ + has = navigator.plugins['Shockwave Flash'].description && !(mime && mime['application/x-shockwave-flash'] && !mime['application/x-shockwave-flash'].enabledPlugin); + } + else { + try { + has = !!(window.ActiveXObject && new ActiveXObject('ShockwaveFlash.ShockwaveFlash')); + } + catch(er){ + api.log('Flash -- does not supported.'); + } + } + + if( has && /^file:/i.test(location) ){ + api.log('[warn] Flash does not work on `file:` protocol.'); + } + + return has; + })(); + + + api.support.flash + && (0 + || !api.html5 || !api.support.html5 + || (api.cors && !api.support.cors) + || (api.media && !api.support.media) + ) + && (function (){ + var + _attr = api.uid() + , _retry = 0 + , _files = {} + , _rhttp = /^https?:/i + + , flash = { + _fn: {}, + + + /** + * Publish flash-object + * + * @param {HTMLElement} el + * @param {String} id + * @param {Object} [opts] + */ + publish: function (el, id, opts){ + opts = opts || {}; + el.innerHTML = _makeFlashHTML({ + id: id + , src: _getUrl(api.flashUrl, 'r=' + api.version) +// , src: _getUrl('http://v.demidov.boom.corp.mail.ru/uploaderfileapi/FlashFileAPI.swf?1') + , wmode: opts.camera ? '' : 'transparent' + , flashvars: 'callback=' + (opts.onEvent || 'FileAPI.Flash.onEvent') + + '&flashId='+ id + + '&storeKey='+ navigator.userAgent.match(/\d/ig).join('') +'_'+ api.version + + (flash.isReady || (api.pingUrl ? '&ping='+api.pingUrl : '')) + + '&timeout='+api.flashAbortTimeout + + (opts.camera ? '&useCamera=' + _getUrl(api.flashWebcamUrl) : '') + + '&debug='+(api.debug?"1":"") + }, opts); + }, + + + /** + * Initialization & preload flash object + */ + init: function (){ + var child = document.body && document.body.firstChild; + + if( child ){ + do { + if( child.nodeType == 1 ){ + api.log('FlashAPI.state: awaiting'); + + var dummy = document.createElement('div'); + + dummy.id = '_' + _attr; + + _css(dummy, { + top: 1 + , right: 1 + , width: 5 + , height: 5 + , position: 'absolute' + , zIndex: 1e6+'' // set max zIndex + }); + + child.parentNode.insertBefore(dummy, child); + flash.publish(dummy, _attr); + + return; + } + } + while( child = child.nextSibling ); + } + + if( _retry < 10 ){ + setTimeout(flash.init, ++_retry*50); + } + }, + + + ready: function (){ + api.log('FlashAPI.state: ready'); + + flash.ready = api.F; + flash.isReady = true; + flash.patch(); + flash.patchCamera && flash.patchCamera(); + api.event.on(document, 'mouseover', flash.mouseover); + api.event.on(document, 'click', function (evt){ + if( flash.mouseover(evt) ){ + evt.preventDefault + ? evt.preventDefault() + : (evt.returnValue = true) + ; + } + }); + }, + + + getEl: function (){ + return document.getElementById('_'+_attr); + }, + + + getWrapper: function (node){ + do { + if( /js-fileapi-wrapper/.test(node.className) ){ + return node; + } + } + while( (node = node.parentNode) && (node !== document.body) ); + }, + + disableMouseover: false, + + mouseover: function (evt){ + if (!flash.disableMouseover) { + var target = api.event.fix(evt).target; + + if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){ + var + state = target.getAttribute(_attr) + , wrapper = flash.getWrapper(target) + ; + + if( api.multiFlash ){ + // check state: + // i — published + // i — initialization + // r — ready + if( state == 'i' || state == 'r' ){ + // publish fail + return false; + } + else if( state != 'p' ){ + // set "init" state + target.setAttribute(_attr, 'i'); + + var dummy = document.createElement('div'); + + if( !wrapper ){ + api.log('[err] FlashAPI.mouseover: js-fileapi-wrapper not found'); + return; + } + + _css(dummy, { + top: 0 + , left: 0 + , width: target.offsetWidth + , height: target.offsetHeight + , zIndex: 1e6+'' // set max zIndex + , position: 'absolute' + }); + + wrapper.appendChild(dummy); + flash.publish(dummy, api.uid()); + + // set "publish" state + target.setAttribute(_attr, 'p'); + } + + return true; + } + else if( wrapper ){ + // Use one flash element + var box = _getDimensions(wrapper); + _css(flash.getEl(), box); + + // Set current input + flash.curInp = target; + } + } + else if( !/object|embed/i.test(target.nodeName) ){ + _css(flash.getEl(), { top: 1, left: 1, width: 5, height: 5 }); + } + } + }, + + onEvent: function (evt){ + var type = evt.type; + + if( type == 'ready' ){ + try { + // set "ready" state + flash.getInput(evt.flashId).setAttribute(_attr, 'r'); + } catch (e){ + } + + flash.ready(); + setTimeout(function (){ flash.mouseenter(evt); }, 50); + return true; + } + else if( type === 'ping' ){ + api.log('(flash -> js).ping:', [evt.status, evt.savedStatus], evt.error); + } + else if( type === 'log' ){ + api.log('(flash -> js).log:', evt.target); + } + else if( type in flash ){ + setTimeout(function (){ + api.log('FlashAPI.event.'+evt.type+':', evt); + flash[type](evt); + }, 1); + } + }, + mouseDown: function(evt) { + flash.disableMouseover = true; + }, + cancel: function(evt) { + flash.disableMouseover = false; + }, + mouseenter: function (evt){ + var node = flash.getInput(evt.flashId); + + if( node ){ + // Set multiple mode + flash.cmd(evt, 'multiple', node.getAttribute('multiple') != null); + + + // Set files filter + var accept = [], exts = {}; + + _each((node.getAttribute('accept') || '').split(/,\s*/), function (mime){ + api.accept[mime] && _each(api.accept[mime].split(' '), function (ext){ + exts[ext] = 1; + }); + }); + + _each(exts, function (i, ext){ + accept.push( ext ); + }); + + flash.cmd(evt, 'accept', accept.length ? accept.join(',')+','+accept.join(',').toUpperCase() : '*'); + } + }, + + + get: function (id){ + return document[id] || window[id] || document.embeds[id]; + }, + + + getInput: function (id){ + if( api.multiFlash ){ + try { + var node = flash.getWrapper(flash.get(id)); + if( node ){ + return node.getElementsByTagName('input')[0]; + } + } catch (e){ + api.log('[err] Can not find "input" by flashId:', id, e); + } + } else { + return flash.curInp; + } + }, + + + select: function (evt){ + try { + var + inp = flash.getInput(evt.flashId) + , uid = api.uid(inp) + , files = evt.target.files + , event + ; + _each(files, function (file){ + api.checkFileObj(file); + }); + + _files[uid] = files; + + if( document.createEvent ){ + event = document.createEvent('Event'); + event.files = files; + event.initEvent('change', true, true); + inp.dispatchEvent(event); + } + else if( jQuery ){ + jQuery(inp).trigger({ type: 'change', files: files }); + } + else { + event = document.createEventObject(); + event.files = files; + inp.fireEvent('onchange', event); + } + } finally { + flash.disableMouseover = false; + } + }, + + interval: null, + cmd: function (id, name, data, last) { + if (flash.uploadInProgress && flash.readInProgress) { + setTimeout(function() { + flash.cmd(id, name, data, last); + }, 100); + } else { + this.cmdFn(id, name, data, last); + } + }, + + cmdFn: function(id, name, data, last) { + try { + api.log('(js -> flash).'+name+':', data); + return flash.get(id.flashId || id).cmd(name, data); + } catch (e){ + api.log('(js -> flash).onError:', e); + if( !last ){ + // try again + setTimeout(function (){ flash.cmd(id, name, data, true); }, 50); + } + } + }, + + patch: function (){ + api.flashEngine = true; + + // FileAPI + _inherit(api, { + readAsDataURL: function (file, callback){ + if( _isHtmlFile(file) ){ + this.parent.apply(this, arguments); + } + else { + api.log('FlashAPI.readAsBase64'); + flash.readInProgress = true; + flash.cmd(file, 'readAsBase64', { + id: file.id, + callback: _wrap(function _(err, base64){ + flash.readInProgress = false; + _unwrap(_); + + api.log('FlashAPI.readAsBase64:', err); + + callback({ + type: err ? 'error' : 'load' + , error: err + , result: 'data:'+ file.type +';base64,'+ base64 + }); + }) + }); + } + }, + + readAsText: function (file, encoding, callback){ + if( callback ){ + api.log('[warn] FlashAPI.readAsText not supported `encoding` param'); + } else { + callback = encoding; + } + + api.readAsDataURL(file, function (evt){ + if( evt.type == 'load' ){ + try { + evt.result = window.atob(evt.result.split(';base64,')[1]); + } catch( err ){ + evt.type = 'error'; + evt.error = err.toString(); + } + } + callback(evt); + }); + }, + + getFiles: function (input, filter, callback){ + if( callback ){ + api.filterFiles(api.getFiles(input), filter, callback); + return null; + } + + var files = api.isArray(input) ? input : _files[api.uid(input.target || input.srcElement || input)]; + + + if( !files ){ + // Файлов нету, вызываем родительский метод + return this.parent.apply(this, arguments); + } + + + if( filter ){ + filter = api.getFilesFilter(filter); + files = api.filter(files, function (file){ return filter.test(file.name); }); + } + + return files; + }, + + + getInfo: function (file, fn){ + if( _isHtmlFile(file) ){ + this.parent.apply(this, arguments); + } + else if( file.isShot ){ + fn(null, file.info = { + width: file.width, + height: file.height + }); + } + else { + if( !file.__info ){ + var defer = file.__info = api.defer(); + +// flash.cmd(file, 'getFileInfo', { +// id: file.id +// , callback: _wrap(function _(err, info){ +// _unwrap(_); +// defer.resolve(err, file.info = info); +// }) +// }); + defer.resolve(null, file.info = null); + + } + + file.__info.then(fn); + } + } + }); + + + // FileAPI.Image + api.support.transform = true; + api.Image && _inherit(api.Image.prototype, { + get: function (fn, scaleMode){ + this.set({ scaleMode: scaleMode || 'noScale' }); // noScale, exactFit + return this.parent(fn); + }, + + _load: function (file, fn){ + api.log('FlashAPI.Image._load:', file); + + if( _isHtmlFile(file) ){ + this.parent.apply(this, arguments); + } + else { + var _this = this; + api.getInfo(file, function (err){ + fn.call(_this, err, file); + }); + } + }, + + _apply: function (file, fn){ + api.log('FlashAPI.Image._apply:', file); + + if( _isHtmlFile(file) ){ + this.parent.apply(this, arguments); + } + else { + var m = this.getMatrix(file.info), doneFn = fn; + + flash.cmd(file, 'imageTransform', { + id: file.id + , matrix: m + , callback: _wrap(function _(err, base64){ + api.log('FlashAPI.Image._apply.callback:', err); + _unwrap(_); + + if( err ){ + doneFn(err); + } + else if( !api.support.html5 && (!api.support.dataURI || base64.length > 3e4) ){ + _makeFlashImage({ + width: (m.deg % 180) ? m.dh : m.dw + , height: (m.deg % 180) ? m.dw : m.dh + , scale: m.scaleMode + }, base64, doneFn); + } + else { + if( m.filter ){ + doneFn = function (err, img){ + if( err ){ + fn(err); + } + else { + api.Image.applyFilter(img, m.filter, function (){ + fn(err, this.canvas); + }); + } + }; + } + + api.newImage('data:'+ file.type +';base64,'+ base64, doneFn); + } + }) + }); + } + }, + + toData: function (fn){ + var + file = this.file + , info = file.info + , matrix = this.getMatrix(info) + ; + api.log('FlashAPI.Image.toData'); + + if( _isHtmlFile(file) ){ + this.parent.apply(this, arguments); + } + else { + if( matrix.deg == 'auto' ){ + matrix.deg = api.Image.exifOrientation[info && info.exif && info.exif.Orientation] || 0; + } + + fn.call(this, !file.info, { + id: file.id + , flashId: file.flashId + , name: file.name + , type: file.type + , matrix: matrix + }); + } + } + }); + + + api.Image && _inherit(api.Image, { + fromDataURL: function (dataURL, size, callback){ + if( !api.support.dataURI || dataURL.length > 3e4 ){ + _makeFlashImage( + api.extend({ scale: 'exactFit' }, size) + , dataURL.replace(/^data:[^,]+,/, '') + , function (err, el){ callback(el); } + ); + } + else { + this.parent(dataURL, size, callback); + } + } + }); + + // FileAPI.Form + _inherit(api.Form.prototype, { + toData: function (fn){ + var items = this.items, i = items.length; + + for( ; i--; ){ + if( items[i].file && _isHtmlFile(items[i].blob) ){ + return this.parent.apply(this, arguments); + } + } + + api.log('FlashAPI.Form.toData'); + fn(items); + } + }); + + + // FileAPI.XHR + _inherit(api.XHR.prototype, { + _send: function (options, formData){ + if( + formData.nodeName + || formData.append && api.support.html5 + || api.isArray(formData) && (typeof formData[0] === 'string') + ){ + // HTML5, Multipart or IFrame + return this.parent.apply(this, arguments); + } + + + var + data = {} + , files = {} + , _this = this + , flashId + , fileId + ; + + _each(formData, function (item){ + if( item.file ){ + files[item.name] = item = _getFileDescr(item.blob); + fileId = item.id; + flashId = item.flashId; + } + else { + data[item.name] = item.blob; + } + }); + + if( !fileId ){ + flashId = _attr; + } + + if( !flashId ){ + api.log('[err] FlashAPI._send: flashId -- undefined'); + return this.parent.apply(this, arguments); + } + else { + api.log('FlashAPI.XHR._send: '+ flashId +' -> '+ fileId); + } + + _this.xhr = { + headers: {}, + abort: function (){ flash.uploadInProgress = false; flash.cmd(flashId, 'abort', { id: fileId }); }, + getResponseHeader: function (name){ return this.headers[name]; }, + getAllResponseHeaders: function (){ return this.headers; } + }; + + var queue = api.queue(function (){ + flash.uploadInProgress = true; + flash.cmd(flashId, 'upload', { + url: _getUrl(options.url.replace(/([a-z]+)=(\?)&?/i, '')) + , data: data + , files: fileId ? files : null + , headers: options.headers || {} + , callback: _wrap(function upload(evt){ + var type = evt.type, result = evt.result; + + api.log('FlashAPI.upload.'+type); + + if( type == 'progress' ){ + evt.loaded = Math.min(evt.loaded, evt.total); // @todo fixme + evt.lengthComputable = true; + options.progress(evt); + } + else if( type == 'complete' ){ + flash.uploadInProgress = false; + _unwrap(upload); + + if( typeof result == 'string' ){ + _this.responseText = result.replace(/%22/g, "\"").replace(/%5c/g, "\\").replace(/%26/g, "&").replace(/%25/g, "%"); + } + + _this.end(evt.status || 200); + } + else if( type == 'abort' || type == 'error' ){ + flash.uploadInProgress = false; + _this.end(evt.status || 0, evt.message); + _unwrap(upload); + } + }) + }); + }); + + + // #2174: FileReference.load() call while FileReference.upload() or vice versa + _each(files, function (file){ + queue.inc(); + api.getInfo(file, queue.next); + }); + + queue.check(); + } + }); + } + } + ; + + + function _makeFlashHTML(opts){ + return ('' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '').replace(/#(\w+)#/ig, function (a, name){ return opts[name]; }) + ; + } + + + function _css(el, css){ + if( el && el.style ){ + var key, val; + for( key in css ){ + val = css[key]; + if( typeof val == 'number' ){ + val += 'px'; + } + try { el.style[key] = val; } catch (e) {} + } + + } + } + + + function _inherit(obj, methods){ + _each(methods, function (fn, name){ + var prev = obj[name]; + obj[name] = function (){ + this.parent = prev; + return fn.apply(this, arguments); + }; + }); + } + + function _isHtmlFile(file){ + return file && !file.flashId; + } + + function _wrap(fn){ + var id = fn.wid = api.uid(); + flash._fn[id] = fn; + return 'FileAPI.Flash._fn.'+id; + } + + + function _unwrap(fn){ + try { + flash._fn[fn.wid] = null; + delete flash._fn[fn.wid]; + } + catch(e){} + } + + + function _getUrl(url, params){ + if( !_rhttp.test(url) ){ + if( /^\.\//.test(url) || '/' != url.charAt(0) ){ + var path = location.pathname; + path = path.substr(0, path.lastIndexOf('/')); + url = (path +'/'+ url).replace('/./', '/'); + } + + if( '//' != url.substr(0, 2) ){ + url = '//' + location.host + url; + } + + if( !_rhttp.test(url) ){ + url = location.protocol + url; + } + } + + if( params ){ + url += (/\?/.test(url) ? '&' : '?') + params; + } + + return url; + } + + + function _makeFlashImage(opts, base64, fn){ + var + key + , flashId = api.uid() + , el = document.createElement('div') + , attempts = 10 + ; + + for( key in opts ){ + el.setAttribute(key, opts[key]); + el[key] = opts[key]; + } + + _css(el, opts); + + opts.width = '100%'; + opts.height = '100%'; + + el.innerHTML = _makeFlashHTML(api.extend({ + id: flashId + , src: _getUrl(api.flashImageUrl, 'r='+ api.uid()) + , wmode: 'opaque' + , flashvars: 'scale='+ opts.scale +'&callback='+_wrap(function _(){ + _unwrap(_); + if( --attempts > 0 ){ + _setImage(); + } + return true; + }) + }, opts)); + + function _setImage(){ + try { + // Get flash-object by id + var img = flash.get(flashId); + img.setImage(base64); + } catch (e){ + api.log('[err] FlashAPI.Preview.setImage -- can not set "base64":', e); + } + } + + fn(false, el); + el = null; + } + + + function _getFileDescr(file){ + return { + id: file.id + , name: file.name + , matrix: file.matrix + , flashId: file.flashId + }; + } + + + function _getDimensions(el){ + var + box = el.getBoundingClientRect() + , body = document.body + , docEl = (el && el.ownerDocument).documentElement + ; + + function getOffset(obj) { + var left, top; + left = top = 0; + if (obj.offsetParent) { + do { + left += obj.offsetLeft; + top += obj.offsetTop; + } while (obj = obj.offsetParent); + } + return { + left : left, + top : top + }; + }; + + return { + top: getOffset(el).top + , left: getOffset(el).left + , width: el.offsetWidth + , height: el.offsetHeight + }; + } + + // @export + api.Flash = flash; + + + // Check dataURI support + api.newImage('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', function (err, img){ + api.support.dataURI = !(img.width != 1 || img.height != 1); + flash.init(); + }); + })(); +})(window, window.jQuery, FileAPI); + +/** + * FileAPI fallback to Flash + * + * @flash-developer "Vladimir Demidov" + */ + +/*global window, FileAPI */ +(function (window, jQuery, api) { + "use strict"; + + var _each = api.each, + _cameraQueue = []; + + + if (api.support.flash && (api.media && !api.support.media)) { + (function () { + + function _wrap(fn) { + var id = fn.wid = api.uid(); + api.Flash._fn[id] = fn; + return 'FileAPI.Flash._fn.' + id; + } + + + function _unwrap(fn) { + try { + api.Flash._fn[fn.wid] = null; + delete api.Flash._fn[fn.wid]; + } catch (e) { + } + } + + var flash = api.Flash; + api.extend(api.Flash, { + + patchCamera: function () { + api.Camera.fallback = function (el, options, callback) { + var camId = api.uid(); + api.log('FlashAPI.Camera.publish: ' + camId); + flash.publish(el, camId, api.extend(options, { + camera: true, + onEvent: _wrap(function _(evt) { + if (evt.type === 'camera') { + _unwrap(_); + + if (evt.error) { + api.log('FlashAPI.Camera.publish.error: ' + evt.error); + callback(evt.error); + } else { + api.log('FlashAPI.Camera.publish.success: ' + camId); + callback(null); + } + } + }) + })); + }; + // Run + _each(_cameraQueue, function (args) { + api.Camera.fallback.apply(api.Camera, args); + }); + _cameraQueue = []; + + + // FileAPI.Camera:proto + api.extend(api.Camera.prototype, { + _id: function () { + return this.video.id; + }, + + start: function (callback) { + var _this = this; + flash.cmd(this._id(), 'camera.on', { + callback: _wrap(function _(evt) { + _unwrap(_); + + if (evt.error) { + api.log('FlashAPI.camera.on.error: ' + evt.error); + callback(evt.error, _this); + } else { + api.log('FlashAPI.camera.on.success: ' + _this._id()); + _this._active = true; + callback(null, _this); + } + }) + }); + }, + + stop: function () { + this._active = false; + flash.cmd(this._id(), 'camera.off'); + }, + + shot: function () { + api.log('FlashAPI.Camera.shot:', this._id()); + + var shot = api.Flash.cmd(this._id(), 'shot', {}); + shot.type = 'image/png'; + shot.flashId = this._id(); + shot.isShot = true; + + return new api.Camera.Shot(shot); + } + }); + } + }); + + api.Camera.fallback = function () { + _cameraQueue.push(arguments); + }; + + }()); + } +}(window, window.jQuery, FileAPI)); +if( typeof define === "function" && define.amd ){ define("FileAPI", [], function (){ return FileAPI; }); } diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.min.js b/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.min.js new file mode 100755 index 0000000..d82fa61 --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/FileAPI.min.js @@ -0,0 +1,6 @@ +/*! 12.2.11 */ +/*! FileAPI 2.0.7 - BSD | git://github.com/mailru/FileAPI.git + * FileAPI — a set of javascript tools for working with files. Multiupload, drag'n'drop and chunked file upload. Images: crop, resize and auto orientation by EXIF. + */ +!function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.Blob&&function(){try{return Boolean(new Blob)}catch(a){return!1}}(),d=c&&a.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(a){return!1}}(),e=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,f=(c||e)&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,f,g,h,i,j;for(b=a.split(",")[0].indexOf("base64")>=0?atob(a.split(",")[1]):decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f),h=0;hd;d++)d in a&&b.call(c,a[d],d,a);else for(var f in a)a.hasOwnProperty(f)&&b.call(c,a[f],f,a)},S=function(a){for(var b=arguments,c=1,d=function(b,c){a[c]=b};c=c&&!d&&f.end()},isFail:function(){return d},fail:function(){!d&&a(d=!0)},end:function(){e||(e=!0,a())}};return f},each:R,afor:function(a,b){var c=0,d=a.length;Q(a)&&d--?!function e(){b(d!=c&&e,a[c],c++)}():b(!1)},extend:S,isFile:function(a){return"[object File]"===H.call(a)},isBlob:function(a){return this.isFile(a)||"[object Blob]"===H.call(a)},isCanvas:function(a){return a&&D.test(a.nodeName)},getFilesFilter:function(a){return a="string"==typeof a?a:a.getAttribute&&a.getAttribute("accept")||"",a?new RegExp("("+a.replace(/\./g,"\\.").replace(/,/g,"|")+")$","i"):/./},readAsDataURL:function(a,b){Y.isCanvas(a)?c(a,b,"load",Y.toDataURL(a)):e(a,b,"DataURL")},readAsBinaryString:function(a,b){d("BinaryString")?e(a,b,"BinaryString"):e(a,function(a){if("load"==a.type)try{a.result=Y.toBinaryString(a.result)}catch(c){a.type="error",a.message=c.toString()}b(a)},"DataURL")},readAsArrayBuffer:function(a,b){e(a,b,"ArrayBuffer")},readAsText:function(a,b,c){c||(c=b,b="utf-8"),e(a,c,"Text",b)},toDataURL:function(a,b){return"string"==typeof a?a:a.toDataURL?a.toDataURL(b||"image/png"):void 0},toBinaryString:function(b){return a.atob(Y.toDataURL(b).replace(G,""))},readAsImage:function(a,d,e){if(Y.isFile(a))if(r){var f=r.createObjectURL(a);f===b?c(a,d,"error"):Y.readAsImage(f,d,e)}else Y.readAsDataURL(a,function(b){"load"==b.type?Y.readAsImage(b.result,d,e):(e||"error"==b.type)&&c(a,d,b,null,{loaded:b.loaded,total:b.total})});else if(Y.isCanvas(a))c(a,d,"load",a);else if(C.test(a.nodeName))if(a.complete)c(a,d,"load",a);else{var g="error abort load";V(a,g,function i(b){"load"==b.type&&r&&r.revokeObjectURL(a.src),U(a,g,i),c(a,d,b,a)})}else if(a.iframe)c(a,d,{type:"error"});else{var h=Y.newImage(a.dataURL||a);Y.readAsImage(h,d,e)}},checkFileObj:function(a){var b={},c=Y.accept;return"object"==typeof a?b=a:b.name=(a+"").split(/\\|\//g).pop(),null==b.type&&(b.type=b.name.split(".").pop()),R(c,function(a,c){a=new RegExp(a.replace(/\s/g,"|"),"i"),(a.test(b.type)||Y.ext2mime[b.type])&&(b.type=Y.ext2mime[b.type]||c.split("/")[0]+"/"+b.type)}),b},getDropFiles:function(a,b){var c=[],d=k(a),e=Q(d.items)&&d.items[0]&&g(d.items[0]),i=Y.queue(function(){b(c)});R((e?d.items:d.files)||[],function(a){i.inc();try{e?h(a,function(a,b){a?Y.log("[err] getDropFiles:",a):c.push.apply(c,b),i.next()}):f(a,function(b){b&&c.push(a),i.next()})}catch(b){i.next(),Y.log("[err] getDropFiles: ",b)}}),i.check()},getFiles:function(a,b,c){var d=[];return c?(Y.filterFiles(Y.getFiles(a),b,c),null):(a.jquery&&(a.each(function(){d=d.concat(Y.getFiles(this))}),a=d,d=[]),"string"==typeof b&&(b=Y.getFilesFilter(b)),a.originalEvent?a=W(a.originalEvent):a.srcElement&&(a=W(a)),a.dataTransfer?a=a.dataTransfer:a.target&&(a=a.target),a.files?(d=a.files,y||(d[0].blob=a,d[0].iframe=!0)):!y&&j(a)?Y.trim(a.value)&&(d=[Y.checkFileObj(a.value)],d[0].blob=a,d[0].iframe=!0):Q(a)&&(d=a),Y.filter(d,function(a){return!b||b.test(a.name)}))},getTotalSize:function(a){for(var b=0,c=a&&a.length;c--;)b+=a[c].size;return b},getInfo:function(a,b){var c={},d=L.concat();Y.isFile(a)?!function e(){var f=d.shift();f?f.test(a.type)?f(a,function(a,d){a?b(a):(S(c,d),e())}):e():b(!1,c)}():b("not_support_info",c)},addInfoReader:function(a,b){b.test=function(b){return a.test(b)},L.push(b)},filter:function(a,b){for(var c,d=[],e=0,f=a.length;f>e;e++)e in a&&(c=a[e],b.call(c,c,e,a)&&d.push(c));return d},filterFiles:function(a,b,c){if(a.length){var d,e=a.concat(),f=[],g=[];!function h(){e.length?(d=e.shift(),Y.getInfo(d,function(a,c){(b(d,a?!1:c)?f:g).push(d),h()})):c(f,g)}()}else c([],a)},upload:function(a){a=S({jsonp:"callback",prepare:Y.F,beforeupload:Y.F,upload:Y.F,fileupload:Y.F,fileprogress:Y.F,filecomplete:Y.F,progress:Y.F,complete:Y.F,pause:Y.F,imageOriginal:!0,chunkSize:Y.chunkSize,chunkUploadRetry:Y.chunkUploadRetry,uploadRetry:Y.uploadRetry},a),a.imageAutoOrientation&&!a.imageTransform&&(a.imageTransform={rotate:"auto"});var b,c=new Y.XHR(a),d=this._getFilesDataArray(a.files),e=this,f=0,g=0,h=!1;return R(d,function(a){f+=a.size}),c.files=[],R(d,function(a){c.files.push(a.file)}),c.total=f,c.loaded=0,c.filesLeft=d.length,a.beforeupload(c,a),b=function(){var j=d.shift(),k=j&&j.file,l=!1,m=i(a);if(c.filesLeft=d.length,k&&k.name===Y.expando&&(k=null,Y.log("[warn] FileAPI.upload() — called without files")),("abort"!=c.statusText||c.current)&&j){if(h=!1,c.currentFile=k,k&&a.prepare(k,m)===!1)return void b.call(e);m.file=k,e._getFormData(m,j,function(h){g||a.upload(c,a);var i=new Y.XHR(S({},m,{upload:k?function(){a.fileupload(k,i,m)}:n,progress:k?function(b){l||(l=b.loaded===b.total,a.fileprogress({type:"progress",total:j.total=b.total,loaded:j.loaded=b.loaded},k,i,m),a.progress({type:"progress",total:f,loaded:c.loaded=g+j.size*(b.loaded/b.total)|0},k,i,m))}:n,complete:function(d){R(N,function(a){c[a]=i[a]}),k&&(j.total=j.total||j.size,j.loaded=j.total,d||(this.progress(j),l=!0,g+=j.size,c.loaded=g),a.filecomplete(d,i,k,m)),setTimeout(function(){b.call(e)},0)}}));c.abort=function(a){a||(d.length=0),this.current=a,i.abort()},i.send(h)})}else{var o=200==c.status||201==c.status||204==c.status;a.complete(o?!1:c.statusText||"error",c,a),h=!0}},setTimeout(b,0),c.append=function(a,g){a=Y._getFilesDataArray([].concat(a)),R(a,function(a){f+=a.size,c.files.push(a.file),g?d.unshift(a):d.push(a)}),c.statusText="",h&&b.call(e)},c.remove=function(a){for(var b,c=d.length;c--;)d[c].file==a&&(b=d.splice(c,1),f-=b.size);return b},c},_getFilesDataArray:function(a){var b=[],c={};if(j(a)){var d=Y.getFiles(a);c[a.name||"file"]=null!==a.getAttribute("multiple")?d:d[0]}else Q(a)&&j(a[0])?R(a,function(a){c[a.name||"file"]=Y.getFiles(a)}):c=a;return R(c,function e(a,c){Q(a)?R(a,function(a){e(a,c)}):a&&(a.name||a.image)&&b.push({name:c,file:a,size:a.size,total:a.size,loaded:0})}),b.length||b.push({file:{name:Y.expando}}),b},_getFormData:function(a,b,c){var d=b.file,e=b.name,f=d.name,g=d.type,h=Y.support.transform&&a.imageTransform,i=new Y.Form,j=Y.queue(function(){c(i)}),k=h&&l(h),m=Y.postNameConcat;R(a.data,function n(a,b){"object"==typeof a?R(a,function(a,c){n(a,m(b,c))}):i.append(b,a)}),function o(b){b.image?(j.inc(),b.toData(function(a,b){f=f||(new Date).getTime()+".png",o(b),j.next()})):Y.Image&&h&&(/^image/.test(b.type)||E.test(b.nodeName))?(j.inc(),k&&(h=[h]),Y.Image.transform(b,h,a.imageAutoOrientation,function(c,d){if(k&&!c)B||Y.flashEngine||(i.multipart=!0),i.append(e,d[0],f,h[0].type||g);else{var l=0;c||R(d,function(a,b){B||Y.flashEngine||(i.multipart=!0),h[b].postName||(l=1),i.append(h[b].postName||m(e,b),a,f,h[b].type||g)}),(c||a.imageOriginal)&&i.append(m(e,l?"original":null),b,f,g)}j.next()})):f!==Y.expando&&i.append(e,b,f)}(d),j.check()},reset:function(a,b){var c,d;return x?(d=x(a).clone(!0).insertBefore(a).val("")[0],b||x(a).remove()):(c=a.parentNode,d=c.insertBefore(a.cloneNode(!0),a),d.value="",b||c.removeChild(a),R(K[Y.uid(a)],function(b,c){R(b,function(b){U(a,c,b),T(d,c,b)})})),d},load:function(a,b){var c=Y.getXHR();return c?(c.open("GET",a,!0),c.overrideMimeType&&c.overrideMimeType("text/plain; charset=x-user-defined"),T(c,"progress",function(a){a.lengthComputable&&b({type:a.type,loaded:a.loaded,total:a.total},c)}),c.onreadystatechange=function(){if(4==c.readyState)if(c.onreadystatechange=null,200==c.status){a=a.split("/");var d={name:a[a.length-1],size:c.getResponseHeader("Content-Length"),type:c.getResponseHeader("Content-Type")};d.dataURL="data:"+d.type+";base64,"+Y.encode64(c.responseBody||c.responseText),b({type:"load",result:d},c)}else b({type:"error"},c)},c.send(null)):b({type:"error"}),c},encode64:function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",c="",d=0;for("string"!=typeof a&&(a=String(a));d>2,k=(3&g)<<4|h>>4;isNaN(h)?e=f=64:(e=(15&h)<<2|i>>6,f=isNaN(i)?64:63&i),c+=b.charAt(j)+b.charAt(k)+b.charAt(e)+b.charAt(f)}return c}};Y.addInfoReader(/^image/,function(a,b){if(!a.__dimensions){var c=a.__dimensions=Y.defer();Y.readAsImage(a,function(a){var b=a.target;c.resolve("load"==a.type?!1:"error",{width:b.width,height:b.height}),b.src=Y.EMPTY_PNG,b=null})}a.__dimensions.then(b)}),Y.event.dnd=function(a,b,c){var d,e;c||(c=b,b=Y.F),u?(T(a,"dragenter dragleave dragover",b.ff=b.ff||function(a){for(var c=k(a).types,f=c&&c.length,g=!1;f--;)if(~c[f].indexOf("File")){a[P](),e!==a.type&&(e=a.type,"dragleave"!=e&&b.call(a[O],!0,a),g=!0);break}g&&(clearTimeout(d),d=setTimeout(function(){b.call(a[O],"dragleave"!=e,a)},50))}),T(a,"drop",c.ff=c.ff||function(a){a[P](),e=0,b.call(a[O],!1,a),Y.getDropFiles(a,function(b){c.call(a[O],b,a)})})):Y.log("Drag'n'Drop -- not supported")},Y.event.dnd.off=function(a,b,c){U(a,"dragenter dragleave dragover",b.ff),U(a,"drop",c.ff)},x&&!x.fn.dnd&&(x.fn.dnd=function(a,b){return this.each(function(){Y.event.dnd(this,a,b)})},x.fn.offdnd=function(a,b){return this.each(function(){Y.event.dnd.off(this,a,b)})}),a.FileAPI=S(Y,a.FileAPI),Y.log("FileAPI: "+Y.version),Y.log("protocol: "+a.location.protocol),Y.log("doctype: ["+p.name+"] "+p.publicId+" "+p.systemId),R(o.getElementsByTagName("meta"),function(a){/x-ua-compatible/i.test(a.getAttribute("http-equiv"))&&Y.log("meta.http-equiv: "+a.getAttribute("content"))});try{Y._supportConsoleLog=!!console.log,Y._supportConsoleLogApply=!!console.log.apply}catch(Z){}Y.flashUrl||(Y.flashUrl=Y.staticPath+"FileAPI.flash.swf"),Y.flashImageUrl||(Y.flashImageUrl=Y.staticPath+"FileAPI.flash.image.swf"),Y.flashWebcamUrl||(Y.flashWebcamUrl=Y.staticPath+"FileAPI.flash.camera.swf")}(window,void 0),function(a,b,c){"use strict";function d(b){if(b instanceof d){var c=new d(b.file);return a.extend(c.matrix,b.matrix),c}return this instanceof d?(this.file=b,this.size=b.size||100,void(this.matrix={sx:0,sy:0,sw:0,sh:0,dx:0,dy:0,dw:0,dh:0,resize:0,deg:0,quality:1,filter:0})):new d(b)}var e=Math.min,f=Math.round,g=function(){return b.createElement("canvas")},h=!1,i={8:270,3:180,6:90,7:270,4:180,5:90};try{h=g().toDataURL("image/png").indexOf("data:image/png")>-1}catch(j){}d.prototype={image:!0,constructor:d,set:function(b){return a.extend(this.matrix,b),this},crop:function(a,b,d,e){return d===c&&(d=a,e=b,a=b=0),this.set({sx:a,sy:b,sw:d,sh:e||d})},resize:function(a,b,c){return/min|max/.test(b)&&(c=b,b=a),this.set({dw:a,dh:b||a,resize:c})},preview:function(a,b){return this.resize(a,b||a,"preview")},rotate:function(a){return this.set({deg:a})},filter:function(a){return this.set({filter:a})},overlay:function(a){return this.set({overlay:a})},clone:function(){return new d(this)},_load:function(b,c){var d=this;/img|video/i.test(b.nodeName)?c.call(d,null,b):a.readAsImage(b,function(a){c.call(d,"load"!=a.type,a.result)})},_apply:function(b,c){var f,h=g(),i=this.getMatrix(b),j=h.getContext("2d"),k=b.videoWidth||b.width,l=b.videoHeight||b.height,m=i.deg,n=i.dw,o=i.dh,p=k,q=l,r=i.filter,s=b,t=i.overlay,u=a.queue(function(){b.src=a.EMPTY_PNG,c(!1,h)}),v=a.renderImageToCanvas;for(m-=360*Math.floor(m/360),b._type=this.file.type;i.multipass&&e(p/n,q/o)>2;)p=p/2+.5|0,q=q/2+.5|0,f=g(),f.width=p,f.height=q,s!==b?(v(f,s,0,0,s.width,s.height,0,0,p,q),s=f):(s=f,v(s,b,i.sx,i.sy,i.sw,i.sh,0,0,p,q),i.sx=i.sy=i.sw=i.sh=0);h.width=m%180?o:n,h.height=m%180?n:o,h.type=i.type,h.quality=i.quality,j.rotate(m*Math.PI/180),v(j.canvas,s,i.sx,i.sy,i.sw||s.width,i.sh||s.height,180==m||270==m?-n:0,90==m||180==m?-o:0,n,o),n=h.width,o=h.height,t&&a.each([].concat(t),function(b){u.inc();var c=new window.Image,d=function(){var e=0|b.x,f=0|b.y,g=b.w||c.width,h=b.h||c.height,i=b.rel;e=1==i||4==i||7==i?(n-g+e)/2:2==i||5==i||8==i?n-(g+e):e,f=3==i||4==i||5==i?(o-h+f)/2:i>=6?o-(h+f):f,a.event.off(c,"error load abort",d);try{j.globalAlpha=b.opacity||1,j.drawImage(c,e,f,g,h)}catch(k){}u.next()};a.event.on(c,"error load abort",d),c.src=b.src,c.complete&&d()}),r&&(u.inc(),d.applyFilter(h,r,u.next)),u.check()},getMatrix:function(b){var c=a.extend({},this.matrix),d=c.sw=c.sw||b.videoWidth||b.naturalWidth||b.width,g=c.sh=c.sh||b.videoHeight||b.naturalHeight||b.height,h=c.dw=c.dw||d,i=c.dh=c.dh||g,j=d/g,k=h/i,l=c.resize;if("preview"==l){if(h!=d||i!=g){var m,n;k>=j?(m=d,n=m/k):(n=g,m=n*k),(m!=d||n!=g)&&(c.sx=~~((d-m)/2),c.sy=~~((g-n)/2),d=m,g=n)}}else l&&(d>h||g>i?"min"==l?(h=f(k>j?e(d,h):i*j),i=f(k>j?h/j:e(g,i))):(h=f(j>=k?e(d,h):i*j),i=f(j>=k?h/j:e(g,i))):(h=d,i=g));return c.sw=d,c.sh=g,c.dw=h,c.dh=i,c.multipass=a.multiPassResize,c},_trans:function(b){this._load(this.file,function(c,d){if(c)b(c);else try{this._apply(d,b)}catch(c){a.log("[err] FileAPI.Image.fn._apply:",c),b(c)}})},get:function(b){if(a.support.transform){var c=this,d=c.matrix;"auto"==d.deg?a.getInfo(c.file,function(a,e){d.deg=i[e&&e.exif&&e.exif.Orientation]||0,c._trans(b)}):c._trans(b)}else b("not_support_transform");return this},toData:function(a){return this.get(a)}},d.exifOrientation=i,d.transform=function(b,e,f,g){function h(h,i){var j={},k=a.queue(function(a){g(a,j)});h?k.fail():a.each(e,function(a,e){if(!k.isFail()){var g=new d(i.nodeType?i:b),h="function"==typeof a;if(h?a(i,g):a.width?g[a.preview?"preview":"resize"](a.width,a.height,a.strategy):a.maxWidth&&(i.width>a.maxWidth||i.height>a.maxHeight)&&g.resize(a.maxWidth,a.maxHeight,"max"),a.crop){var l=a.crop;g.crop(0|l.x,0|l.y,l.w||l.width,l.h||l.height)}a.rotate===c&&f&&(a.rotate="auto"),g.set({type:g.matrix.type||a.type||b.type||"image/png"}),h||g.set({deg:a.rotate,overlay:a.overlay,filter:a.filter,quality:a.quality||1}),k.inc(),g.toData(function(a,b){a?k.fail():(j[e]=b,k.next())})}})}b.width?h(!1,b):a.getInfo(b,h)},a.each(["TOP","CENTER","BOTTOM"],function(b,c){a.each(["LEFT","CENTER","RIGHT"],function(a,e){d[b+"_"+a]=3*c+e,d[a+"_"+b]=3*c+e})}),d.toCanvas=function(a){var c=b.createElement("canvas");return c.width=a.videoWidth||a.width,c.height=a.videoHeight||a.height,c.getContext("2d").drawImage(a,0,0),c},d.fromDataURL=function(b,c,d){var e=a.newImage(b);a.extend(e,c),d(e)},d.applyFilter=function(b,c,e){"function"==typeof c?c(b,e):window.Caman&&window.Caman("IMG"==b.tagName?d.toCanvas(b):b,function(){"string"==typeof c?this[c]():a.each(c,function(a,b){this[b](a)},this),this.render(e)})},a.renderImageToCanvas=function(b,c,d,e,f,g,h,i,j,k){try{return b.getContext("2d").drawImage(c,d,e,f,g,h,i,j,k)}catch(l){throw a.log("renderImageToCanvas failed"),l}},a.support.canvas=a.support.transform=h,a.Image=d}(FileAPI,document),function(a){"use strict";a(FileAPI)}(function(a){"use strict";if(window.navigator&&window.navigator.platform&&/iP(hone|od|ad)/.test(window.navigator.platform)){var b=a.renderImageToCanvas;a.detectSubsampling=function(a){var b,c;return a.width*a.height>1048576?(b=document.createElement("canvas"),b.width=b.height=1,c=b.getContext("2d"),c.drawImage(a,-a.width+1,0),0===c.getImageData(0,0,1,1).data[3]):!1},a.detectVerticalSquash=function(a,b){var c,d,e,f,g,h=a.naturalHeight||a.height,i=document.createElement("canvas"),j=i.getContext("2d");for(b&&(h/=2),i.width=1,i.height=h,j.drawImage(a,0,0),c=j.getImageData(0,0,1,h).data,d=0,e=h,f=h;f>d;)g=c[4*(f-1)+3],0===g?e=f:d=f,f=e+d>>1;return f/h||1},a.renderImageToCanvas=function(c,d,e,f,g,h,i,j,k,l){if("image/jpeg"===d._type){var m,n,o,p,q=c.getContext("2d"),r=document.createElement("canvas"),s=1024,t=r.getContext("2d");if(r.width=s,r.height=s,q.save(),m=a.detectSubsampling(d),m&&(e/=2,f/=2,g/=2,h/=2),n=a.detectVerticalSquash(d,m),m||1!==n){for(f*=n,k=Math.ceil(s*k/g),l=Math.ceil(s*l/h/n),j=0,p=0;h>p;){for(i=0,o=0;g>o;)t.clearRect(0,0,s,s),t.drawImage(d,e,f,g,h,-o,-p,g,h),q.drawImage(r,0,0,s,s,i,j,k,l),o+=s,i+=k;p+=s,j+=l}return q.restore(),c}}return b(c,d,e,f,g,h,i,j,k,l)}}}),function(a,b){"use strict";function c(b,c,d){var e=b.blob,f=b.file;if(f){if(!e.toDataURL)return void a.readAsBinaryString(e,function(a){"load"==a.type&&c(b,a.result)});var g={"image/jpeg":".jpe?g","image/png":".png"},h=g[b.type]?b.type:"image/png",i=g[h]||".png",j=e.quality||1;f.match(new RegExp(i+"$","i"))||(f+=i.replace("?","")),b.file=f,b.type=h,!d&&e.toBlob?e.toBlob(function(a){c(b,a)},h,j):c(b,a.toBinaryString(e.toDataURL(h,j)))}else c(b,e)}var d=b.document,e=b.FormData,f=function(){this.items=[]},g=b.encodeURIComponent;f.prototype={append:function(a,b,c,d){this.items.push({name:a,blob:b&&b.blob||(void 0==b?"":b),file:b&&(c||b.name),type:b&&(d||b.type)})},each:function(a){for(var b=0,c=this.items.length;c>b;b++)a.call(this,this.items[b])},toData:function(b,c){c._chunked=a.support.chunked&&c.chunkSize>0&&1==a.filter(this.items,function(a){return a.file}).length,a.support.html5?a.formData&&!this.multipart&&e?c._chunked?(a.log("FileAPI.Form.toPlainData"),this.toPlainData(b)):(a.log("FileAPI.Form.toFormData"),this.toFormData(b)):(a.log("FileAPI.Form.toMultipartData"),this.toMultipartData(b)):(a.log("FileAPI.Form.toHtmlData"),this.toHtmlData(b))},_to:function(b,c,d,e){var f=a.queue(function(){c(b)});this.each(function(a){d(a,b,f,e)}),f.check()},toHtmlData:function(b){this._to(d.createDocumentFragment(),b,function(b,c){var e,f=b.blob;b.file?(a.reset(f,!0),f.name=b.name,f.disabled=!1,c.appendChild(f)):(e=d.createElement("input"),e.name=b.name,e.type="hidden",e.value=f,c.appendChild(e))})},toPlainData:function(a){this._to({},a,function(a,b,d){a.file&&(b.type=a.file),a.blob.toBlob?(d.inc(),c(a,function(a,c){b.name=a.name,b.file=c,b.size=c.length,b.type=a.type,d.next()})):a.file?(b.name=a.blob.name,b.file=a.blob,b.size=a.blob.size,b.type=a.type):(b.params||(b.params=[]),b.params.push(g(a.name)+"="+g(a.blob))),b.start=-1,b.end=b.file&&b.file.FileAPIReadPosition||-1,b.retry=0})},toFormData:function(a){this._to(new e,a,function(a,b,d){a.blob&&a.blob.toBlob?(d.inc(),c(a,function(a,c){b.append(a.name,c,a.file),d.next()})):a.file?b.append(a.name,a.blob,a.file):b.append(a.name,a.blob),a.file&&b.append("_"+a.name,a.file)})},toMultipartData:function(b){this._to([],b,function(a,b,d,e){d.inc(),c(a,function(a,c){b.push("--_"+e+('\r\nContent-Disposition: form-data; name="'+a.name+'"'+(a.file?'; filename="'+g(a.file)+'"':"")+(a.file?"\r\nContent-Type: "+(a.type||"application/octet-stream"):"")+"\r\n\r\n"+(a.file?c:g(c))+"\r\n")),d.next()},!0)},a.expando)}},a.Form=f}(FileAPI,window),function(a,b){"use strict";var c=function(){},d=a.document,e=function(a){this.uid=b.uid(),this.xhr={abort:c,getResponseHeader:c,getAllResponseHeaders:c},this.options=a},f={"":1,XML:1,Text:1,Body:1};e.prototype={status:0,statusText:"",constructor:e,getResponseHeader:function(a){return this.xhr.getResponseHeader(a)},getAllResponseHeaders:function(){return this.xhr.getAllResponseHeaders()||{}},end:function(d,e){var f=this,g=f.options;f.end=f.abort=c,f.status=d,e&&(f.statusText=e),b.log("xhr.end:",d,e),g.complete(200==d||201==d?!1:f.statusText||"unknown",f),f.xhr&&f.xhr.node&&setTimeout(function(){var b=f.xhr.node;try{b.parentNode.removeChild(b)}catch(c){}try{delete a[f.uid]}catch(c){}a[f.uid]=f.xhr.node=null},9)},abort:function(){this.end(0,"abort"),this.xhr&&(this.xhr.aborted=!0,this.xhr.abort())},send:function(a){var b=this,c=this.options;a.toData(function(a){c.upload(c,b),b._send.call(b,c,a)},c)},_send:function(c,e){var g,h=this,i=h.uid,j=h.uid+"Load",k=c.url;if(b.log("XHR._send:",e),c.cache||(k+=(~k.indexOf("?")?"&":"?")+b.uid()),e.nodeName){var l=c.jsonp;k=k.replace(/([a-z]+)=(\?)/i,"$1="+i),c.upload(c,h);var m=function(a){if(~k.indexOf(a.origin))try{var c=b.parseJSON(a.data);c.id==i&&n(c.status,c.statusText,c.response)}catch(d){n(0,d.message)}},n=a[i]=function(c,d,e){h.readyState=4,h.responseText=e,h.end(c,d),b.event.off(a,"message",m),a[i]=g=p=a[j]=null};h.xhr.abort=function(){try{p.stop?p.stop():p.contentWindow.stop?p.contentWindow.stop():p.contentWindow.document.execCommand("Stop")}catch(a){}n(0,"abort")},b.event.on(a,"message",m),a[j]=function(){try{var a=p.contentWindow,c=a.document,d=a.result||b.parseJSON(c.body.innerHTML);n(d.status,d.statusText,d.response)}catch(e){b.log("[transport.onload]",e)}},g=d.createElement("div"),g.innerHTML='
'+(l&&c.url.indexOf("=?")<0?'':"")+"
";var o=g.getElementsByTagName("form")[0],p=g.getElementsByTagName("iframe")[0];o.appendChild(e),b.log(o.parentNode.innerHTML),d.body.appendChild(g),h.xhr.node=g,h.readyState=2,o.submit(),o=null}else{if(k=k.replace(/([a-z]+)=(\?)&?/i,""),this.xhr&&this.xhr.aborted)return void b.log("Error: already aborted");if(g=h.xhr=b.getXHR(),e.params&&(k+=(k.indexOf("?")<0?"?":"&")+e.params.join("&")),g.open("POST",k,!0),b.withCredentials&&(g.withCredentials="true"),c.headers&&c.headers["X-Requested-With"]||g.setRequestHeader("X-Requested-With","XMLHttpRequest"),b.each(c.headers,function(a,b){g.setRequestHeader(b,a)}),c._chunked){g.upload&&g.upload.addEventListener("progress",b.throttle(function(a){e.retry||c.progress({type:a.type,total:e.size,loaded:e.start+a.loaded,totalSize:e.size},h,c)},100),!1),g.onreadystatechange=function(){var a=parseInt(g.getResponseHeader("X-Last-Known-Byte"),10);if(h.status=g.status,h.statusText=g.statusText,h.readyState=g.readyState,4==g.readyState){try{for(var d in f)h["response"+d]=g["response"+d]}catch(i){}if(g.onreadystatechange=null,!g.status||g.status-201>0)if(b.log("Error: "+g.status),(!g.status&&!g.aborted||500==g.status||416==g.status)&&++e.retry<=c.chunkUploadRetry){var j=g.status?0:b.chunkNetworkDownRetryTimeout;c.pause(e.file,c),b.log("X-Last-Known-Byte: "+a),a?e.end=a:(e.end=e.start-1,416==g.status&&(e.end=e.end-c.chunkSize)),setTimeout(function(){h._send(c,e)},j)}else h.end(g.status);else e.retry=0,e.end==e.size-1?h.end(g.status):(b.log("X-Last-Known-Byte: "+a),a&&(e.end=a),e.file.FileAPIReadPosition=e.end,setTimeout(function(){h._send(c,e)},0));g=null}},e.start=e.end+1,e.end=Math.max(Math.min(e.start+c.chunkSize,e.size)-1,e.start);var q=e.file,r=(q.slice||q.mozSlice||q.webkitSlice).call(q,e.start,e.end+1);e.size&&!r.size?setTimeout(function(){h.end(-1)}):(g.setRequestHeader("Content-Range","bytes "+e.start+"-"+e.end+"/"+e.size),g.setRequestHeader("Content-Disposition","attachment; filename="+encodeURIComponent(e.name)),g.setRequestHeader("Content-Type",e.type||"application/octet-stream"),g.send(r)),q=r=null}else if(g.upload&&g.upload.addEventListener("progress",b.throttle(function(a){c.progress(a,h,c)},100),!1),g.onreadystatechange=function(){if(h.status=g.status,h.statusText=g.statusText,h.readyState=g.readyState,4==g.readyState){for(var a in f)h["response"+a]=g["response"+a];if(g.onreadystatechange=null,!g.status||g.status>201)if(b.log("Error: "+g.status),(!g.status&&!g.aborted||500==g.status)&&(c.retry||0)=0?a+"px":a}function d(a){var b,c=f.createElement("canvas"),d=!1;try{b=c.getContext("2d"),b.drawImage(a,0,0,1,1),d=255!=b.getImageData(0,0,1,1).data[4]}catch(e){}return d}var e=a.URL||a.webkitURL,f=a.document,g=a.navigator,h=g.getUserMedia||g.webkitGetUserMedia||g.mozGetUserMedia||g.msGetUserMedia,i=!!h;b.support.media=i;var j=function(a){this.video=a};j.prototype={isActive:function(){return!!this._active},start:function(a){var b,c,f=this,i=f.video,j=function(d){f._active=!d,clearTimeout(c),clearTimeout(b),a&&a(d,f)};h.call(g,{video:!0},function(a){f.stream=a,i.src=e.createObjectURL(a),b=setInterval(function(){d(i)&&j(null)},1e3),c=setTimeout(function(){j("timeout")},5e3),i.play()},j)},stop:function(){try{this._active=!1,this.video.pause(),this.stream.stop()}catch(a){}},shot:function(){return new k(this.video)}},j.get=function(a){return new j(a.firstChild)},j.publish=function(d,e,g){"function"==typeof e&&(g=e,e={}),e=b.extend({},{width:"100%",height:"100%",start:!0},e),d.jquery&&(d=d[0]);var h=function(a){if(a)g(a);else{var b=j.get(d);e.start?b.start(g):g(null,b)}};if(d.style.width=c(e.width),d.style.height=c(e.height),b.html5&&i){var k=f.createElement("video");k.style.width=c(e.width),k.style.height=c(e.height),a.jQuery?jQuery(d).empty():d.innerHTML="",d.appendChild(k),h()}else j.fallback(d,e,h)},j.fallback=function(a,b,c){c("not_support_camera")};var k=function(a){var c=a.nodeName?b.Image.toCanvas(a):a,d=b.Image(c);return d.type="image/png",d.width=c.width,d.height=c.height,d.size=c.width*c.height*4,d};j.Shot=k,b.Camera=j}(window,FileAPI),function(a,b,c){"use strict";var d=a.document,e=a.location,f=a.navigator,g=c.each;c.support.flash=function(){var b=f.mimeTypes,d=!1;if(f.plugins&&"object"==typeof f.plugins["Shockwave Flash"])d=f.plugins["Shockwave Flash"].description&&!(b&&b["application/x-shockwave-flash"]&&!b["application/x-shockwave-flash"].enabledPlugin); +else try{d=!(!a.ActiveXObject||!new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))}catch(g){c.log("Flash -- does not supported.")}return d&&/^file:/i.test(e)&&c.log("[warn] Flash does not work on `file:` protocol."),d}(),c.support.flash&&(0||!c.html5||!c.support.html5||c.cors&&!c.support.cors||c.media&&!c.support.media)&&function(){function h(a){return('').replace(/#(\w+)#/gi,function(b,c){return a[c]})}function i(a,b){if(a&&a.style){var c,d;for(c in b){d=b[c],"number"==typeof d&&(d+="px");try{a.style[c]=d}catch(e){}}}}function j(a,b){g(b,function(b,c){var d=a[c];a[c]=function(){return this.parent=d,b.apply(this,arguments)}})}function k(a){return a&&!a.flashId}function l(a){var b=a.wid=c.uid();return v._fn[b]=a,"FileAPI.Flash._fn."+b}function m(a){try{v._fn[a.wid]=null,delete v._fn[a.wid]}catch(b){}}function n(a,b){if(!u.test(a)){if(/^\.\//.test(a)||"/"!=a.charAt(0)){var c=e.pathname;c=c.substr(0,c.lastIndexOf("/")),a=(c+"/"+a).replace("/./","/")}"//"!=a.substr(0,2)&&(a="//"+e.host+a),u.test(a)||(a=e.protocol+a)}return b&&(a+=(/\?/.test(a)?"&":"?")+b),a}function o(a,b,e){function f(){try{var a=v.get(j);a.setImage(b)}catch(d){c.log('[err] FlashAPI.Preview.setImage -- can not set "base64":',d)}}var g,j=c.uid(),k=d.createElement("div"),o=10;for(g in a)k.setAttribute(g,a[g]),k[g]=a[g];i(k,a),a.width="100%",a.height="100%",k.innerHTML=h(c.extend({id:j,src:n(c.flashImageUrl,"r="+c.uid()),wmode:"opaque",flashvars:"scale="+a.scale+"&callback="+l(function p(){return m(p),--o>0&&f(),!0})},a)),e(!1,k),k=null}function p(a){return{id:a.id,name:a.name,matrix:a.matrix,flashId:a.flashId}}function q(a){function b(a){var b,c;if(b=c=0,a.offsetParent)do b+=a.offsetLeft,c+=a.offsetTop;while(a=a.offsetParent);return{left:b,top:c}}a.getBoundingClientRect(),d.body,(a&&a.ownerDocument).documentElement;return{top:b(a).top,left:b(a).left,width:a.offsetWidth,height:a.offsetHeight}}var r=c.uid(),s=0,t={},u=/^https?:/i,v={_fn:{},publish:function(a,b,d){d=d||{},a.innerHTML=h({id:b,src:n(c.flashUrl,"r="+c.version),wmode:d.camera?"":"transparent",flashvars:"callback="+(d.onEvent||"FileAPI.Flash.onEvent")+"&flashId="+b+"&storeKey="+f.userAgent.match(/\d/gi).join("")+"_"+c.version+(v.isReady||(c.pingUrl?"&ping="+c.pingUrl:""))+"&timeout="+c.flashAbortTimeout+(d.camera?"&useCamera="+n(c.flashWebcamUrl):"")+"&debug="+(c.debug?"1":"")},d)},init:function(){var a=d.body&&d.body.firstChild;if(a)do if(1==a.nodeType){c.log("FlashAPI.state: awaiting");var b=d.createElement("div");return b.id="_"+r,i(b,{top:1,right:1,width:5,height:5,position:"absolute",zIndex:1e6+""}),a.parentNode.insertBefore(b,a),void v.publish(b,r)}while(a=a.nextSibling);10>s&&setTimeout(v.init,50*++s)},ready:function(){c.log("FlashAPI.state: ready"),v.ready=c.F,v.isReady=!0,v.patch(),v.patchCamera&&v.patchCamera(),c.event.on(d,"mouseover",v.mouseover),c.event.on(d,"click",function(a){v.mouseover(a)&&(a.preventDefault?a.preventDefault():a.returnValue=!0)})},getEl:function(){return d.getElementById("_"+r)},getWrapper:function(a){do if(/js-fileapi-wrapper/.test(a.className))return a;while((a=a.parentNode)&&a!==d.body)},disableMouseover:!1,mouseover:function(a){if(!v.disableMouseover){var b=c.event.fix(a).target;if(/input/i.test(b.nodeName)&&"file"==b.type&&!b.disabled){var e=b.getAttribute(r),f=v.getWrapper(b);if(c.multiFlash){if("i"==e||"r"==e)return!1;if("p"!=e){b.setAttribute(r,"i");var g=d.createElement("div");if(!f)return void c.log("[err] FlashAPI.mouseover: js-fileapi-wrapper not found");i(g,{top:0,left:0,width:b.offsetWidth,height:b.offsetHeight,zIndex:1e6+"",position:"absolute"}),f.appendChild(g),v.publish(g,c.uid()),b.setAttribute(r,"p")}return!0}if(f){var h=q(f);i(v.getEl(),h),v.curInp=b}}else/object|embed/i.test(b.nodeName)||i(v.getEl(),{top:1,left:1,width:5,height:5})}},onEvent:function(a){var b=a.type;if("ready"==b){try{v.getInput(a.flashId).setAttribute(r,"r")}catch(d){}return v.ready(),setTimeout(function(){v.mouseenter(a)},50),!0}"ping"===b?c.log("(flash -> js).ping:",[a.status,a.savedStatus],a.error):"log"===b?c.log("(flash -> js).log:",a.target):b in v&&setTimeout(function(){c.log("FlashAPI.event."+a.type+":",a),v[b](a)},1)},mouseDown:function(){v.disableMouseover=!0},cancel:function(){v.disableMouseover=!1},mouseenter:function(a){var b=v.getInput(a.flashId);if(b){v.cmd(a,"multiple",null!=b.getAttribute("multiple"));var d=[],e={};g((b.getAttribute("accept")||"").split(/,\s*/),function(a){c.accept[a]&&g(c.accept[a].split(" "),function(a){e[a]=1})}),g(e,function(a,b){d.push(b)}),v.cmd(a,"accept",d.length?d.join(",")+","+d.join(",").toUpperCase():"*")}},get:function(b){return d[b]||a[b]||d.embeds[b]},getInput:function(a){if(!c.multiFlash)return v.curInp;try{var b=v.getWrapper(v.get(a));if(b)return b.getElementsByTagName("input")[0]}catch(d){c.log('[err] Can not find "input" by flashId:',a,d)}},select:function(a){try{var e,f=v.getInput(a.flashId),h=c.uid(f),i=a.target.files;g(i,function(a){c.checkFileObj(a)}),t[h]=i,d.createEvent?(e=d.createEvent("Event"),e.files=i,e.initEvent("change",!0,!0),f.dispatchEvent(e)):b?b(f).trigger({type:"change",files:i}):(e=d.createEventObject(),e.files=i,f.fireEvent("onchange",e))}finally{v.disableMouseover=!1}},interval:null,cmd:function(a,b,c,d){v.uploadInProgress&&v.readInProgress?setTimeout(function(){v.cmd(a,b,c,d)},100):this.cmdFn(a,b,c,d)},cmdFn:function(a,b,d,e){try{return c.log("(js -> flash)."+b+":",d),v.get(a.flashId||a).cmd(b,d)}catch(f){c.log("(js -> flash).onError:",f),e||setTimeout(function(){v.cmd(a,b,d,!0)},50)}},patch:function(){c.flashEngine=!0,j(c,{readAsDataURL:function(a,b){k(a)?this.parent.apply(this,arguments):(c.log("FlashAPI.readAsBase64"),v.readInProgress=!0,v.cmd(a,"readAsBase64",{id:a.id,callback:l(function d(e,f){v.readInProgress=!1,m(d),c.log("FlashAPI.readAsBase64:",e),b({type:e?"error":"load",error:e,result:"data:"+a.type+";base64,"+f})})}))},readAsText:function(b,d,e){e?c.log("[warn] FlashAPI.readAsText not supported `encoding` param"):e=d,c.readAsDataURL(b,function(b){if("load"==b.type)try{b.result=a.atob(b.result.split(";base64,")[1])}catch(c){b.type="error",b.error=c.toString()}e(b)})},getFiles:function(a,b,d){if(d)return c.filterFiles(c.getFiles(a),b,d),null;var e=c.isArray(a)?a:t[c.uid(a.target||a.srcElement||a)];return e?(b&&(b=c.getFilesFilter(b),e=c.filter(e,function(a){return b.test(a.name)})),e):this.parent.apply(this,arguments)},getInfo:function(a,b){if(k(a))this.parent.apply(this,arguments);else if(a.isShot)b(null,a.info={width:a.width,height:a.height});else{if(!a.__info){var d=a.__info=c.defer();d.resolve(null,a.info=null)}a.__info.then(b)}}}),c.support.transform=!0,c.Image&&j(c.Image.prototype,{get:function(a,b){return this.set({scaleMode:b||"noScale"}),this.parent(a)},_load:function(a,b){if(c.log("FlashAPI.Image._load:",a),k(a))this.parent.apply(this,arguments);else{var d=this;c.getInfo(a,function(c){b.call(d,c,a)})}},_apply:function(a,b){if(c.log("FlashAPI.Image._apply:",a),k(a))this.parent.apply(this,arguments);else{var d=this.getMatrix(a.info),e=b;v.cmd(a,"imageTransform",{id:a.id,matrix:d,callback:l(function f(g,h){c.log("FlashAPI.Image._apply.callback:",g),m(f),g?e(g):c.support.html5||c.support.dataURI&&!(h.length>3e4)?(d.filter&&(e=function(a,e){a?b(a):c.Image.applyFilter(e,d.filter,function(){b(a,this.canvas)})}),c.newImage("data:"+a.type+";base64,"+h,e)):o({width:d.deg%180?d.dh:d.dw,height:d.deg%180?d.dw:d.dh,scale:d.scaleMode},h,e)})})}},toData:function(a){var b=this.file,d=b.info,e=this.getMatrix(d);c.log("FlashAPI.Image.toData"),k(b)?this.parent.apply(this,arguments):("auto"==e.deg&&(e.deg=c.Image.exifOrientation[d&&d.exif&&d.exif.Orientation]||0),a.call(this,!b.info,{id:b.id,flashId:b.flashId,name:b.name,type:b.type,matrix:e}))}}),c.Image&&j(c.Image,{fromDataURL:function(a,b,d){!c.support.dataURI||a.length>3e4?o(c.extend({scale:"exactFit"},b),a.replace(/^data:[^,]+,/,""),function(a,b){d(b)}):this.parent(a,b,d)}}),j(c.Form.prototype,{toData:function(a){for(var b=this.items,d=b.length;d--;)if(b[d].file&&k(b[d].blob))return this.parent.apply(this,arguments);c.log("FlashAPI.Form.toData"),a(b)}}),j(c.XHR.prototype,{_send:function(a,b){if(b.nodeName||b.append&&c.support.html5||c.isArray(b)&&"string"==typeof b[0])return this.parent.apply(this,arguments);var d,e,f={},h={},i=this;if(g(b,function(a){a.file?(h[a.name]=a=p(a.blob),e=a.id,d=a.flashId):f[a.name]=a.blob}),e||(d=r),!d)return c.log("[err] FlashAPI._send: flashId -- undefined"),this.parent.apply(this,arguments);c.log("FlashAPI.XHR._send: "+d+" -> "+e),i.xhr={headers:{},abort:function(){v.uploadInProgress=!1,v.cmd(d,"abort",{id:e})},getResponseHeader:function(a){return this.headers[a]},getAllResponseHeaders:function(){return this.headers}};var j=c.queue(function(){v.uploadInProgress=!0,v.cmd(d,"upload",{url:n(a.url.replace(/([a-z]+)=(\?)&?/i,"")),data:f,files:e?h:null,headers:a.headers||{},callback:l(function b(d){var e=d.type,f=d.result;c.log("FlashAPI.upload."+e),"progress"==e?(d.loaded=Math.min(d.loaded,d.total),d.lengthComputable=!0,a.progress(d)):"complete"==e?(v.uploadInProgress=!1,m(b),"string"==typeof f&&(i.responseText=f.replace(/%22/g,'"').replace(/%5c/g,"\\").replace(/%26/g,"&").replace(/%25/g,"%")),i.end(d.status||200)):("abort"==e||"error"==e)&&(v.uploadInProgress=!1,i.end(d.status||0,d.message),m(b))})})});g(h,function(a){j.inc(),c.getInfo(a,j.next)}),j.check()}})}};c.Flash=v,c.newImage("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==",function(a,b){c.support.dataURI=!(1!=b.width||1!=b.height),v.init()})}()}(window,window.jQuery,FileAPI),function(a,b,c){"use strict";var d=c.each,e=[];c.support.flash&&c.media&&!c.support.media&&!function(){function a(a){var b=a.wid=c.uid();return c.Flash._fn[b]=a,"FileAPI.Flash._fn."+b}function b(a){try{c.Flash._fn[a.wid]=null,delete c.Flash._fn[a.wid]}catch(b){}}var f=c.Flash;c.extend(c.Flash,{patchCamera:function(){c.Camera.fallback=function(d,e,g){var h=c.uid();c.log("FlashAPI.Camera.publish: "+h),f.publish(d,h,c.extend(e,{camera:!0,onEvent:a(function i(a){"camera"===a.type&&(b(i),a.error?(c.log("FlashAPI.Camera.publish.error: "+a.error),g(a.error)):(c.log("FlashAPI.Camera.publish.success: "+h),g(null)))})}))},d(e,function(a){c.Camera.fallback.apply(c.Camera,a)}),e=[],c.extend(c.Camera.prototype,{_id:function(){return this.video.id},start:function(d){var e=this;f.cmd(this._id(),"camera.on",{callback:a(function g(a){b(g),a.error?(c.log("FlashAPI.camera.on.error: "+a.error),d(a.error,e)):(c.log("FlashAPI.camera.on.success: "+e._id()),e._active=!0,d(null,e))})})},stop:function(){this._active=!1,f.cmd(this._id(),"camera.off")},shot:function(){c.log("FlashAPI.Camera.shot:",this._id());var a=c.Flash.cmd(this._id(),"shot",{});return a.type="image/png",a.flashId=this._id(),a.isShot=!0,new c.Camera.Shot(a)}})}}),c.Camera.fallback=function(){e.push(arguments)}}()}(window,window.jQuery,FileAPI),"function"==typeof define&&define.amd&&define("FileAPI",[],function(){return FileAPI}); \ No newline at end of file diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/LICENSE b/infoscreen/static/js/ng-file-upload-bower-12.2.11/LICENSE new file mode 100644 index 0000000..7ebd53c --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 danialfarid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/README.md b/infoscreen/static/js/ng-file-upload-bower-12.2.11/README.md new file mode 100755 index 0000000..ae7bc79 --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/README.md @@ -0,0 +1,5 @@ +# angular-file-upload-bower + +bower distribution of [angular-file-upload](https://github.com/danialfarid/angular-file-upload). +All issues and pull request must be sumbitted to [angular-file-upload](https://github.com/danialfarid/angular-file-upload) + diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/bower.json b/infoscreen/static/js/ng-file-upload-bower-12.2.11/bower.json new file mode 100755 index 0000000..306e760 --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/bower.json @@ -0,0 +1,14 @@ +{ + "name": "ng-file-upload", + "main": "ng-file-upload.js", + "homepage": "https://github.com/danialfarid/ng-file-upload", + "dependencies": { + "angular": ">1.2.0" + }, + "authors": [ + "danialf " + ], + "description": "Lightweight Angular JS directive to upload files. Support drag&drop, paste image, progress and abort", + "ignore": [], + "license": "MIT" + } diff --git a/infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-all.js b/infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-all.js new file mode 100644 index 0000000..792d4b5 --- /dev/null +++ b/infoscreen/static/js/ng-file-upload-bower-12.2.11/ng-file-upload-all.js @@ -0,0 +1,2888 @@ +/**! + * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort, + * progress, resize, thumbnail, preview, validation and CORS + * FileAPI Flash shim for old browsers not supporting FormData + * @author Danial + * @version 12.2.11 + */ + +(function () { + /** @namespace FileAPI.noContentTimeout */ + + function patchXHR(fnName, newFn) { + window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]); + } + + function redefineProp(xhr, prop, fn) { + try { + Object.defineProperty(xhr, prop, {get: fn}); + } catch (e) {/*ignore*/ + } + } + + if (!window.FileAPI) { + window.FileAPI = {}; + } + + if (!window.XMLHttpRequest) { + throw 'AJAX is not supported. XMLHttpRequest is not defined.'; + } + + FileAPI.shouldLoad = !window.FormData || FileAPI.forceLoad; + if (FileAPI.shouldLoad) { + var initializeUploadListener = function (xhr) { + if (!xhr.__listeners) { + if (!xhr.upload) xhr.upload = {}; + xhr.__listeners = []; + var origAddEventListener = xhr.upload.addEventListener; + xhr.upload.addEventListener = function (t, fn) { + xhr.__listeners[t] = fn; + if (origAddEventListener) origAddEventListener.apply(this, arguments); + }; + } + }; + + patchXHR('open', function (orig) { + return function (m, url, b) { + initializeUploadListener(this); + this.__url = url; + try { + orig.apply(this, [m, url, b]); + } catch (e) { + if (e.message.indexOf('Access is denied') > -1) { + this.__origError = e; + orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]); + } + } + }; + }); + + patchXHR('getResponseHeader', function (orig) { + return function (h) { + return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h])); + }; + }); + + patchXHR('getAllResponseHeaders', function (orig) { + return function () { + return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this)); + }; + }); + + patchXHR('abort', function (orig) { + return function () { + return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this)); + }; + }); + + patchXHR('setRequestHeader', function (orig) { + return function (header, value) { + if (header === '__setXHR_') { + initializeUploadListener(this); + var val = value(this); + // fix for angular < 1.2.0 + if (val instanceof Function) { + val(this); + } + } else { + this.__requestHeaders = this.__requestHeaders || {}; + this.__requestHeaders[header] = value; + orig.apply(this, arguments); + } + }; + }); + + patchXHR('send', function (orig) { + return function () { + var xhr = this; + if (arguments[0] && arguments[0].__isFileAPIShim) { + var formData = arguments[0]; + var config = { + url: xhr.__url, + jsonp: false, //removes the callback form param + cache: true, //removes the ?fileapiXXX in the url + complete: function (err, fileApiXHR) { + if (err && angular.isString(err) && err.indexOf('#2174') !== -1) { + // this error seems to be fine the file is being uploaded properly. + err = null; + } + xhr.__completed = true; + if (!err && xhr.__listeners.load) + xhr.__listeners.load({ + type: 'load', + loaded: xhr.__loaded, + total: xhr.__total, + target: xhr, + lengthComputable: true + }); + if (!err && xhr.__listeners.loadend) + xhr.__listeners.loadend({ + type: 'loadend', + loaded: xhr.__loaded, + total: xhr.__total, + target: xhr, + lengthComputable: true + }); + if (err === 'abort' && xhr.__listeners.abort) + xhr.__listeners.abort({ + type: 'abort', + loaded: xhr.__loaded, + total: xhr.__total, + target: xhr, + lengthComputable: true + }); + if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function () { + return (fileApiXHR.status === 0 && err && err !== 'abort') ? 500 : fileApiXHR.status; + }); + if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function () { + return fileApiXHR.statusText; + }); + redefineProp(xhr, 'readyState', function () { + return 4; + }); + if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function () { + return fileApiXHR.response; + }); + var resp = fileApiXHR.responseText || (err && fileApiXHR.status === 0 && err !== 'abort' ? err : undefined); + redefineProp(xhr, 'responseText', function () { + return resp; + }); + redefineProp(xhr, 'response', function () { + return resp; + }); + if (err) redefineProp(xhr, 'err', function () { + return err; + }); + xhr.__fileApiXHR = fileApiXHR; + if (xhr.onreadystatechange) xhr.onreadystatechange(); + if (xhr.onload) xhr.onload(); + }, + progress: function (e) { + e.target = xhr; + if (xhr.__listeners.progress) xhr.__listeners.progress(e); + xhr.__total = e.total; + xhr.__loaded = e.loaded; + if (e.total === e.loaded) { + // fix flash issue that doesn't call complete if there is no response text from the server + var _this = this; + setTimeout(function () { + if (!xhr.__completed) { + xhr.getAllResponseHeaders = function () { + }; + _this.complete(null, {status: 204, statusText: 'No Content'}); + } + }, FileAPI.noContentTimeout || 10000); + } + }, + headers: xhr.__requestHeaders + }; + config.data = {}; + config.files = {}; + for (var i = 0; i < formData.data.length; i++) { + var item = formData.data[i]; + if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) { + config.files[item.key] = item.val; + } else { + config.data[item.key] = item.val; + } + } + + setTimeout(function () { + if (!FileAPI.hasFlash) { + throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"'; + } + xhr.__fileApiXHR = FileAPI.upload(config); + }, 1); + } else { + if (this.__origError) { + throw this.__origError; + } + orig.apply(xhr, arguments); + } + }; + }); + window.XMLHttpRequest.__isFileAPIShim = true; + window.FormData = FormData = function () { + return { + append: function (key, val, name) { + if (val.__isFileAPIBlobShim) { + val = val.data[0]; + } + this.data.push({ + key: key, + val: val, + name: name + }); + }, + data: [], + __isFileAPIShim: true + }; + }; + + window.Blob = Blob = function (b) { + return { + data: b, + __isFileAPIBlobShim: true + }; + }; + } + +})(); + +(function () { + /** @namespace FileAPI.forceLoad */ + /** @namespace window.FileAPI.jsUrl */ + /** @namespace window.FileAPI.jsPath */ + + function isInputTypeFile(elem) { + return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file'; + } + + function hasFlash() { + try { + var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); + if (fo) return true; + } catch (e) { + if (navigator.mimeTypes['application/x-shockwave-flash'] !== undefined) return true; + } + return false; + } + + function getOffset(obj) { + var left = 0, top = 0; + + if (window.jQuery) { + return jQuery(obj).offset(); + } + + if (obj.offsetParent) { + do { + left += (obj.offsetLeft - obj.scrollLeft); + top += (obj.offsetTop - obj.scrollTop); + obj = obj.offsetParent; + } while (obj); + } + return { + left: left, + top: top + }; + } + + if (FileAPI.shouldLoad) { + FileAPI.hasFlash = hasFlash(); + + //load FileAPI + if (FileAPI.forceLoad) { + FileAPI.html5 = false; + } + + if (!FileAPI.upload) { + var jsUrl, basePath, script = document.createElement('script'), allScripts = document.getElementsByTagName('script'), i, index, src; + if (window.FileAPI.jsUrl) { + jsUrl = window.FileAPI.jsUrl; + } else if (window.FileAPI.jsPath) { + basePath = window.FileAPI.jsPath; + } else { + for (i = 0; i < allScripts.length; i++) { + src = allScripts[i].src; + index = src.search(/\/ng\-file\-upload[\-a-zA-z0-9\.]*\.js/); + if (index > -1) { + basePath = src.substring(0, index + 1); + break; + } + } + } + + if (FileAPI.staticPath == null) FileAPI.staticPath = basePath; + script.setAttribute('src', jsUrl || basePath + 'FileAPI.min.js'); + document.getElementsByTagName('head')[0].appendChild(script); + } + + FileAPI.ngfFixIE = function (elem, fileElem, changeFn) { + if (!hasFlash()) { + throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"'; + } + var fixInputStyle = function () { + var label = fileElem.parent(); + if (elem.attr('disabled')) { + if (label) label.removeClass('js-fileapi-wrapper'); + } else { + if (!fileElem.attr('__ngf_flash_')) { + fileElem.unbind('change'); + fileElem.unbind('click'); + fileElem.bind('change', function (evt) { + fileApiChangeFn.apply(this, [evt]); + changeFn.apply(this, [evt]); + }); + fileElem.attr('__ngf_flash_', 'true'); + } + label.addClass('js-fileapi-wrapper'); + if (!isInputTypeFile(elem)) { + label.css('position', 'absolute') + .css('top', getOffset(elem[0]).top + 'px').css('left', getOffset(elem[0]).left + 'px') + .css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px') + .css('filter', 'alpha(opacity=0)').css('display', elem.css('display')) + .css('overflow', 'hidden').css('z-index', '900000') + .css('visibility', 'visible'); + fileElem.css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px') + .css('position', 'absolute').css('top', '0px').css('left', '0px'); + } + } + }; + + elem.bind('mouseenter', fixInputStyle); + + var fileApiChangeFn = function (evt) { + var files = FileAPI.getFiles(evt); + //just a double check for #233 + for (var i = 0; i < files.length; i++) { + if (files[i].size === undefined) files[i].size = 0; + if (files[i].name === undefined) files[i].name = 'file'; + if (files[i].type === undefined) files[i].type = 'undefined'; + } + if (!evt.target) { + evt.target = {}; + } + evt.target.files = files; + // if evt.target.files is not writable use helper field + if (evt.target.files !== files) { + evt.__files_ = files; + } + (evt.__files_ || evt.target.files).item = function (i) { + return (evt.__files_ || evt.target.files)[i] || null; + }; + }; + }; + + FileAPI.disableFileInput = function (elem, disable) { + if (disable) { + elem.removeClass('js-fileapi-wrapper'); + } else { + elem.addClass('js-fileapi-wrapper'); + } + }; + } +})(); + +if (!window.FileReader) { + window.FileReader = function () { + var _this = this, loadStarted = false; + this.listeners = {}; + this.addEventListener = function (type, fn) { + _this.listeners[type] = _this.listeners[type] || []; + _this.listeners[type].push(fn); + }; + this.removeEventListener = function (type, fn) { + if (_this.listeners[type]) _this.listeners[type].splice(_this.listeners[type].indexOf(fn), 1); + }; + this.dispatchEvent = function (evt) { + var list = _this.listeners[evt.type]; + if (list) { + for (var i = 0; i < list.length; i++) { + list[i].call(_this, evt); + } + } + }; + this.onabort = this.onerror = this.onload = this.onloadstart = this.onloadend = this.onprogress = null; + + var constructEvent = function (type, evt) { + var e = {type: type, target: _this, loaded: evt.loaded, total: evt.total, error: evt.error}; + if (evt.result != null) e.target.result = evt.result; + return e; + }; + var listener = function (evt) { + if (!loadStarted) { + loadStarted = true; + if (_this.onloadstart) _this.onloadstart(constructEvent('loadstart', evt)); + } + var e; + if (evt.type === 'load') { + if (_this.onloadend) _this.onloadend(constructEvent('loadend', evt)); + e = constructEvent('load', evt); + if (_this.onload) _this.onload(e); + _this.dispatchEvent(e); + } else if (evt.type === 'progress') { + e = constructEvent('progress', evt); + if (_this.onprogress) _this.onprogress(e); + _this.dispatchEvent(e); + } else { + e = constructEvent('error', evt); + if (_this.onerror) _this.onerror(e); + _this.dispatchEvent(e); + } + }; + this.readAsDataURL = function (file) { + FileAPI.readAsDataURL(file, listener); + }; + this.readAsText = function (file) { + FileAPI.readAsText(file, listener); + }; + }; +} + +/**! + * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort, + * progress, resize, thumbnail, preview, validation and CORS + * @author Danial + * @version 12.2.11 + */ + +if (window.XMLHttpRequest && !(window.FileAPI && FileAPI.shouldLoad)) { + window.XMLHttpRequest.prototype.setRequestHeader = (function (orig) { + return function (header, value) { + if (header === '__setXHR_') { + var val = value(this); + // fix for angular < 1.2.0 + if (val instanceof Function) { + val(this); + } + } else { + orig.apply(this, arguments); + } + }; + })(window.XMLHttpRequest.prototype.setRequestHeader); +} + +var ngFileUpload = angular.module('ngFileUpload', []); + +ngFileUpload.version = '12.2.11'; + +ngFileUpload.service('UploadBase', ['$http', '$q', '$timeout', function ($http, $q, $timeout) { + var upload = this; + upload.promisesCount = 0; + + this.isResumeSupported = function () { + return window.Blob && window.Blob.prototype.slice; + }; + + var resumeSupported = this.isResumeSupported(); + + function sendHttp(config) { + config.method = config.method || 'POST'; + config.headers = config.headers || {}; + + var deferred = config._deferred = config._deferred || $q.defer(); + var promise = deferred.promise; + + function notifyProgress(e) { + if (deferred.notify) { + deferred.notify(e); + } + if (promise.progressFunc) { + $timeout(function () { + promise.progressFunc(e); + }); + } + } + + function getNotifyEvent(n) { + if (config._start != null && resumeSupported) { + return { + loaded: n.loaded + config._start, + total: (config._file && config._file.size) || n.total, + type: n.type, config: config, + lengthComputable: true, target: n.target + }; + } else { + return n; + } + } + + if (!config.disableProgress) { + config.headers.__setXHR_ = function () { + return function (xhr) { + if (!xhr || !xhr.upload || !xhr.upload.addEventListener) return; + config.__XHR = xhr; + if (config.xhrFn) config.xhrFn(xhr); + xhr.upload.addEventListener('progress', function (e) { + e.config = config; + notifyProgress(getNotifyEvent(e)); + }, false); + //fix for firefox not firing upload progress end, also IE8-9 + xhr.upload.addEventListener('load', function (e) { + if (e.lengthComputable) { + e.config = config; + notifyProgress(getNotifyEvent(e)); + } + }, false); + }; + }; + } + + function uploadWithAngular() { + $http(config).then(function (r) { + if (resumeSupported && config._chunkSize && !config._finished && config._file) { + var fileSize = config._file && config._file.size || 0; + notifyProgress({ + loaded: Math.min(config._end, fileSize), + total: fileSize, + config: config, + type: 'progress' + } + ); + upload.upload(config, true); + } else { + if (config._finished) delete config._finished; + deferred.resolve(r); + } + }, function (e) { + deferred.reject(e); + }, function (n) { + deferred.notify(n); + } + ); + } + + if (!resumeSupported) { + uploadWithAngular(); + } else if (config._chunkSize && config._end && !config._finished) { + config._start = config._end; + config._end += config._chunkSize; + uploadWithAngular(); + } else if (config.resumeSizeUrl) { + $http.get(config.resumeSizeUrl).then(function (resp) { + if (config.resumeSizeResponseReader) { + config._start = config.resumeSizeResponseReader(resp.data); + } else { + config._start = parseInt((resp.data.size == null ? resp.data : resp.data.size).toString()); + } + if (config._chunkSize) { + config._end = config._start + config._chunkSize; + } + uploadWithAngular(); + }, function (e) { + throw e; + }); + } else if (config.resumeSize) { + config.resumeSize().then(function (size) { + config._start = size; + if (config._chunkSize) { + config._end = config._start + config._chunkSize; + } + uploadWithAngular(); + }, function (e) { + throw e; + }); + } else { + if (config._chunkSize) { + config._start = 0; + config._end = config._start + config._chunkSize; + } + uploadWithAngular(); + } + + + promise.success = function (fn) { + promise.then(function (response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.error = function (fn) { + promise.then(null, function (response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.progress = function (fn) { + promise.progressFunc = fn; + promise.then(null, null, function (n) { + fn(n); + }); + return promise; + }; + promise.abort = promise.pause = function () { + if (config.__XHR) { + $timeout(function () { + config.__XHR.abort(); + }); + } + return promise; + }; + promise.xhr = function (fn) { + config.xhrFn = (function (origXhrFn) { + return function () { + if (origXhrFn) origXhrFn.apply(promise, arguments); + fn.apply(promise, arguments); + }; + })(config.xhrFn); + return promise; + }; + + upload.promisesCount++; + if (promise['finally'] && promise['finally'] instanceof Function) { + promise['finally'](function () { + upload.promisesCount--; + }); + } + return promise; + } + + this.isUploadInProgress = function () { + return upload.promisesCount > 0; + }; + + this.rename = function (file, name) { + file.ngfName = name; + return file; + }; + + this.jsonBlob = function (val) { + if (val != null && !angular.isString(val)) { + val = JSON.stringify(val); + } + var blob = new window.Blob([val], {type: 'application/json'}); + blob._ngfBlob = true; + return blob; + }; + + this.json = function (val) { + return angular.toJson(val); + }; + + function copy(obj) { + var clone = {}; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + clone[key] = obj[key]; + } + } + return clone; + } + + this.isFile = function (file) { + return file != null && (file instanceof window.Blob || (file.flashId && file.name && file.size)); + }; + + this.upload = function (config, internal) { + function toResumeFile(file, formData) { + if (file._ngfBlob) return file; + config._file = config._file || file; + if (config._start != null && resumeSupported) { + if (config._end && config._end >= file.size) { + config._finished = true; + config._end = file.size; + } + var slice = file.slice(config._start, config._end || file.size); + slice.name = file.name; + slice.ngfName = file.ngfName; + if (config._chunkSize) { + formData.append('_chunkSize', config._chunkSize); + formData.append('_currentChunkSize', config._end - config._start); + formData.append('_chunkNumber', Math.floor(config._start / config._chunkSize)); + formData.append('_totalSize', config._file.size); + } + return slice; + } + return file; + } + + function addFieldToFormData(formData, val, key) { + if (val !== undefined) { + if (angular.isDate(val)) { + val = val.toISOString(); + } + if (angular.isString(val)) { + formData.append(key, val); + } else if (upload.isFile(val)) { + var file = toResumeFile(val, formData); + var split = key.split(','); + if (split[1]) { + file.ngfName = split[1].replace(/^\s+|\s+$/g, ''); + key = split[0]; + } + config._fileKey = config._fileKey || key; + formData.append(key, file, file.ngfName || file.name); + } else { + if (angular.isObject(val)) { + if (val.$$ngfCircularDetection) throw 'ngFileUpload: Circular reference in config.data. Make sure specified data for Upload.upload() has no circular reference: ' + key; + + val.$$ngfCircularDetection = true; + try { + for (var k in val) { + if (val.hasOwnProperty(k) && k !== '$$ngfCircularDetection') { + var objectKey = config.objectKey == null ? '[i]' : config.objectKey; + if (val.length && parseInt(k) > -1) { + objectKey = config.arrayKey == null ? objectKey : config.arrayKey; + } + addFieldToFormData(formData, val[k], key + objectKey.replace(/[ik]/g, k)); + } + } + } finally { + delete val.$$ngfCircularDetection; + } + } else { + formData.append(key, val); + } + } + } + } + + function digestConfig() { + config._chunkSize = upload.translateScalars(config.resumeChunkSize); + config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null; + + config.headers = config.headers || {}; + config.headers['Content-Type'] = undefined; + config.transformRequest = config.transformRequest ? + (angular.isArray(config.transformRequest) ? + config.transformRequest : [config.transformRequest]) : []; + config.transformRequest.push(function (data) { + var formData = new window.FormData(), key; + data = data || config.fields || {}; + if (config.file) { + data.file = config.file; + } + for (key in data) { + if (data.hasOwnProperty(key)) { + var val = data[key]; + if (config.formDataAppender) { + config.formDataAppender(formData, key, val); + } else { + addFieldToFormData(formData, val, key); + } + } + } + + return formData; + }); + } + + if (!internal) config = copy(config); + if (!config._isDigested) { + config._isDigested = true; + digestConfig(); + } + + return sendHttp(config); + }; + + this.http = function (config) { + config = copy(config); + config.transformRequest = config.transformRequest || function (data) { + if ((window.ArrayBuffer && data instanceof window.ArrayBuffer) || data instanceof window.Blob) { + return data; + } + return $http.defaults.transformRequest[0].apply(this, arguments); + }; + config._chunkSize = upload.translateScalars(config.resumeChunkSize); + config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null; + + return sendHttp(config); + }; + + this.translateScalars = function (str) { + if (angular.isString(str)) { + if (str.search(/kb/i) === str.length - 2) { + return parseFloat(str.substring(0, str.length - 2) * 1024); + } else if (str.search(/mb/i) === str.length - 2) { + return parseFloat(str.substring(0, str.length - 2) * 1048576); + } else if (str.search(/gb/i) === str.length - 2) { + return parseFloat(str.substring(0, str.length - 2) * 1073741824); + } else if (str.search(/b/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1)); + } else if (str.search(/s/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1)); + } else if (str.search(/m/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1) * 60); + } else if (str.search(/h/i) === str.length - 1) { + return parseFloat(str.substring(0, str.length - 1) * 3600); + } + } + return str; + }; + + this.urlToBlob = function(url) { + var defer = $q.defer(); + $http({url: url, method: 'get', responseType: 'arraybuffer'}).then(function (resp) { + var arrayBufferView = new Uint8Array(resp.data); + var type = resp.headers('content-type') || 'image/WebP'; + var blob = new window.Blob([arrayBufferView], {type: type}); + var matches = url.match(/.*\/(.+?)(\?.*)?$/); + if (matches.length > 1) { + blob.name = matches[1]; + } + defer.resolve(blob); + }, function (e) { + defer.reject(e); + }); + return defer.promise; + }; + + this.setDefaults = function (defaults) { + this.defaults = defaults || {}; + }; + + this.defaults = {}; + this.version = ngFileUpload.version; +} + +]); + +ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', '$q', 'UploadExif', function ($parse, $timeout, $compile, $q, UploadExif) { + var upload = UploadExif; + upload.getAttrWithDefaults = function (attr, name) { + if (attr[name] != null) return attr[name]; + var def = upload.defaults[name]; + return (def == null ? def : (angular.isString(def) ? def : JSON.stringify(def))); + }; + + upload.attrGetter = function (name, attr, scope, params) { + var attrVal = this.getAttrWithDefaults(attr, name); + if (scope) { + try { + if (params) { + return $parse(attrVal)(scope, params); + } else { + return $parse(attrVal)(scope); + } + } catch (e) { + // hangle string value without single qoute + if (name.search(/min|max|pattern/i)) { + return attrVal; + } else { + throw e; + } + } + } else { + return attrVal; + } + }; + + upload.shouldUpdateOn = function (type, attr, scope) { + var modelOptions = upload.attrGetter('ngfModelOptions', attr, scope); + if (modelOptions && modelOptions.updateOn) { + return modelOptions.updateOn.split(' ').indexOf(type) > -1; + } + return true; + }; + + upload.emptyPromise = function () { + var d = $q.defer(); + var args = arguments; + $timeout(function () { + d.resolve.apply(d, args); + }); + return d.promise; + }; + + upload.rejectPromise = function () { + var d = $q.defer(); + var args = arguments; + $timeout(function () { + d.reject.apply(d, args); + }); + return d.promise; + }; + + upload.happyPromise = function (promise, data) { + var d = $q.defer(); + promise.then(function (result) { + d.resolve(result); + }, function (error) { + $timeout(function () { + throw error; + }); + d.resolve(data); + }); + return d.promise; + }; + + function applyExifRotations(files, attr, scope) { + var promises = [upload.emptyPromise()]; + angular.forEach(files, function (f, i) { + if (f.type.indexOf('image/jpeg') === 0 && upload.attrGetter('ngfFixOrientation', attr, scope, {$file: f})) { + promises.push(upload.happyPromise(upload.applyExifRotation(f), f).then(function (fixedFile) { + files.splice(i, 1, fixedFile); + })); + } + }); + return $q.all(promises); + } + + function resize(files, attr, scope) { + var resizeVal = upload.attrGetter('ngfResize', attr, scope); + if (!resizeVal || !upload.isResizeSupported() || !files.length) return upload.emptyPromise(); + if (resizeVal instanceof Function) { + var defer = $q.defer(); + return resizeVal(files).then(function (p) { + resizeWithParams(p, files, attr, scope).then(function (r) { + defer.resolve(r); + }, function (e) { + defer.reject(e); + }); + }, function (e) { + defer.reject(e); + }); + } else { + return resizeWithParams(resizeVal, files, attr, scope); + } + } + + function resizeWithParams(params, files, attr, scope) { + var promises = [upload.emptyPromise()]; + + function handleFile(f, i) { + if (f.type.indexOf('image') === 0) { + if (params.pattern && !upload.validatePattern(f, params.pattern)) return; + params.resizeIf = function (width, height) { + return upload.attrGetter('ngfResizeIf', attr, scope, + {$width: width, $height: height, $file: f}); + }; + var promise = upload.resize(f, params); + promises.push(promise); + promise.then(function (resizedFile) { + files.splice(i, 1, resizedFile); + }, function (e) { + f.$error = 'resize'; + f.$errorParam = (e ? (e.message ? e.message : e) + ': ' : '') + (f && f.name); + }); + } + } + + for (var i = 0; i < files.length; i++) { + handleFile(files[i], i); + } + return $q.all(promises); + } + + upload.updateModel = function (ngModel, attr, scope, fileChange, files, evt, noDelay) { + function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) { + attr.$$ngfPrevValidFiles = files; + attr.$$ngfPrevInvalidFiles = invalidFiles; + var file = files && files.length ? files[0] : null; + var invalidFile = invalidFiles && invalidFiles.length ? invalidFiles[0] : null; + + if (ngModel) { + upload.applyModelValidation(ngModel, files); + ngModel.$setViewValue(isSingleModel ? file : files); + } + + if (fileChange) { + $parse(fileChange)(scope, { + $files: files, + $file: file, + $newFiles: newFiles, + $duplicateFiles: dupFiles, + $invalidFiles: invalidFiles, + $invalidFile: invalidFile, + $event: evt + }); + } + + var invalidModel = upload.attrGetter('ngfModelInvalid', attr); + if (invalidModel) { + $timeout(function () { + $parse(invalidModel).assign(scope, isSingleModel ? invalidFile : invalidFiles); + }); + } + $timeout(function () { + // scope apply changes + }); + } + + var allNewFiles, dupFiles = [], prevValidFiles, prevInvalidFiles, + invalids = [], valids = []; + + function removeDuplicates() { + function equals(f1, f2) { + return f1.name === f2.name && (f1.$ngfOrigSize || f1.size) === (f2.$ngfOrigSize || f2.size) && + f1.type === f2.type; + } + + function isInPrevFiles(f) { + var j; + for (j = 0; j < prevValidFiles.length; j++) { + if (equals(f, prevValidFiles[j])) { + return true; + } + } + for (j = 0; j < prevInvalidFiles.length; j++) { + if (equals(f, prevInvalidFiles[j])) { + return true; + } + } + return false; + } + + if (files) { + allNewFiles = []; + dupFiles = []; + for (var i = 0; i < files.length; i++) { + if (isInPrevFiles(files[i])) { + dupFiles.push(files[i]); + } else { + allNewFiles.push(files[i]); + } + } + } + } + + function toArray(v) { + return angular.isArray(v) ? v : [v]; + } + + function resizeAndUpdate() { + function updateModel() { + $timeout(function () { + update(keep ? prevValidFiles.concat(valids) : valids, + keep ? prevInvalidFiles.concat(invalids) : invalids, + files, dupFiles, isSingleModel); + }, options && options.debounce ? options.debounce.change || options.debounce : 0); + } + + resize(validateAfterResize ? allNewFiles : valids, attr, scope).then(function () { + if (validateAfterResize) { + upload.validate(allNewFiles, keep ? prevValidFiles.length : 0, ngModel, attr, scope) + .then(function (validationResult) { + valids = validationResult.validsFiles; + invalids = validationResult.invalidsFiles; + updateModel(); + }); + } else { + updateModel(); + } + }, function (e) { + throw 'Could not resize files ' + e; + }); + } + + prevValidFiles = attr.$$ngfPrevValidFiles || []; + prevInvalidFiles = attr.$$ngfPrevInvalidFiles || []; + if (ngModel && ngModel.$modelValue) { + prevValidFiles = toArray(ngModel.$modelValue); + } + + var keep = upload.attrGetter('ngfKeep', attr, scope); + allNewFiles = (files || []).slice(0); + if (keep === 'distinct' || upload.attrGetter('ngfKeepDistinct', attr, scope) === true) { + removeDuplicates(attr, scope); + } + + var isSingleModel = !keep && !upload.attrGetter('ngfMultiple', attr, scope) && !upload.attrGetter('multiple', attr); + + if (keep && !allNewFiles.length) return; + + upload.attrGetter('ngfBeforeModelChange', attr, scope, { + $files: files, + $file: files && files.length ? files[0] : null, + $newFiles: allNewFiles, + $duplicateFiles: dupFiles, + $event: evt + }); + + var validateAfterResize = upload.attrGetter('ngfValidateAfterResize', attr, scope); + + var options = upload.attrGetter('ngfModelOptions', attr, scope); + upload.validate(allNewFiles, keep ? prevValidFiles.length : 0, ngModel, attr, scope) + .then(function (validationResult) { + if (noDelay) { + update(allNewFiles, [], files, dupFiles, isSingleModel); + } else { + if ((!options || !options.allowInvalid) && !validateAfterResize) { + valids = validationResult.validFiles; + invalids = validationResult.invalidFiles; + } else { + valids = allNewFiles; + } + if (upload.attrGetter('ngfFixOrientation', attr, scope) && upload.isExifSupported()) { + applyExifRotations(valids, attr, scope).then(function () { + resizeAndUpdate(); + }); + } else { + resizeAndUpdate(); + } + } + }); + }; + + return upload; +}]); + +ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', function ($parse, $timeout, $compile, Upload) { + var generatedElems = []; + + function isDelayedClickSupported(ua) { + // fix for android native browser < 4.4 and safari windows + var m = ua.match(/Android[^\d]*(\d+)\.(\d+)/); + if (m && m.length > 2) { + var v = Upload.defaults.androidFixMinorVersion || 4; + return parseInt(m[1]) < 4 || (parseInt(m[1]) === v && parseInt(m[2]) < v); + } + + // safari on windows + return ua.indexOf('Chrome') === -1 && /.*Windows.*Safari.*/.test(ua); + } + + function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, upload) { + /** @namespace attr.ngfSelect */ + /** @namespace attr.ngfChange */ + /** @namespace attr.ngModel */ + /** @namespace attr.ngfModelOptions */ + /** @namespace attr.ngfMultiple */ + /** @namespace attr.ngfCapture */ + /** @namespace attr.ngfValidate */ + /** @namespace attr.ngfKeep */ + var attrGetter = function (name, scope) { + return upload.attrGetter(name, attr, scope); + }; + + function isInputTypeFile() { + return elem[0].tagName.toLowerCase() === 'input' && attr.type && attr.type.toLowerCase() === 'file'; + } + + function fileChangeAttr() { + return attrGetter('ngfChange') || attrGetter('ngfSelect'); + } + + function changeFn(evt) { + if (upload.shouldUpdateOn('change', attr, scope)) { + var fileList = evt.__files_ || (evt.target && evt.target.files), files = []; + /* Handle duplicate call in IE11 */ + if (!fileList) return; + for (var i = 0; i < fileList.length; i++) { + files.push(fileList[i]); + } + upload.updateModel(ngModel, attr, scope, fileChangeAttr(), + files.length ? files : null, evt); + } + } + + upload.registerModelChangeValidator(ngModel, attr, scope); + + var unwatches = []; + if (attrGetter('ngfMultiple')) { + unwatches.push(scope.$watch(attrGetter('ngfMultiple'), function () { + fileElem.attr('multiple', attrGetter('ngfMultiple', scope)); + })); + } + if (attrGetter('ngfCapture')) { + unwatches.push(scope.$watch(attrGetter('ngfCapture'), function () { + fileElem.attr('capture', attrGetter('ngfCapture', scope)); + })); + } + if (attrGetter('ngfAccept')) { + unwatches.push(scope.$watch(attrGetter('ngfAccept'), function () { + fileElem.attr('accept', attrGetter('ngfAccept', scope)); + })); + } + attr.$observe('accept', function () { + fileElem.attr('accept', attrGetter('accept')); + }); + unwatches.push(function () { + if (attr.$$observers) delete attr.$$observers.accept; + }); + function bindAttrToFileInput(fileElem) { + for (var i = 0; i < elem[0].attributes.length; i++) { + var attribute = elem[0].attributes[i]; + if (attribute.name !== 'type' && attribute.name !== 'class' && attribute.name !== 'style') { + if (attribute.value == null || attribute.value === '') { + if (attribute.name === 'required') attribute.value = 'required'; + if (attribute.name === 'multiple') attribute.value = 'multiple'; + } + fileElem.attr(attribute.name, attribute.name === 'id' ? 'ngf-' + attribute.value : attribute.value); + } + } + } + + function createFileInput() { + if (isInputTypeFile()) { + return elem; + } + + var fileElem = angular.element(''); + + bindAttrToFileInput(fileElem); + + var label = angular.element(''); + label.css('visibility', 'hidden').css('position', 'absolute').css('overflow', 'hidden') + .css('width', '0px').css('height', '0px').css('border', 'none') + .css('margin', '0px').css('padding', '0px').attr('tabindex', '-1'); + if (elem.attr('id')) { + label.attr('id', 'ngf-label-' + elem.attr('id')); + } + generatedElems.push({el: elem, ref: label}); + + document.body.appendChild(label.append(fileElem)[0]); + + return fileElem; + } + + function clickHandler(evt) { + if (elem.attr('disabled')) return false; + if (attrGetter('ngfSelectDisabled', scope)) return; + + var r = detectSwipe(evt); + // prevent the click if it is a swipe + if (r != null) return r; + + resetModel(evt); + + // fix for md when the element is removed from the DOM and added back #460 + try { + if (!isInputTypeFile() && !document.body.contains(fileElem[0])) { + generatedElems.push({el: elem, ref: fileElem.parent()}); + document.body.appendChild(fileElem.parent()[0]); + fileElem.bind('change', changeFn); + } + } catch(e){/*ignore*/} + + if (isDelayedClickSupported(navigator.userAgent)) { + setTimeout(function () { + fileElem[0].click(); + }, 0); + } else { + fileElem[0].click(); + } + + return false; + } + + + var initialTouchStartY = 0; + var initialTouchStartX = 0; + + function detectSwipe(evt) { + var touches = evt.changedTouches || (evt.originalEvent && evt.originalEvent.changedTouches); + if (touches) { + if (evt.type === 'touchstart') { + initialTouchStartX = touches[0].clientX; + initialTouchStartY = touches[0].clientY; + return true; // don't block event default + } else { + // prevent scroll from triggering event + if (evt.type === 'touchend') { + var currentX = touches[0].clientX; + var currentY = touches[0].clientY; + if ((Math.abs(currentX - initialTouchStartX) > 20) || + (Math.abs(currentY - initialTouchStartY) > 20)) { + evt.stopPropagation(); + evt.preventDefault(); + return false; + } + } + return true; + } + } + } + + var fileElem = elem; + + function resetModel(evt) { + if (upload.shouldUpdateOn('click', attr, scope) && fileElem.val()) { + fileElem.val(null); + upload.updateModel(ngModel, attr, scope, fileChangeAttr(), null, evt, true); + } + } + + if (!isInputTypeFile()) { + fileElem = createFileInput(); + } + fileElem.bind('change', changeFn); + + if (!isInputTypeFile()) { + elem.bind('click touchstart touchend', clickHandler); + } else { + elem.bind('click', resetModel); + } + + function ie10SameFileSelectFix(evt) { + if (fileElem && !fileElem.attr('__ngf_ie10_Fix_')) { + if (!fileElem[0].parentNode) { + fileElem = null; + return; + } + evt.preventDefault(); + evt.stopPropagation(); + fileElem.unbind('click'); + var clone = fileElem.clone(); + fileElem.replaceWith(clone); + fileElem = clone; + fileElem.attr('__ngf_ie10_Fix_', 'true'); + fileElem.bind('change', changeFn); + fileElem.bind('click', ie10SameFileSelectFix); + fileElem[0].click(); + return false; + } else { + fileElem.removeAttr('__ngf_ie10_Fix_'); + } + } + + if (navigator.appVersion.indexOf('MSIE 10') !== -1) { + fileElem.bind('click', ie10SameFileSelectFix); + } + + if (ngModel) ngModel.$formatters.push(function (val) { + if (val == null || val.length === 0) { + if (fileElem.val()) { + fileElem.val(null); + } + } + return val; + }); + + scope.$on('$destroy', function () { + if (!isInputTypeFile()) fileElem.parent().remove(); + angular.forEach(unwatches, function (unwatch) { + unwatch(); + }); + }); + + $timeout(function () { + for (var i = 0; i < generatedElems.length; i++) { + var g = generatedElems[i]; + if (!document.body.contains(g.el[0])) { + generatedElems.splice(i, 1); + g.ref.remove(); + } + } + }); + + if (window.FileAPI && window.FileAPI.ngfFixIE) { + window.FileAPI.ngfFixIE(elem, fileElem, changeFn); + } + } + + return { + restrict: 'AEC', + require: '?ngModel', + link: function (scope, elem, attr, ngModel) { + linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, Upload); + } + }; +}]); + +(function () { + + ngFileUpload.service('UploadDataUrl', ['UploadBase', '$timeout', '$q', function (UploadBase, $timeout, $q) { + var upload = UploadBase; + upload.base64DataUrl = function (file) { + if (angular.isArray(file)) { + var d = $q.defer(), count = 0; + angular.forEach(file, function (f) { + upload.dataUrl(f, true)['finally'](function () { + count++; + if (count === file.length) { + var urls = []; + angular.forEach(file, function (ff) { + urls.push(ff.$ngfDataUrl); + }); + d.resolve(urls, file); + } + }); + }); + return d.promise; + } else { + return upload.dataUrl(file, true); + } + }; + upload.dataUrl = function (file, disallowObjectUrl) { + if (!file) return upload.emptyPromise(file, file); + if ((disallowObjectUrl && file.$ngfDataUrl != null) || (!disallowObjectUrl && file.$ngfBlobUrl != null)) { + return upload.emptyPromise(disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl, file); + } + var p = disallowObjectUrl ? file.$$ngfDataUrlPromise : file.$$ngfBlobUrlPromise; + if (p) return p; + + var deferred = $q.defer(); + $timeout(function () { + if (window.FileReader && file && + (!window.FileAPI || navigator.userAgent.indexOf('MSIE 8') === -1 || file.size < 20000) && + (!window.FileAPI || navigator.userAgent.indexOf('MSIE 9') === -1 || file.size < 4000000)) { + //prefer URL.createObjectURL for handling refrences to files of all sizes + //since it doesn´t build a large string in memory + var URL = window.URL || window.webkitURL; + if (URL && URL.createObjectURL && !disallowObjectUrl) { + var url; + try { + url = URL.createObjectURL(file); + } catch (e) { + $timeout(function () { + file.$ngfBlobUrl = ''; + deferred.reject(); + }); + return; + } + $timeout(function () { + file.$ngfBlobUrl = url; + if (url) { + deferred.resolve(url, file); + upload.blobUrls = upload.blobUrls || []; + upload.blobUrlsTotalSize = upload.blobUrlsTotalSize || 0; + upload.blobUrls.push({url: url, size: file.size}); + upload.blobUrlsTotalSize += file.size || 0; + var maxMemory = upload.defaults.blobUrlsMaxMemory || 268435456; + var maxLength = upload.defaults.blobUrlsMaxQueueSize || 200; + while ((upload.blobUrlsTotalSize > maxMemory || upload.blobUrls.length > maxLength) && upload.blobUrls.length > 1) { + var obj = upload.blobUrls.splice(0, 1)[0]; + URL.revokeObjectURL(obj.url); + upload.blobUrlsTotalSize -= obj.size; + } + } + }); + } else { + var fileReader = new FileReader(); + fileReader.onload = function (e) { + $timeout(function () { + file.$ngfDataUrl = e.target.result; + deferred.resolve(e.target.result, file); + $timeout(function () { + delete file.$ngfDataUrl; + }, 1000); + }); + }; + fileReader.onerror = function () { + $timeout(function () { + file.$ngfDataUrl = ''; + deferred.reject(); + }); + }; + fileReader.readAsDataURL(file); + } + } else { + $timeout(function () { + file[disallowObjectUrl ? '$ngfDataUrl' : '$ngfBlobUrl'] = ''; + deferred.reject(); + }); + } + }); + + if (disallowObjectUrl) { + p = file.$$ngfDataUrlPromise = deferred.promise; + } else { + p = file.$$ngfBlobUrlPromise = deferred.promise; + } + p['finally'](function () { + delete file[disallowObjectUrl ? '$$ngfDataUrlPromise' : '$$ngfBlobUrlPromise']; + }); + return p; + }; + return upload; + }]); + + function getTagType(el) { + if (el.tagName.toLowerCase() === 'img') return 'image'; + if (el.tagName.toLowerCase() === 'audio') return 'audio'; + if (el.tagName.toLowerCase() === 'video') return 'video'; + return /./; + } + + function linkFileDirective(Upload, $timeout, scope, elem, attr, directiveName, resizeParams, isBackground) { + function constructDataUrl(file) { + var disallowObjectUrl = Upload.attrGetter('ngfNoObjectUrl', attr, scope); + Upload.dataUrl(file, disallowObjectUrl)['finally'](function () { + $timeout(function () { + var src = (disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl; + if (isBackground) { + elem.css('background-image', 'url(\'' + (src || '') + '\')'); + } else { + elem.attr('src', src); + } + if (src) { + elem.removeClass('ng-hide'); + } else { + elem.addClass('ng-hide'); + } + }); + }); + } + + $timeout(function () { + var unwatch = scope.$watch(attr[directiveName], function (file) { + var size = resizeParams; + if (directiveName === 'ngfThumbnail') { + if (!size) { + size = {width: elem[0].naturalWidth || elem[0].clientWidth, + height: elem[0].naturalHeight || elem[0].clientHeight}; + } + if (size.width === 0 && window.getComputedStyle) { + var style = getComputedStyle(elem[0]); + size = { + width: parseInt(style.width.slice(0, -2)), + height: parseInt(style.height.slice(0, -2)) + }; + } + } + + if (angular.isString(file)) { + elem.removeClass('ng-hide'); + if (isBackground) { + return elem.css('background-image', 'url(\'' + file + '\')'); + } else { + return elem.attr('src', file); + } + } + if (file && file.type && file.type.search(getTagType(elem[0])) === 0 && + (!isBackground || file.type.indexOf('image') === 0)) { + if (size && Upload.isResizeSupported()) { + size.resizeIf = function (width, height) { + return Upload.attrGetter('ngfResizeIf', attr, scope, + {$width: width, $height: height, $file: file}); + }; + Upload.resize(file, size).then( + function (f) { + constructDataUrl(f); + }, function (e) { + throw e; + } + ); + } else { + constructDataUrl(file); + } + } else { + elem.addClass('ng-hide'); + } + }); + + scope.$on('$destroy', function () { + unwatch(); + }); + }); + } + + + /** @namespace attr.ngfSrc */ + /** @namespace attr.ngfNoObjectUrl */ + ngFileUpload.directive('ngfSrc', ['Upload', '$timeout', function (Upload, $timeout) { + return { + restrict: 'AE', + link: function (scope, elem, attr) { + linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfSrc', + Upload.attrGetter('ngfResize', attr, scope), false); + } + }; + }]); + + /** @namespace attr.ngfBackground */ + /** @namespace attr.ngfNoObjectUrl */ + ngFileUpload.directive('ngfBackground', ['Upload', '$timeout', function (Upload, $timeout) { + return { + restrict: 'AE', + link: function (scope, elem, attr) { + linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfBackground', + Upload.attrGetter('ngfResize', attr, scope), true); + } + }; + }]); + + /** @namespace attr.ngfThumbnail */ + /** @namespace attr.ngfAsBackground */ + /** @namespace attr.ngfSize */ + /** @namespace attr.ngfNoObjectUrl */ + ngFileUpload.directive('ngfThumbnail', ['Upload', '$timeout', function (Upload, $timeout) { + return { + restrict: 'AE', + link: function (scope, elem, attr) { + var size = Upload.attrGetter('ngfSize', attr, scope); + linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfThumbnail', size, + Upload.attrGetter('ngfAsBackground', attr, scope)); + } + }; + }]); + + ngFileUpload.config(['$compileProvider', function ($compileProvider) { + if ($compileProvider.imgSrcSanitizationWhitelist) $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/); + if ($compileProvider.aHrefSanitizationWhitelist) $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/); + }]); + + ngFileUpload.filter('ngfDataUrl', ['UploadDataUrl', '$sce', function (UploadDataUrl, $sce) { + return function (file, disallowObjectUrl, trustedUrl) { + if (angular.isString(file)) { + return $sce.trustAsResourceUrl(file); + } + var src = file && ((disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl); + if (file && !src) { + if (!file.$ngfDataUrlFilterInProgress && angular.isObject(file)) { + file.$ngfDataUrlFilterInProgress = true; + UploadDataUrl.dataUrl(file, disallowObjectUrl); + } + return ''; + } + if (file) delete file.$ngfDataUrlFilterInProgress; + return (file && src ? (trustedUrl ? $sce.trustAsResourceUrl(src) : src) : file) || ''; + }; + }]); + +})(); + +ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) { + var upload = UploadDataUrl; + + function globStringToRegex(str) { + var regexp = '', excludes = []; + if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { + regexp = str.substring(1, str.length - 1); + } else { + var split = str.split(','); + if (split.length > 1) { + for (var i = 0; i < split.length; i++) { + var r = globStringToRegex(split[i]); + if (r.regexp) { + regexp += '(' + r.regexp + ')'; + if (i < split.length - 1) { + regexp += '|'; + } + } else { + excludes = excludes.concat(r.excludes); + } + } + } else { + if (str.indexOf('!') === 0) { + excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$'); + } else { + if (str.indexOf('.') === 0) { + str = '*' + str; + } + regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$'; + regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); + } + } + } + return {regexp: regexp, excludes: excludes}; + } + + upload.validatePattern = function (file, val) { + if (!val) { + return true; + } + var pattern = globStringToRegex(val), valid = true; + if (pattern.regexp && pattern.regexp.length) { + var regexp = new RegExp(pattern.regexp, 'i'); + valid = (file.type != null && regexp.test(file.type)) || + (file.name != null && regexp.test(file.name)); + } + var len = pattern.excludes.length; + while (len--) { + var exclude = new RegExp(pattern.excludes[len], 'i'); + valid = valid && (file.type == null || exclude.test(file.type)) && + (file.name == null || exclude.test(file.name)); + } + return valid; + }; + + upload.ratioToFloat = function (val) { + var r = val.toString(), xIndex = r.search(/[x:]/i); + if (xIndex > -1) { + r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1)); + } else { + r = parseFloat(r); + } + return r; + }; + + upload.registerModelChangeValidator = function (ngModel, attr, scope) { + if (ngModel) { + ngModel.$formatters.push(function (files) { + if (ngModel.$dirty) { + var filesArray = files; + if (files && !angular.isArray(files)) { + filesArray = [files]; + } + upload.validate(filesArray, 0, ngModel, attr, scope).then(function () { + upload.applyModelValidation(ngModel, filesArray); + }); + } + return files; + }); + } + }; + + function markModelAsDirty(ngModel, files) { + if (files != null && !ngModel.$dirty) { + if (ngModel.$setDirty) { + ngModel.$setDirty(); + } else { + ngModel.$dirty = true; + } + } + } + + upload.applyModelValidation = function (ngModel, files) { + markModelAsDirty(ngModel, files); + angular.forEach(ngModel.$ngfValidations, function (validation) { + ngModel.$setValidity(validation.name, validation.valid); + }); + }; + + upload.getValidationAttr = function (attr, scope, name, validationName, file) { + var dName = 'ngf' + name[0].toUpperCase() + name.substr(1); + var val = upload.attrGetter(dName, attr, scope, {$file: file}); + if (val == null) { + val = upload.attrGetter('ngfValidate', attr, scope, {$file: file}); + if (val) { + var split = (validationName || name).split('.'); + val = val[split[0]]; + if (split.length > 1) { + val = val && val[split[1]]; + } + } + } + return val; + }; + + upload.validate = function (files, prevLength, ngModel, attr, scope) { + ngModel = ngModel || {}; + ngModel.$ngfValidations = ngModel.$ngfValidations || []; + + angular.forEach(ngModel.$ngfValidations, function (v) { + v.valid = true; + }); + + var attrGetter = function (name, params) { + return upload.attrGetter(name, attr, scope, params); + }; + + var ignoredErrors = (upload.attrGetter('ngfIgnoreInvalid', attr, scope) || '').split(' '); + var runAllValidation = upload.attrGetter('ngfRunAllValidations', attr, scope); + + if (files == null || files.length === 0) { + return upload.emptyPromise({'validFiles': files, 'invalidFiles': []}); + } + + files = files.length === undefined ? [files] : files.slice(0); + var invalidFiles = []; + + function validateSync(name, validationName, fn) { + if (files) { + var i = files.length, valid = null; + while (i--) { + var file = files[i]; + if (file) { + var val = upload.getValidationAttr(attr, scope, name, validationName, file); + if (val != null) { + if (!fn(file, val, i)) { + if (ignoredErrors.indexOf(name) === -1) { + file.$error = name; + (file.$errorMessages = (file.$errorMessages || {}))[name] = true; + file.$errorParam = val; + if (invalidFiles.indexOf(file) === -1) { + invalidFiles.push(file); + } + if (!runAllValidation) { + files.splice(i, 1); + } + valid = false; + } else { + files.splice(i, 1); + } + } + } + } + } + if (valid !== null) { + ngModel.$ngfValidations.push({name: name, valid: valid}); + } + } + } + + validateSync('pattern', null, upload.validatePattern); + validateSync('minSize', 'size.min', function (file, val) { + return file.size + 0.1 >= upload.translateScalars(val); + }); + validateSync('maxSize', 'size.max', function (file, val) { + return file.size - 0.1 <= upload.translateScalars(val); + }); + var totalSize = 0; + validateSync('maxTotalSize', null, function (file, val) { + totalSize += file.size; + if (totalSize > upload.translateScalars(val)) { + files.splice(0, files.length); + return false; + } + return true; + }); + + validateSync('validateFn', null, function (file, r) { + return r === true || r === null || r === ''; + }); + + if (!files.length) { + return upload.emptyPromise({'validFiles': [], 'invalidFiles': invalidFiles}); + } + + function validateAsync(name, validationName, type, asyncFn, fn) { + function resolveResult(defer, file, val) { + function resolveInternal(fn) { + if (fn()) { + if (ignoredErrors.indexOf(name) === -1) { + file.$error = name; + (file.$errorMessages = (file.$errorMessages || {}))[name] = true; + file.$errorParam = val; + if (invalidFiles.indexOf(file) === -1) { + invalidFiles.push(file); + } + if (!runAllValidation) { + var i = files.indexOf(file); + if (i > -1) files.splice(i, 1); + } + defer.resolve(false); + } else { + var j = files.indexOf(file); + if (j > -1) files.splice(j, 1); + defer.resolve(true); + } + } else { + defer.resolve(true); + } + } + + if (val != null) { + asyncFn(file, val).then(function (d) { + resolveInternal(function () { + return !fn(d, val); + }); + }, function () { + resolveInternal(function () { + return attrGetter('ngfValidateForce', {$file: file}); + }); + }); + } else { + defer.resolve(true); + } + } + + var promises = [upload.emptyPromise(true)]; + if (files) { + files = files.length === undefined ? [files] : files; + angular.forEach(files, function (file) { + var defer = $q.defer(); + promises.push(defer.promise); + if (type && (file.type == null || file.type.search(type) !== 0)) { + defer.resolve(true); + return; + } + if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) { + upload.imageDimensions(file).then(function (d) { + resolveResult(defer, file, + attrGetter('ngfDimensions', {$file: file, $width: d.width, $height: d.height})); + }, function () { + defer.resolve(false); + }); + } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) { + upload.mediaDuration(file).then(function (d) { + resolveResult(defer, file, + attrGetter('ngfDuration', {$file: file, $duration: d})); + }, function () { + defer.resolve(false); + }); + } else { + resolveResult(defer, file, + upload.getValidationAttr(attr, scope, name, validationName, file)); + } + }); + } + var deffer = $q.defer(); + $q.all(promises).then(function (values) { + var isValid = true; + for (var i = 0; i < values.length; i++) { + if (!values[i]) { + isValid = false; + break; + } + } + ngModel.$ngfValidations.push({name: name, valid: isValid}); + deffer.resolve(isValid); + }); + return deffer.promise; + } + + var deffer = $q.defer(); + var promises = []; + + promises.push(validateAsync('maxHeight', 'height.max', /image/, + this.imageDimensions, function (d, val) { + return d.height <= val; + })); + promises.push(validateAsync('minHeight', 'height.min', /image/, + this.imageDimensions, function (d, val) { + return d.height >= val; + })); + promises.push(validateAsync('maxWidth', 'width.max', /image/, + this.imageDimensions, function (d, val) { + return d.width <= val; + })); + promises.push(validateAsync('minWidth', 'width.min', /image/, + this.imageDimensions, function (d, val) { + return d.width >= val; + })); + promises.push(validateAsync('dimensions', null, /image/, + function (file, val) { + return upload.emptyPromise(val); + }, function (r) { + return r; + })); + promises.push(validateAsync('ratio', null, /image/, + this.imageDimensions, function (d, val) { + var split = val.toString().split(','), valid = false; + for (var i = 0; i < split.length; i++) { + if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.01) { + valid = true; + } + } + return valid; + })); + promises.push(validateAsync('maxRatio', 'ratio.max', /image/, + this.imageDimensions, function (d, val) { + return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001; + })); + promises.push(validateAsync('minRatio', 'ratio.min', /image/, + this.imageDimensions, function (d, val) { + return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001; + })); + promises.push(validateAsync('maxDuration', 'duration.max', /audio|video/, + this.mediaDuration, function (d, val) { + return d <= upload.translateScalars(val); + })); + promises.push(validateAsync('minDuration', 'duration.min', /audio|video/, + this.mediaDuration, function (d, val) { + return d >= upload.translateScalars(val); + })); + promises.push(validateAsync('duration', null, /audio|video/, + function (file, val) { + return upload.emptyPromise(val); + }, function (r) { + return r; + })); + + promises.push(validateAsync('validateAsyncFn', null, null, + function (file, val) { + return val; + }, function (r) { + return r === true || r === null || r === ''; + })); + + $q.all(promises).then(function () { + + if (runAllValidation) { + for (var i = 0; i < files.length; i++) { + var file = files[i]; + if (file.$error) { + files.splice(i--, 1); + } + } + } + + runAllValidation = false; + validateSync('maxFiles', null, function (file, val, i) { + return prevLength + i < val; + }); + + deffer.resolve({'validFiles': files, 'invalidFiles': invalidFiles}); + }); + return deffer.promise; + }; + + upload.imageDimensions = function (file) { + if (file.$ngfWidth && file.$ngfHeight) { + var d = $q.defer(); + $timeout(function () { + d.resolve({width: file.$ngfWidth, height: file.$ngfHeight}); + }); + return d.promise; + } + if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise; + + var deferred = $q.defer(); + $timeout(function () { + if (file.type.indexOf('image') !== 0) { + deferred.reject('not image'); + return; + } + upload.dataUrl(file).then(function (dataUrl) { + var img = angular.element('').attr('src', dataUrl) + .css('visibility', 'hidden').css('position', 'fixed') + .css('max-width', 'none !important').css('max-height', 'none !important'); + + function success() { + var width = img[0].naturalWidth || img[0].clientWidth; + var height = img[0].naturalHeight || img[0].clientHeight; + img.remove(); + file.$ngfWidth = width; + file.$ngfHeight = height; + deferred.resolve({width: width, height: height}); + } + + function error() { + img.remove(); + deferred.reject('load error'); + } + + img.on('load', success); + img.on('error', error); + + var secondsCounter = 0; + function checkLoadErrorInCaseOfNoCallback() { + $timeout(function () { + if (img[0].parentNode) { + if (img[0].clientWidth) { + success(); + } else if (secondsCounter++ > 10) { + error(); + } else { + checkLoadErrorInCaseOfNoCallback(); + } + } + }, 1000); + } + + checkLoadErrorInCaseOfNoCallback(); + + angular.element(document.getElementsByTagName('body')[0]).append(img); + }, function () { + deferred.reject('load error'); + }); + }); + + file.$ngfDimensionPromise = deferred.promise; + file.$ngfDimensionPromise['finally'](function () { + delete file.$ngfDimensionPromise; + }); + return file.$ngfDimensionPromise; + }; + + upload.mediaDuration = function (file) { + if (file.$ngfDuration) { + var d = $q.defer(); + $timeout(function () { + d.resolve(file.$ngfDuration); + }); + return d.promise; + } + if (file.$ngfDurationPromise) return file.$ngfDurationPromise; + + var deferred = $q.defer(); + $timeout(function () { + if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) { + deferred.reject('not media'); + return; + } + upload.dataUrl(file).then(function (dataUrl) { + var el = angular.element(file.type.indexOf('audio') === 0 ? '