1+ ( function ( window , document ) {
2+
3+ // Create all modules and define dependencies to make sure they exist
4+ // and are loaded in the correct order to satisfy dependency injection
5+ // before all nested files are concatenated by Grunt
6+
7+ // Config
8+ angular . module ( 'ngS3upload.config' , [ ] ) .
9+ value ( 'ngS3upload.config' , {
10+ debug : true
11+ } ) .
12+ config ( [ '$compileProvider' , function ( $compileProvider ) {
13+ if ( angular . isDefined ( $compileProvider . urlSanitizationWhitelist ) ) {
14+ $compileProvider . urlSanitizationWhitelist ( / ^ \s * ( h t t p s ? | f t p | m a i l t o | f i l e | d a t a ) : / ) ;
15+ } else {
16+ $compileProvider . aHrefSanitizationWhitelist ( / ^ \s * ( h t t p s ? | f t p | m a i l t o | f i l e | d a t a ) : / ) ;
17+ }
18+ } ] ) ;
19+
20+ // Modules
21+ angular . module ( 'ngS3upload.directives' , [ ] ) ;
22+ angular . module ( 'ngS3upload' ,
23+ [
24+ 'ngS3upload.config' ,
25+ 'ngS3upload.directives' ,
26+ 'ngS3upload.services' ,
27+ 'ngSanitize'
28+ ] ) ;
29+ angular . module ( 'ngS3upload.services' , [ ] ) .
30+ service ( 'S3Uploader' , [ '$http' , '$q' , '$window' , function ( $http , $q , $window ) {
31+ this . uploads = 0 ;
32+ var self = this ;
33+
34+ this . getUploadOptions = function ( uri ) {
35+ var deferred = $q . defer ( ) ;
36+ $http . get ( uri ) .
37+ success ( function ( response , status ) {
38+ deferred . resolve ( response ) ;
39+ } ) . error ( function ( error , status ) {
40+ deferred . reject ( error ) ;
41+ } ) ;
42+
43+ return deferred . promise ;
44+ } ;
45+
46+ this . randomString = function ( length ) {
47+ var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ;
48+ var result = '' ;
49+ for ( var i = length ; i > 0 ; -- i ) result += chars [ Math . round ( Math . random ( ) * ( chars . length - 1 ) ) ] ;
50+
51+ return result ;
52+ } ;
53+
54+
55+ this . upload = function ( scope , uri , key , acl , type , accessKey , policy , signature , file ) {
56+ var deferred = $q . defer ( ) ;
57+ scope . attempt = true ;
58+
59+ var fd = new FormData ( ) ;
60+ fd . append ( 'key' , key ) ;
61+ fd . append ( 'acl' , acl ) ;
62+ fd . append ( 'Content-Type' , file . type ) ;
63+ fd . append ( 'AWSAccessKeyId' , accessKey ) ;
64+ fd . append ( 'policy' , policy ) ;
65+ fd . append ( 'signature' , signature ) ;
66+ fd . append ( "file" , file ) ;
67+
68+ var xhr = new XMLHttpRequest ( ) ;
69+ xhr . upload . addEventListener ( "progress" , uploadProgress , false ) ;
70+ xhr . addEventListener ( "load" , uploadComplete , false ) ;
71+ xhr . addEventListener ( "error" , uploadFailed , false ) ;
72+ xhr . addEventListener ( "abort" , uploadCanceled , false ) ;
73+
74+ // Define event handlers
75+ function uploadProgress ( e ) {
76+ scope . $apply ( function ( ) {
77+ if ( e . lengthComputable ) {
78+ scope . progress = Math . round ( e . loaded * 100 / e . total ) ;
79+ } else {
80+ scope . progress = 'unable to compute' ;
81+ }
82+ } ) ;
83+ }
84+ function uploadComplete ( e ) {
85+ scope . $apply ( function ( ) {
86+ self . uploads -- ;
87+ scope . uploading = false ;
88+ scope . success = true ;
89+ deferred . resolve ( ) ;
90+ } ) ;
91+ }
92+ function uploadFailed ( e ) {
93+ scope . $apply ( function ( ) {
94+ self . uploads -- ;
95+ scope . uploading = false ;
96+ scope . success = false ;
97+ deferred . reject ( ) ;
98+ } ) ;
99+ }
100+ function uploadCanceled ( e ) {
101+ scope . $apply ( function ( ) {
102+ self . uploads -- ;
103+ scope . uploading = false ;
104+ scope . success = false ;
105+ deferred . reject ( ) ;
106+ } ) ;
107+ }
108+
109+ // Send the file
110+ scope . uploading = true ;
111+ this . uploads ++ ;
112+ xhr . open ( 'POST' , uri , true ) ;
113+ xhr . send ( fd ) ;
114+
115+ return deferred . promise ;
116+ } ;
117+
118+ this . isUploading = function ( ) {
119+ return this . uploads > 0 ;
120+ } ;
121+ } ] ) ;
122+ angular . module ( 'ngS3upload.directives' , [ ] ) .
123+ directive ( 's3Upload' , [ '$parse' , 'S3Uploader' , function ( $parse , S3Uploader ) {
124+ return {
125+ restrict : 'AC' ,
126+ require : '?ngModel' ,
127+ replace : true ,
128+ transclude : false ,
129+ scope : true ,
130+ controller : [ '$scope' , '$element' , '$attrs' , '$transclude' , function ( $scope , $element , $attrs , $transclude ) {
131+ $scope . attempt = false ;
132+ $scope . success = false ;
133+ $scope . uploading = false ;
134+
135+ $scope . barClass = function ( ) {
136+ return {
137+ "bar-success" : $scope . attempt && ! $scope . uploading && $scope . success
138+ } ;
139+ } ;
140+ } ] ,
141+ compile : function ( element , attr , linker ) {
142+ return {
143+ pre : function ( $scope , $element , $attr ) {
144+ if ( angular . isUndefined ( $attr . bucket ) ) {
145+ throw Error ( 'bucket is a mandatory attribute' ) ;
146+ }
147+ } ,
148+ post : function ( scope , element , attrs , ngModel ) {
149+ // Build the opts array
150+ var opts = angular . extend ( { } , scope . $eval ( attrs . s3UploadOptions || attrs . options ) ) ;
151+ opts = angular . extend ( {
152+ submitOnChange : true ,
153+ getOptionsUri : '/getS3Options' ,
154+ acl : 'public-read' ,
155+ uploadingKey : 'uploading' ,
156+ folder : ''
157+ } , opts ) ;
158+ var bucket = scope . $eval ( attrs . bucket ) ;
159+
160+ // Bind the button click event
161+ var button = angular . element ( element . children ( ) [ 0 ] ) ,
162+ file = angular . element ( element . find ( "input" ) [ 0 ] ) ;
163+ button . bind ( 'click' , function ( e ) {
164+ file [ 0 ] . click ( ) ;
165+ } ) ;
166+
167+ // Update the scope with the view value
168+ ngModel . $render = function ( ) {
169+ scope . filename = ngModel . $viewValue ;
170+ } ;
171+
172+ var uploadFile = function ( ) {
173+ var selectedFile = file [ 0 ] . files [ 0 ] ;
174+ var filename = selectedFile . name ;
175+ var ext = filename . split ( '.' ) . pop ( ) ;
176+
177+ scope . $apply ( function ( ) {
178+ S3Uploader . getUploadOptions ( opts . getOptionsUri ) . then ( function ( s3Options ) {
179+ ngModel . $setValidity ( 'uploading' , false ) ;
180+ var s3Uri = 'https://' + bucket + '.s3.amazonaws.com/' + opts . folder ;
181+ var key = ( new Date ( ) ) . getTime ( ) + '-' + S3Uploader . randomString ( 16 ) + "." + ext ;
182+ S3Uploader . upload ( scope ,
183+ s3Uri ,
184+ key ,
185+ opts . acl ,
186+ selectedFile . type ,
187+ s3Options . key ,
188+ s3Options . policy ,
189+ s3Options . signature ,
190+ selectedFile
191+ ) . then ( function ( ) {
192+ ngModel . $setViewValue ( s3Uri + key ) ;
193+ scope . filename = ngModel . $viewValue ;
194+ ngModel . $setValidity ( 'uploading' , true ) ;
195+ ngModel . $setValidity ( 'succeeded' , true ) ;
196+ } , function ( ) {
197+ scope . filename = ngModel . $viewValue ;
198+ ngModel . $setValidity ( 'uploading' , true ) ;
199+ ngModel . $setValidity ( 'succeeded' , false ) ;
200+ } ) ;
201+
202+ } , function ( error ) {
203+ throw Error ( "Can't receive the needed options for S3 " + error ) ;
204+ } ) ;
205+ } ) ;
206+ } ;
207+
208+ element . bind ( 'change' , function ( nVal ) {
209+ if ( opts . submitOnChange ) {
210+ uploadFile ( ) ;
211+ }
212+ } ) ;
213+ }
214+ } ;
215+ } ,
216+ template : '<div class="upload-wrap">' +
217+ '<button class="btn btn-primary" type="button"><span ng-if="!filename">Choose file</span><span ng-if="filename">Replace file</span></button>' +
218+ '<a ng-href="{{ filename }}" target="_blank" class="" ng-if="filename" > Stored file </a>' +
219+ '<div class="progress progress-striped" ng-class="{active: uploading}" ng-show="attempt" style="margin-top: 10px">' +
220+ '<div class="bar" style="width: {{ progress }}%;" ng-class="barClass()"></div>' +
221+ '</div>' +
222+ '<input type="file" style="display: none"/>' +
223+ '</div>'
224+ } ;
225+ } ] ) ;
226+ } ) ( window , document ) ;
0 commit comments