Skip to content

Instantly share code, notes, and snippets.

@KamiKillertO
Created July 27, 2018 07:23
Show Gist options
  • Select an option

  • Save KamiKillertO/14f4242ac8f5d65a18b22d7993411598 to your computer and use it in GitHub Desktop.

Select an option

Save KamiKillertO/14f4242ac8f5d65a18b22d7993411598 to your computer and use it in GitHub Desktop.
New Twiddle
const ua = navigator.userAgent;
function isIE() {
return ua.indexOf('MSIE')!=-1 || ua.indexOf('Trident/') !== -1;
}
function getIEVersion() {
const msie = ua.indexOf('MSIE ');
if (msie > 0) {
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
}
const trident = ua.indexOf('Trident/');
if (trident > 0) {
const rv = ua.indexOf('rv:');
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
}
return null;
}
function isEdge() {
return ua.indexOf('Edge/') !== -1;
}
function getEdgeVersion() {
const edge = ua.indexOf('Edge/');
return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
}
function isChrome() {
return ua.indexOf('Chrome') !== -1;
}
function getChromeVersion() {
const version = /Chrome\/(\d+)./.exec(ua);
return version !== null? parseInt(version[1], 10) : null;
}
function isFirefox() {
return ua.indexOf('Firefox') !== -1;
}
function getFirefoxVersion() {
const version = /Firefox\/(\d+)./.exec(ua);
return version !== null? parseInt(version[1], 10) : null;
}
function isOpera() {
return ua.indexOf('Opera') !== -1 || ua.indexOf('OPR');
}
function isSafari() {
return ua.indexOf('Safari') !== -1 && ua.indexOf('Chrome') === -1;
}
function getSafariVersion() {
const version = /Version\/(\d+)./.exec(ua);
return version !== null? parseInt(version[1], 10) : null;
}
export { isIE, getIEVersion, isEdge, getEdgeVersion, isChrome, getChromeVersion, isFirefox, getFirefoxVersion, isOpera, isSafari, getSafariVersion }
import Ember from 'ember';
export default Ember.Component.extend({
});
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle'
});
import Mixin from '@ember/object/mixin';
import $ from 'jquery';
import { get, set } from '@ember/object';
import { inject as service } from '@ember/service';
import { run } from '@ember/runloop';
import { isIE, isEdge, isSafari, getSafariVersion } from 'appaloosa-components/browser-detect';
// find a way to namespace variables
// variables namespace using an object => issue (share between all mixin instance ><)
// see https://dockyard.com/blog/2015/09/18/ember-best-practices-avoid-leaking-state-into-factories
// maybe a mixin is not the good solution
export default Mixin.create({
assetsUploader: service(),
classNameBindings: ['isDragOver:drag'],
/**
* @property {boolean} disableDragAndDrop - Disable component dragAndDrop
* @default false
* @public
*/
disableDragAndDrop: false,
/**
* @property {boolean} isDragOver - Will be set to true if something is dragOver the component
* @readonly // disable update in didUpdateAttrs
* @default false
* @public
*/
isDragOver: false,
/**
* @property {boolean} uploadDone - Will be set to true when upload is done.
* @readonly // disable update in didUpdateAttrs
* @default false
* @public
*/
uploadDone: false,
/**
* @property {boolean} uploadStart - Will be set to true when uploadStart.
* @readonly // disable update in didUpdateAttrs
* @default false
* @public
*/
// behavior when multiple files??
uploadStart: false,
/**
* @property {string} dropZoneSelector - You can optionally pass in a jQuery style selector string for the dragAnDrop zone
* @default null
* @public
*/
dropZoneSelector: null,
/**
* @property {string} acceptedMime - The mime types accepted by the input and the drag-and-drop. Use a comma between types
* @default '*'
* @public
*/
// prevent update
acceptedMime: "*",
__R_A_MIME: null,
/**
* @property {string} acceptedFileExtensions - The files extensions accepted by the input and the drag-and-drop. Use a comma between types. No need to add the dot before;
* @default ''
* @public
*/
// prevent update
acceptedExtensions: '',
__R_A_EXTENSIONS: null,
/**
* @property {boolean} inputMultiple - Input `multiple` attribute
* @default true
* @public
*/
inputMultiple: true,
/**
* @property {boolean} invalidFileDropped - Will be set to true if an invalid file is dropped
* @default false
* @public
*/
invalidFileDropped: false,
/**
* @property {File[]} processingFiles - Contain all files dropped. File contained new properties `uploadStart`, `uploadPercentage` and `uploadDone`
* @default true
* @public
*/
processingFiles: [],
/**
* @property {boolean} directUpload - By default files are uploaded after drop.Set it to false to disable this behavior
* @default true
* @public
*/
directUpload: true,
/**
* @property {function} uploadDoneAction - An ember action to trigger when the file is uploaded
* @default A noop function
* @public
*/
uploadDoneAction: () => {},
__input: null,
__resetDefaultValue() {
set(this, 'uploadDone', false);
set(this, 'uploadStart', false);
set(this, 'invalidFileDropped', false);
this.__clearProcessingFiles();
},
init() {
this._super(...arguments);
const acceptedMime = get(this, 'acceptedMime');
const R_A_MIME = new RegExp(acceptedMime.replace(/\//g, '\\/').replace(/,/g, '|'));
set(this, '__R_A_MIME', R_A_MIME);
const acceptedExtensions = get(this, 'acceptedExtensions');
const R_A_EXTENSIONS = new RegExp(`\\.(${acceptedExtensions.replace(/\./g,'').replace(/,/g, '|')})$`);
set(this, '__R_A_EXTENSIONS', R_A_EXTENSIONS);
},
drop(event) {
event.preventDefault();
event.stopPropagation();
set(this, 'isDragOver', false);
if (get(this, 'disableDragAndDrop') === true) {
return;
}
const files = event.dataTransfer.files; // Array of all files
if (this.canDropFiles(files) === true) {
set(this, 'invalidFileDropped', true);
return false;
}
this.__clearProcessingFiles();
// clear uploadQueue ? before?
set(this, "disableDragAndDrop", true); // temporary
this.__resetDefaultValue();
this.__populateUploadQueue(files);
if (get(this, "directUpload")) {
this.__startUpload();
}
},
dragOver: function (e) {
if (get(this, 'disableDragAndDrop') === true) { //find a better way to disable drag and Drop
return false;
}
let container = this.$(get(this, 'dropZoneSelector'));
if (container.is(this.$(e.target)) || container.has(this.$(e.target)).length) {
e.preventDefault(e);
e.stopPropagation();
// Makes it possible to drag files from chrome's download bar
// http://stackoverflow.com/questions/19526430/drag-and-drop-file-uploads-from-chrome-downloads-bar
// Try is required to prevent bug in Internet Explorer 11 (SCRIPT65535 exception)
var efct = void 0;
try {
efct = e.dataTransfer.effectAllowed;
} catch (error) {
//
}
e.dataTransfer.dropEffect = ('move' === efct || 'linkMove' === efct) ? 'move' : 'copy';
set(this, 'isDragOver', true);
}
return false;
},
dragLeave: function (event) {
event.preventDefault();
event.stopPropagation();
if (get(this, 'disableDragAndDrop') === true) {
return;
}
set(this, 'isDragOver', false);
},
canDropFiles(files) {
if (files.length === 0) {
return true;
}
if ( files.length > 1 && get(this, 'inputMultiple') === false) {
return true;
}
if (this.__isAcceptedFiles(files) === false) {
return true;
}
return false;
},
/**
* Check files extensions
*/
__isAcceptedFiles(files){
for (let i = 0; i < files.length; i++) {
if (this.isMimeAccepted(files[i]) === false && this.isExtensionAccepted(files[i]) === false) {
return false;
}
}
return true;
},
isMimeAccepted(file) {
return get(this, '__R_A_MIME').test(file.type);
},
// in timeInit // @see didInsertElement
isExtensionAccepted(file) {
return get(this, '__R_A_EXTENSIONS').test(file.name);
},
__populateUploadQueue(files) {
const processingFiles = get(this, 'processingFiles');
for (let i = 0; i < files.length; i++) {
let file = files[i];
file.localFileUri = URL.createObjectURL(file); // add a method to generate thumbnails?
file.uploadStart = false;
file.uploadDone = false;
file.uploadPercentage = 0;
processingFiles.pushObject(file);
}
},
__filePercentageUpdate(file, percentage) {
set(file, 'uploadStart', true);
set(file, 'uploadPercentage', percentage);
},
__startUpload() {
const processingFiles = get(this, 'processingFiles');
processingFiles.forEach(file => this.__uploadFile(file));
},
async __uploadFile(file) {
set(this, 'uploadStart', true);
const key = await get(this, 'assetsUploader').upload(file, this.__filePercentageUpdate.bind(this));
get(this, 'uploadDoneAction')(key, file);
set(file, 'uploadDone', true);
set(file, 'uploadStart', false);
if (get(this, 'inputMultiple') && this.__allFilesUploaded() === true) {
set(this, 'uploadDone', true); // not updating here ><
set(this, 'disableDragAndDrop', false); // temporary
set(this, 'uploadStart', false);
} else {
set(this, 'uploadDone', true);
set(this, 'uploadStart', false);
}
},
__allFilesUploaded() {
return get(this, 'processingFiles').find(file => file.uploadDone === false) === undefined;
},
__clearProcessingFiles() { // should stop upload too
get(this, 'processingFiles').clear();
},
__setupInput() {
const input = document.createElement('input');
input.type = "file";
if (isIE() || isEdge() || (isSafari() && getSafariVersion() < 12)) {
// Edge an safari does not support files extension in accept
// https://caniuse.com/#feat=input-file-accept
// files check will be done in the change event handler
input.accept = `*`;
} else {
input.accept = `${get(this, 'acceptedExtensions')},${get(this, 'acceptedMime')}`;
}
input.multiple = get(this, 'inputMultiple');
return input;
},
__inputChangeHandler({ target }) {
const files = target.files;
run(() => {
if (this.__isAcceptedFiles(files) ===false) {
set(this, 'invalidFileDropped', true);
return false;
}
this.__clearProcessingFiles();
this.__populateUploadQueue(files);
if (get(this, "directUpload")) {
this.__startUpload();
}
$(target).remove();
});
},
actions: {
openFilePicker() {
this.__resetDefaultValue();
const input = this.__setupInput();
$(input).on('change', this.__inputChangeHandler.bind(this));
$(input).trigger('click');
},
uploadFiles() {
this.__startUpload();
}
}
});
<h1>Welcome to {{appName}}</h1>
<br>
<br>
{{outlet}}
<br>
<br>
{
"version": "0.15.0",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js",
"ember": "3.2.2",
"ember-template-compiler": "3.2.2",
"ember-testing": "3.2.2"
},
"addons": {
"ember-data": "3.2.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment