From 45bc92d6fe94ba33e9f872cc8cac7f9617e339e2 Mon Sep 17 00:00:00 2001 From: Grzegorz Judas Date: Mon, 19 Sep 2016 08:42:58 +0200 Subject: [PATCH] Grzegorz Judas --- js/angular-edrna/Reorder.js | 174 ++++++++++++++ js/angular-edrna/SubConverter.js | 373 +++++++++++++++++++++++++++++++ 2 files changed, 547 insertions(+) create mode 100644 js/angular-edrna/Reorder.js create mode 100644 js/angular-edrna/SubConverter.js diff --git a/js/angular-edrna/Reorder.js b/js/angular-edrna/Reorder.js new file mode 100644 index 0000000..6bf854c --- /dev/null +++ b/js/angular-edrna/Reorder.js @@ -0,0 +1,174 @@ +'use strict'; + +var refreshData; + +angular.module('edrnaApp').directive('reorderWrapper', ['$timeout', '$compile', '$document', + function ($timeout, $compile, $document) { + return { + restrict: 'A', + scope: { + reorderComplete: '=', + reorderData: '=' + }, + link: function ($scope, $element, attrs) { + var data = []; + var scopeTag = attrs.reorderWrapper || ''; + var useHandler = false; + var dragInProgress = false; + var currentPosition = -1; + var draggingElement = undefined; + var dummyElement = undefined; + var objectChanged = false; + var overallDiff = 0; + var originalElementPosition = -1; + + $scope.reorderComplete = typeof $scope.reorderComplete === 'function' ? $scope.reorderComplete : new Function(); + + $scope.$on('draggable:start', function(scope, e) { + var _handler = angular.element(e.event.target); + var _currentElement = angular.element(e.event.target); + var _allElements = $element.find('[reorder-element=\'' + scopeTag + '\']'); + overallDiff = 0; + + /* Set current element */ + if(typeof _currentElement.attr('reorder-element') === 'undefined') { + var _parents = _currentElement.parents('[reorder-element]'); + + if(_parents.length > 0) _currentElement = angular.element(_parents[0]); + else { + $document.off('touchmove mousemove'); + return false; + } + } + draggingElement = _currentElement; + + if(useHandler) { + /* Validate handler click, with propagation possible */ + if(typeof _handler.attr('reorder-handler') === 'undefined') { + var _parents = _handler.parents('[reorder-handler]'); + + if(_parents.length > 0) _handler = angular.element(_parents[0]); + else _handler = undefined; + } + + /* Stop drag if using handler and handler not clicked with LMB */ + if(typeof _handler === 'undefined' || e.event.button === 2) { + $document.off('touchmove mousemove'); + return false; + } + } + + /* Set current position */ + currentPosition = _allElements.index(_currentElement); + originalElementPosition = currentPosition; + + /* When hover over another element */ + _allElements.on('mouseenter.reorder', function(e) { + var _hoveredElement = angular.element(e.currentTarget); + var _allElements = $element.find('[reorder-element=\'' + scopeTag + '\']'); + var hoveredPosition = _allElements.index(_hoveredElement); + + var diff = hoveredPosition - currentPosition; + + if(diff < 0) moveUp(Math.abs(diff), _allElements); + if(diff > 0) moveDown(diff, _allElements); + currentPosition = hoveredPosition; + }); + }); + + $scope.$on('draggable:move', function(scope, e) { + if(!dragInProgress) { + dummyElement = createDummy(draggingElement[0].tagName); + dummyElement.insertBefore(draggingElement); + + dragInProgress = true; + } + }); + + $scope.$on('draggable:end', function(e, d) { + + $element.find('[reorder-element=\'' + scopeTag + '\']').off('mouseenter.reorder'); + if(dummyElement) dummyElement.remove(); + + draggingElement = undefined; + dummyElement = undefined; + dragInProgress = false; + + if(!objectChanged) return; + else objectChanged = false; + + $scope.reorderComplete(angular.copy($scope.reorderData), originalElementPosition, overallDiff); + }); + + var getElements = function(callback) { + if(typeof callback !== 'function') callback = new Function(); + + $timeout(function() { + var obj = $element.find('[reorder-element=\'' + scopeTag + '\']'); + var result = []; + + for(var i = 0; i < obj.length; ++i) { + result.push($scope.reorderData[i]); + } + + useHandler = $element.find('[reorder-handler]').length > 0 ? true : false; + callback(result); + }); + }; + + var moveUp = function(diff, all) { + var newPosition = currentPosition - diff; + var elementAt = (all || $element.find('[reorder-element=\'' + scopeTag + '\']'))[newPosition]; + + dummyElement.insertBefore(elementAt); + draggingElement.insertBefore(elementAt); + moveData(currentPosition, newPosition); + objectChanged = true; + overallDiff--; + }; + + var moveDown = function(diff, all) { + var newPosition = currentPosition + diff; + var elementAt = (all || $element.find('[reorder-element=\'' + scopeTag + '\']'))[newPosition]; + + dummyElement.insertAfter(elementAt); + draggingElement.insertAfter(elementAt); + moveData(currentPosition, newPosition); + objectChanged = true; + overallDiff++; + }; + + var moveData = function(from, to) { + $scope.reorderData.splice(to, 0, $scope.reorderData.splice(from, 1)[0]); + }; + + var createDummy = function(type) { + type = type.toLowerCase(); + + return angular.element('<' + type + '>').addClass('dummy-element'); + }; + + var init = function() { + getElements(function(result) { + data = result; + }); + }; + init(); + } + }; + } +]); + +angular.module('edrnaApp').directive('reorderElementData', ['$timeout', '$compile', '$document', + function () { + return { + restrict: 'A', + scope: { + reorderElementData: '@' + }, + link: function ($scope, $element, attrs) { + $scope.$watch('reorderElementData', refreshData); + } + }; + } +]); diff --git a/js/angular-edrna/SubConverter.js b/js/angular-edrna/SubConverter.js new file mode 100644 index 0000000..2817827 --- /dev/null +++ b/js/angular-edrna/SubConverter.js @@ -0,0 +1,373 @@ +'use strict'; + +(function() { + angular + .module('edrnaApp') + .provider('SubConverter', SubConverterProvider) + .config(SubConverterCfg); + + SubConverterCfg.$inject = ['SubConverterProvider']; + + function SubConverterProvider() { + var srcConverters = {}; + var dstConverters = {}; + + this.addSrcConverter = addSrcConverter; + this.addDstConverter = addDstConverter; + this.$get = ['$q', function ($q) { + return new SubConverter(srcConverters, dstConverters, $q); + }]; + + function addSrcConverter(mime, converter) { + if(typeof mime !== 'string' || typeof converter !== 'function') { + console.error('Converter plug-in is not a function!'); + return false; + } + + srcConverters[mime] = converter; + } + + function addDstConverter(mime, converter) { + if(typeof mime !== 'string' || typeof converter !== 'function') { + console.error('Converter plug-in is not a function!'); + return false; + } + + dstConverters[mime] = converter; + } + } + + function SubConverter(srcConverters, dstConverters, $q) { + var supportedSrc = Object.keys(srcConverters); + var supportedDst = Object.keys(dstConverters); + + return { + newConverter: newConverter, + isSupportedSrc: isSupportedSrc, + isSupportedDst: isSupportedDst + }; + + function newConverter() { + return new converter(); + } + + function isSupportedSrc(mime) { + return supportedSrc.indexOf(mime) !== -1; + } + + function isSupportedDst(mime) { + return supportedDst.indexOf(mime) !== -1; + } + + function converter() { + var self = this; + var source = ''; + var srcMime = ''; + var dstMime = ''; + var destination = ''; + + this.load = load; + this.from = from; + this.to = to; + this.convert = convert; + + function load(data) { + var defer = $q.defer(); + source = srcMime = dstMime = destination = ''; + + if(typeof data === 'string') { + source = data; + defer.resolve(); + } + + if(data instanceof Blob) { + var fr = new FileReader(); + fr.onload = function(e) { + source = e.target.result; + srcMime = fr.type; + defer.resolve(); + }; + fr.readAsText(data); + } + + return defer.promise; + } + + function from(mime) { + if(!isSupportedSrc(mime)) { + console.error('Source subtitle format not supported: ' + mime); + return false; + } + + srcMime = mime; + return self; + } + + function to(mime) { + if(!isSupportedDst(mime)) { + console.error('Destination subtitle format not supported: ' + mime); + return false; + } + + dstMime = mime; + return self; + } + + function convert() { + var parsedData = srcConverters[srcMime](source); + var converted = dstConverters[dstMime](parsedData); + + return converted; + } + } + } + + function SubConverterCfg(SubConverterProvider) { + SubConverterProvider.addSrcConverter('application/x-subrip', function(data) { + var lines = []; + var index = 0; + var previousLine; + var currentLine = {}; + + var addToStack = function() { + lines[currentLine.number] = { + start: currentLine.start, + end: currentLine.end, + data: currentLine.data + }; + currentLine = {}; + } + + data.split('\n').forEach(function(dataLine, dataIndex) { + /* Empty space */ + if(!/\S/.test(dataLine)) { + addToStack(); + + previousLine = dataLine; + return; + } + + /* Index */ + if((!/\S/.test(previousLine) || !previousLine) && parseInt(dataLine, 10) !== null) { + currentLine.number = parseInt(dataLine, 10); + + previousLine = dataLine; + return; + } + + /* Timing */ + if(dataLine.indexOf('-->') !== -1 && parseInt(previousLine, 10) !== null) { + var timings = dataLine.split(' --> '); + + currentLine.start = { + h: parseInt(timings[0].split(':')[0], 10), + m: parseInt(timings[0].split(':')[1], 10), + s: parseInt(timings[0].split(':')[2], 10), + mili: parseInt(timings[0].split(',')[1], 10) + }; + currentLine.end = { + h: parseInt(timings[1].split(':')[0], 10), + m: parseInt(timings[1].split(':')[1], 10), + s: parseInt(timings[1].split(':')[2], 10), + mili: parseInt(timings[1].split(',')[1], 10) + }; + + previousLine = dataLine; + return; + } + + /* Subtitle itself */ + currentLine.data = Array.isArray(currentLine.data) ? currentLine.data : []; + + dataLine = dataLine.replace(//g, '{\\b1}').replace(/<\/b>/g, '{\\b0}'); + dataLine = dataLine.replace(//g, '{\\i1}').replace(/<\/i>/g, '{\\i0}'); + dataLine = dataLine.replace(//g, '{\\u1}').replace(/<\/u>/g, '{\\u0}'); + + currentLine.data.push(dataLine); + previousLine = dataLine; + + /* Last dialogue */ + if(dataIndex === data.split('\n').length - 1) { + addToStack(); + } + }); + + lines = lines.filter(function(l) { return true; }); + + return lines; + }); + + SubConverterProvider.addSrcConverter('text/vtt', function(data) { + var lines = []; + var index = 0; + var previousLine; + var currentLine = {}; + + var addToStack = function() { + lines[currentLine.number] = { + start: currentLine.start, + end: currentLine.end, + data: currentLine.data + }; + currentLine = {}; + } + + if(!/WEBVTT FILE/.test(data.split('\n')[0])) return []; + + data.split('\n').forEach(function(dataLine, dataIndex) { + /* Empty space */ + if(!/\S/.test(dataLine)) { + addToStack(); + + previousLine = dataLine; + return; + } + + /* Index */ + if((!/\S/.test(previousLine) || !previousLine) && parseInt(dataLine, 10) !== null) { + currentLine.number = parseInt(dataLine, 10); + + previousLine = dataLine; + return; + } + + /* Timing */ + if(dataLine.indexOf('-->') !== -1 && parseInt(previousLine, 10) !== null) { + var timings = dataLine.split(' --> '); + var cue = timings[1].split(' '); + timings[1] = cue[0]; + cue.shift(); + + currentLine.start = { + h: parseInt(timings[0].split(':')[0], 10), + m: parseInt(timings[0].split(':')[1], 10), + s: parseInt(timings[0].split(':')[2], 10), + mili: parseInt(timings[0].split(',')[1], 10) + }; + currentLine.end = { + h: parseInt(timings[1].split(':')[0], 10), + m: parseInt(timings[1].split(':')[1], 10), + s: parseInt(timings[1].split(':')[2], 10), + mili: parseInt(timings[1].split(',')[1], 10) + }; + + previousLine = dataLine; + return; + } + + /* Subtitle itself */ + currentLine.data = Array.isArray(currentLine.data) ? currentLine.data : []; + + dataLine = dataLine.replace(//g, '{\\b1}').replace(/<\/b>/g, '{\\b0}'); + dataLine = dataLine.replace(//g, '{\\i1}').replace(/<\/i>/g, '{\\i0}'); + dataLine = dataLine.replace(//g, '{\\u1}').replace(/<\/u>/g, '{\\u0}'); + dataLine = dataLine.replace(//g, '{\\1c&HCCFFFF&}').replace(/<\/c>/g, '{\\1c}'); + + currentLine.data.push(dataLine); + previousLine = dataLine; + + /* Last dialogue */ + if(dataIndex === data.split('\n').length - 1) { + addToStack(); + } + }); + + lines = lines.filter(function(l) { return true; }); + + return lines; + }); + + SubConverterProvider.addSrcConverter('text/xml', function(data) { + data = data.substring(data.indexOf(''), data.indexOf('')); + data = data.replace('', ''); + + var parsed = []; + var lines = data.split(''); + var tagRegex = /^/; + // lines = lines.filter(function(line) { return line.match(tagRegex) !== null; }); + + lines.forEach(function(line) { + var matched = line.match(tagRegex); + + if(matched === null) return; + + var start = parseFloat(matched[1], 10) * 1000; + var end = parseFloat(matched[4], 10) * 1000; + var text = line.replace(tagRegex, ''); + var el = angular.element(document.createElement('textarea')); + var epoch = new Date(0); + epoch.setHours(0); + start = new Date(epoch.getTime() + start); + end = new Date(start.getTime() + end); + text = text.replace(/\&/g, '&'); + text = el.html(text).text(); + + parsed.push({ + start: { + h: start.getHours(), + m: start.getMinutes(), + s: start.getSeconds(), + mili: start.getMilliseconds() + }, + end: { + h: end.getHours(), + m: end.getMinutes(), + s: end.getSeconds(), + mili: end.getMilliseconds() + }, + data: text.split('\n') + }); + }); + + + return parsed; + }); + + SubConverterProvider.addDstConverter('text/x-ssa', function(data) { + var result = ''; + result += '[Script Info]\n'; + result += '; Script generated by Junction\n'; + result += '; http://www.junctioneducation.com/\n'; + result += 'ScriptType: v4.00+\n'; + result += 'WrapStyle: 0\n'; + result += 'PlayResX: 640\n'; + result += 'PlayResY: 480\n'; + result += 'ScaledBorderAndShadow: yes\n'; + result += 'VideoAspectRatio: 0\n'; + result += 'VideoZoom: 6\n'; + result += 'VideoPosition: 0\n'; + result += 'Last Style Storage: Default\n'; + result += '\n'; + result += '[V4+ Styles]\n'; + result += 'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n'; + result += 'Style: Default,Arial,25,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,3,1,1,2,10,10,30,1\n'; + result += '\n'; + result += '[Events]\n'; + result += 'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n'; + + function toFixed(number, len) { + while((number + '').length > len) { + number = (number + '').substring(0, (number + '').length-1); + } + + while((number + '').length < len) { + number = '0' + number; + } + + return number; + } + + data.forEach(function(line) { + result += 'Dialogue: '; + result += '0,'; + result += toFixed(line.start.h, 1) + ':' + toFixed(line.start.m, 2) + ':' + toFixed(line.start.s, 2) + '.' + toFixed(line.start.mili, 2) + ','; + result += toFixed(line.end.h, 1) + ':' + toFixed(line.end.m, 2) + ':' + toFixed(line.end.s, 2) + '.' + toFixed(line.end.mili, 2) + ','; + result += 'Default,,0,0,0,,'; + result += line.data.join('\\N'); + result += '\n'; + }); + + return result; + }); + } +})();