From 3f44b1f866e98cc30ef04c7ab1eef7f5ec074ac4 Mon Sep 17 00:00:00 2001 From: Ian Prest Date: Fri, 3 Jul 2015 00:42:30 -0400 Subject: [PATCH] Added the ability to upload a JSON file. -- "Upload" button on the raw-data tab -- Drag & drop to either the raw-data editor, or to the main kb preview area --- CONTRIB.md | 1 + js/ng-file-upload.min.js | 2 ++ kb.css | 2 ++ kb.html | 11 ++++++++--- kb.js | 18 ++++++++++++++++-- serial.js | 8 -------- 6 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 js/ng-file-upload.min.js diff --git a/CONTRIB.md b/CONTRIB.md index e16dc43..b1bad9d 100644 --- a/CONTRIB.md +++ b/CONTRIB.md @@ -15,6 +15,7 @@ Third-Party Software The following third-party software packages were used in the creation of keyboard-layout-editor.com: * [AngularJS](https://angularjs.org/) * [AngularUI](https://angular-ui.github.io/) +* [ng-file-upload](https://github.com/danialfarid/ng-file-upload) * [Bootstrap](http://getbootstrap.com/) * [JQuery](https://jquery.com/) * [colors.js](https://gist.github.com/mikelikespie/641528) (quick RGB to LAB conversions) diff --git a/js/ng-file-upload.min.js b/js/ng-file-upload.min.js new file mode 100644 index 0000000..8a9ecbc --- /dev/null +++ b/js/ng-file-upload.min.js @@ -0,0 +1,2 @@ +/*! 5.0.9 */ +!window.XMLHttpRequest||window.FileAPI&&FileAPI.shouldLoad||(window.XMLHttpRequest.prototype.setRequestHeader=function(a){return function(b,c){if("__setXHR_"===b){var d=c(this);d instanceof Function&&d(this)}else a.apply(this,arguments)}}(window.XMLHttpRequest.prototype.setRequestHeader));var ngFileUpload=angular.module("ngFileUpload",[]);ngFileUpload.version="5.0.9",ngFileUpload.service("Upload",["$http","$q","$timeout",function(a,b,c){function d(d){d.method=d.method||"POST",d.headers=d.headers||{};var e=b.defer(),f=e.promise;return d.headers.__setXHR_=function(){return function(a){a&&(d.__XHR=a,d.xhrFn&&d.xhrFn(a),a.upload.addEventListener("progress",function(a){a.config=d,e.notify?e.notify(a):f.progressFunc&&c(function(){f.progressFunc(a)})},!1),a.upload.addEventListener("load",function(a){a.lengthComputable&&(a.config=d,e.notify?e.notify(a):f.progressFunc&&c(function(){f.progressFunc(a)}))},!1))}},a(d).then(function(a){e.resolve(a)},function(a){e.reject(a)},function(a){e.notify(a)}),f.success=function(a){return f.then(function(b){a(b.data,b.status,b.headers,d)}),f},f.error=function(a){return f.then(null,function(b){a(b.data,b.status,b.headers,d)}),f},f.progress=function(a){return f.progressFunc=a,f.then(null,null,function(b){a(b)}),f},f.abort=function(){return d.__XHR&&c(function(){d.__XHR.abort()}),f},f.xhr=function(a){return d.xhrFn=function(b){return function(){b&&b.apply(f,arguments),a.apply(f,arguments)}}(d.xhrFn),f},f}this.upload=function(a){function b(c,d,e){if(void 0!==d)if(angular.isDate(d)&&(d=d.toISOString()),angular.isString(d))c.append(e,d);else if("form"===a.sendFieldsAs)if(angular.isObject(d))for(var f in d)d.hasOwnProperty(f)&&b(c,d[f],e+"["+f+"]");else c.append(e,d);else d=angular.isString(d)?d:JSON.stringify(d),"json-blob"===a.sendFieldsAs?c.append(e,new Blob([d],{type:"application/json"})):c.append(e,d)}return a.headers=a.headers||{},a.headers["Content-Type"]=void 0,a.transformRequest=a.transformRequest?angular.isArray(a.transformRequest)?a.transformRequest:[a.transformRequest]:[],a.transformRequest.push(function(c){var d,e=new FormData,f={};for(d in a.fields)a.fields.hasOwnProperty(d)&&(f[d]=a.fields[d]);c&&(f.data=c);for(d in f)if(f.hasOwnProperty(d)){var g=f[d];a.formDataAppender?a.formDataAppender(e,d,g):b(e,g,d)}if(null!=a.file){var h=a.fileFormDataName||"file";if(angular.isArray(a.file))for(var i=angular.isString(h),j=0;j');return m(d),k()?(e.replaceWith(d),e=d,d.attr("__ngf_gen__",!0),j(e)(a)):(d.css("visibility","hidden").css("position","absolute").css("overflow","hidden").css("width","0px").css("height","0px").css("z-index","-100000").css("border","none").css("margin","0px").css("padding","0px").attr("tabindex","-1"),e.$$ngfRefElem&&e.$$ngfRefElem.remove(),e.$$ngfRefElem=d,document.body.appendChild(d[0])),d}function o(b){d(h,i,a,g,f,f.ngfChange||f.ngfSelect,[],[],b,!0)}function p(c){function d(a){a&&i[0].click(),(k()||!a)&&e.bind("click touchend",p)}if(e.attr("disabled")||q)return!1;null!=c&&(c.preventDefault(),c.stopPropagation());var g=h(f.ngfResetOnClick)(a)!==!1,i=n(c,g);return i&&((!c||g)&&i.bind("change",l),c&&g&&h(f.ngfResetModelOnClick)(a)!==!1&&o(c),b(navigator.userAgent)?setTimeout(function(){d(c)},0):d(c)),!1}if(!e.attr("__ngf_gen__")){a.$on("$destroy",function(){e.$$ngfRefElem&&e.$$ngfRefElem.remove()});var q=!1;-1===f.ngfSelect.search(/\W+$files\W+/)&&a.$watch(f.ngfSelect,function(a){q=a===!1});var r=!1;window.FileAPI&&window.FileAPI.ngfFixIE?window.FileAPI.ngfFixIE(e,n,m,l):p()}}function b(a){var b=a.match(/Android[^\d]*(\d+)\.(\d+)/);return b&&b.length>2?parseInt(b[1])<4||4===parseInt(b[1])&&parseInt(b[2])<4:/.*Windows.*Safari.*/.test(a)}ngFileUpload.directive("ngfSelect",["$parse","$timeout","$compile",function(b,c,d){return{restrict:"AEC",require:"?ngModel",link:function(e,f,g,h){a(e,f,g,h,b,c,d)}}}]),ngFileUpload.validate=function(a,b,c,d,e){function f(a){if(a.length>2&&"/"===a[0]&&"/"===a[a.length-1])return a.substring(1,a.length-1);var b=a.split(","),c="";if(b.length>1)for(var d=0;d|:\\-]","g"),"\\$&")+"$",c=c.replace(/\\\*/g,".*").replace(/\\\?/g,".");return c}var g=b(c.ngfAccept)(a,{$file:d,$event:e}),h=b(c.ngfMaxSize)(a,{$file:d,$event:e})||9007199254740991,i=b(c.ngfMinSize)(a,{$file:d,$event:e})||-1;if(null!=g&&angular.isString(g)){var j=new RegExp(f(g),"gi");g=null!=d.type&&j.test(d.type.toLowerCase())||null!=d.name&&j.test(d.name.toLowerCase())}return(null==g||g)&&(null==d.size||d.sizei)},ngFileUpload.updateModel=function(a,b,c,d,e,f,g,h,i,j){function k(){if(a(e.ngfKeep)(c)===!0){var j=(d.$modelValue||[]).slice(0);if(g&&g.length)if(a(e.ngfKeepDistinct)(c)===!0){for(var k=j.length,l=0;lm&&g[l].name!==j[m].name;m++);m===k&&j.push(g[l])}g=j}else g=j.concat(g);else g=j}d&&(a(e.ngModel).assign(c,g),b(function(){d&&d.$setViewValue(null!=g&&0===g.length?null:g)})),e.ngModelRejected&&a(e.ngModelRejected).assign(c,h),f&&a(f)(c,{$files:g,$rejectedFiles:h,$event:i})}j?k():b(function(){k()})};var c=ngFileUpload.validate,d=ngFileUpload.updateModel}(),function(){function a(a,e,f,g,h,i,j){function k(a,b,d){var e=!0,f=d.dataTransfer.items;if(null!=f)for(var g=0;g0&&"file"!==j.protocol())for(var q=0;q0)break}else{var t=b.dataTransfer.files;if(null!=t)for(var u=0;u0));u++);}var v=0;!function w(a){i(function(){if(p)10*v++<2e4&&w(10);else{if(!g&&m.length>1){for(q=0;"directory"===m[q].type;)q++;m=[m[q]]}d(m,n)}},a||0)}()}var m=b();if(f.dropAvailable&&i(function(){a[f.dropAvailable]?a[f.dropAvailable].value=m:a[f.dropAvailable]=m}),!m)return void(h(f.ngfHideOnDropNotAvailable)(a)===!0&&e.css("display","none"));var n=!1;-1===f.ngfDrop.search(/\W+$files\W+/)&&a.$watch(f.ngfDrop,function(a){n=a===!1});var o,p=null,q=h(f.ngfStopPropagation),r=1;e[0].addEventListener("dragover",function(b){if(!e.attr("disabled")&&!n){if(b.preventDefault(),q(a)&&b.stopPropagation(),navigator.userAgent.indexOf("Chrome")>-1){var c=b.dataTransfer.effectAllowed;b.dataTransfer.dropEffect="move"===c||"linkMove"===c?"move":"copy"}i.cancel(p),a.actualDragOverClass||(o=k(a,f,b)),e.addClass(o)}},!1),e[0].addEventListener("dragenter",function(b){e.attr("disabled")||n||(b.preventDefault(),q(a)&&b.stopPropagation())},!1),e[0].addEventListener("dragleave",function(){e.attr("disabled")||n||(p=i(function(){e.removeClass(o),o=null},r||1))},!1),e[0].addEventListener("drop",function(b){e.attr("disabled")||n||(b.preventDefault(),q(a)&&b.stopPropagation(),e.removeClass(o),o=null,l(b,function(c,e){d(h,i,a,g,f,f.ngfChange||f.ngfDrop,c,e,b)},h(f.ngfAllowDir)(a)!==!1,f.multiple||h(f.ngfMultiple)(a)))},!1)}function b(){var a=document.createElement("div");return"draggable"in a&&"ondrop"in a}var c=ngFileUpload.validate,d=ngFileUpload.updateModel;ngFileUpload.directive("ngfDrop",["$parse","$timeout","$location",function(b,c,d){return{restrict:"AEC",require:"?ngModel",link:function(e,f,g,h){a(e,f,g,h,b,c,d)}}}]),ngFileUpload.directive("ngfNoFileDrop",function(){return function(a,c){b()&&c.css("display","none")}}),ngFileUpload.directive("ngfDropAvailable",["$parse","$timeout",function(a,c){return function(d,e,f){if(b()){var g=a(f.ngfDropAvailable);c(function(){g(d),g.assign&&g.assign(d,!0)})}}}]),ngFileUpload.directive("ngfSrc",["$parse","$timeout",function(a,b){return{restrict:"AE",link:function(d,e,f){window.FileReader&&d.$watch(f.ngfSrc,function(g){g&&c(d,a,f,g,null)&&(!window.FileAPI||-1===navigator.userAgent.indexOf("MSIE 8")||g.size<2e4)&&(!window.FileAPI||-1===navigator.userAgent.indexOf("MSIE 9")||g.size<4e6)?b(function(){var a=window.URL||window.webkitURL;if(a&&a.createObjectURL)e.attr("src",a.createObjectURL(g));else{var c=new FileReader;c.readAsDataURL(g),c.onload=function(a){b(function(){e.attr("src",a.target.result)})}}}):e.attr("src",f.ngfDefaultSrc||"")})}}}])}(); \ No newline at end of file diff --git a/kb.css b/kb.css index 65f1d51..506f497 100644 --- a/kb.css +++ b/kb.css @@ -287,6 +287,8 @@ span.PETSCII { overflow-y: auto !important; } +.drag-over { border: dotted 3px red !important; box-sizing: border-box; } + /* Only show the keyboard layout itself when printing */ @media print { html, body { overflow:hidden; height:100%; margin: 0 !important; padding: 0 !important; } diff --git a/kb.html b/kb.html index 64b232c..59d1d9c 100644 --- a/kb.html +++ b/kb.html @@ -23,6 +23,7 @@ + @@ -134,7 +135,8 @@ 'ctrl-90' : 'undo()', 'ctrl-shift-90' : 'redo()', 'ctrl-89' : 'redo()' }" - ng-mousedown="selectClick($event)"> + ng-mousedown="selectClick($event)" + ngf-drop="true" ngf-change="uploadJson($files, $event)" ngf-drag-over-class="drag-over">
- +
+ +
- + +
Raw Data Syntax diff --git a/kb.js b/kb.js index 6fb5525..b88f5be 100644 --- a/kb.js +++ b/kb.js @@ -11,7 +11,7 @@ function fromJsonPretty(json) { return $serial.fromJsonL('['+json+']'); } // The angular module for our application - var kbApp = angular.module('kbApp', ["ngSanitize", "ui.utils"]); + var kbApp = angular.module('kbApp', ["ngSanitize", "ui.utils", "ngFileUpload"]); // The main application controller kbApp.controller('kbCtrl', ['$scope','$http','$location','$timeout', '$sce', '$sanitize', function($scope, $http, $location, $timeout, $sce, $sanitize) { @@ -80,7 +80,21 @@ return $scope.dirty; }; $scope.downloadJson = function() { - $serial.downloadJson($serial.serialize($scope.keyboard)); + var data = angular.toJson($serial.serialize($scope.keyboard), true /*pretty*/); + var blob = new Blob([data], {type:"application/json"}); + saveAs(blob, "keyboard-layout.json"); + }; + $scope.uploadJson = function(file, event) { + if(file && file[0]) { + var reader = new FileReader(); + reader.onload = function(event) { + transaction("upload", function() { + $scope.deserializeAndRender($serial.fromJsonL(event.target.result)); + $scope.serializedRaw = '['+$scope.serialized+']'; + }); + }; + reader.readAsText(file[0]); + } }; // Helper function to select a single key diff --git a/serial.js b/serial.js index 65867c9..6f64be5 100644 --- a/serial.js +++ b/serial.js @@ -209,12 +209,4 @@ var $serial = {}; }); }; - $serial.downloadJson = function(layout) { - console.log(layout); - var data = angular.toJson(layout, true /*pretty*/); - console.log(data); - var blob = new Blob([data], {type:"application/json"}); - saveAs(blob, "keyboard-layout.json"); - }; - }());