Last active
August 29, 2015 14:05
-
-
Save yogeshsr/3fb71e38e7a06d0d3383 to your computer and use it in GitHub Desktop.
Online file picker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define( [ 'jquery', 'enketo-js/Widget' ], function( $, Widget ) { | |
"use strict"; | |
var pluginName = 'offlineFilepicker'; | |
/** | |
* Loop through $('form.or input[type="file"]') during submission submit. | |
* | |
* | |
* @constructor | |
* @param {Element} element [description] | |
* @param {(boolean|{touch: boolean, maxlength:number})} options options | |
* @param {*=} e event | |
*/ | |
function OfflineFilepicker( element, options, e ) { | |
if ( e ) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
} | |
this.namespace = pluginName; | |
Widget.call( this, element, options ); | |
this._init(); | |
} | |
//copy the prototype functions from the Widget super class | |
OfflineFilepicker.prototype = Object.create( Widget.prototype ); | |
//ensure the constructor is the new one | |
OfflineFilepicker.prototype.constructor = OfflineFilepicker; | |
/** | |
* initialize | |
* | |
*/ | |
OfflineFilepicker.prototype._init = function() { | |
var existingFileName, | |
$input = $( this.element ), | |
that = this; | |
this._changeListener(); | |
//this attribute is added by the Form instance when loading data to edit | |
existingFileName = $input.attr( 'data-loaded-file-name' ); | |
if ( existingFileName ) { | |
// TODO how to fetch file for preview | |
$input.after( '<div class="file-loaded text-warning">This form was loaded with "' + | |
existingFileName + '". To preserve this file, do not change this input.</div>' ); | |
} | |
$input.parent().addClass( 'with-media clearfix' ); | |
}; | |
OfflineFilepicker.prototype._getMaxSubmissionSize = function() { | |
var maxSize = $( document ).data( 'maxSubmissionSize' ); | |
return maxSize || 5 * 1024 * 1024; | |
}; | |
OfflineFilepicker.prototype._changeListener = function() { | |
/* | |
* This delegated eventhander should actually be added asynchronously( or not at all | |
* if no FS support / permission ).However, it | |
* needs to start * before * the regular input change event handler | |
* for 2 reasons: | |
* 1.If saving the file in the browser 's file system fails, the instance should not be updated | |
* 2. The regular eventhandler has event.stopImmediatePropagation which would mean this handler is never called. | |
* The easiest way to achieve this is to always add it but only let it do something if permission is granted to use FS. | |
*/ | |
var that = this, | |
$input = $( this.element ); | |
$input.on( 'change.passthrough.' + this.namespace, function( event ) { | |
var prevFileName, file, mediaType, $preview, | |
maxSubmissionSize = that._getMaxSubmissionSize(); | |
//console.debug( 'namespace: ' + event.namespace ); | |
if ( event.namespace === 'passthrough' ) { | |
//console.debug('returning true'); | |
$input.trigger( 'change.file' ); | |
return false; | |
} | |
prevFileName = $input.attr( 'data-previous-file-name' ); | |
file = $input[ 0 ].files[ 0 ]; | |
mediaType = $input.attr( 'accept' ); | |
$input.siblings( '.file-preview, .file-loaded' ).remove(); | |
console.debug( 'file: ', file ); | |
//TODO retrive file $('input[type="file"]')[0].files[0] | |
if ( file && file.size > 0 && file.size <= maxSubmissionSize ) { | |
var reader = new FileReader(); | |
reader.onload = function (e) { | |
$preview = that._createPreview( e.target.result, mediaType ); | |
$input.attr( 'data-previous-file-name', file.name ) | |
.removeAttr( 'data-loaded-file-name' ) | |
.siblings( '.file-loaded' ).remove(); | |
$input.trigger( 'change.passthrough' ).after( $preview ); | |
} | |
reader.readAsDataURL(file); | |
return false; | |
} else { | |
//clear instance value by letting it bubble up to normal change handler | |
if ( file.size > maxSubmissionSize ) { | |
$input.after( '<div class="file-feedback text-error">' + | |
'File too large (max ' + | |
( Math.round( ( maxSubmissionSize * 100 ) / ( 1024 * 1024 ) ) / 100 ) + | |
' Mb)</div>' ); | |
} | |
return true; | |
} | |
} ); | |
}; | |
OfflineFilepicker.prototype._createPreview = function( fsURL, mediaType ) { | |
var $preview; | |
$preview = ( mediaType && mediaType === 'image/*' ) ? $( '<img />' ) : ( mediaType === 'audio/*' ) ? $( '<audio controls="controls"/>' ) : ( mediaType === 'video/*' ) ? $( '<video controls="controls"/>' ) : $( '<span>No preview for this mediatype</span>' ); | |
return $preview.addClass( 'file-preview' ).attr( 'src', fsURL ); | |
}; | |
OfflineFilepicker.prototype.destroy = function( element ) { | |
$( element ) | |
//data is not used elsewhere by enketo | |
.removeData( this.namespace ) | |
//remove all the event handlers that used this.namespace as the namespace | |
.off( '.' + this.namespace ) | |
//show the original element | |
.show() | |
//remove elements immediately after the target that have the widget class | |
.next( '.widget' ).remove().end() | |
//console.debug( this.namespace, 'destroy' ); | |
.siblings( '.file-feedback, .file-preview, .file-loaded' ).remove(); | |
}; | |
/** | |
* | |
*/ | |
$.fn[ pluginName ] = function( options, event ) { | |
options = options || {}; | |
return this.each( function() { | |
var $this = $( this ), | |
data = $this.data( pluginName ); | |
//only instantiate if options is an object (i.e. not a string) and if it doesn't exist already | |
if ( !data && typeof options === 'object' ) { | |
$this.data( pluginName, ( data = new OfflineFilepicker( this, options, event ) ) ); | |
} | |
//only call method if widget was instantiated before | |
else if ( data && typeof options == 'string' ) { | |
//pass the element as a parameter as this is used in fix() | |
data[ options ]( this ); | |
} | |
} ); | |
}; | |
} ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment