pip-webui
Version:
HTML5 UI for LOB applications
696 lines (594 loc) • 26.3 kB
JavaScript
/**
* @file Registration of WebUI documents controls
* @copyright Digital Living Software Corp. 2014-2016
*/
(function (angular) {
'use strict';
angular.module('pipDocuments', [
'pipDocumentList',
'pipDocumentListEdit'
]);
})(window.angular);
(function(module) {
try {
module = angular.module('pipDocuments.Templates');
} catch (e) {
module = angular.module('pipDocuments.Templates', []);
}
module.run(['$templateCache', function($templateCache) {
$templateCache.put('document_list/document_list.html',
'<!--\n' +
'@file Document list control content\n' +
'@copyright Digital Living Software Corp. 2014-2016\n' +
'-->\n' +
'\n' +
'<md-button class="pip-documents-name"\n' +
' ng-class="{\'lp24-flex rp16\': pipDocumentIcon}"\n' +
' ng-click="onTitleClick($event); onResize()"\n' +
' aria-label="RESIZE">\n' +
'\n' +
' <div class="layout-align-start-center layout-row w-stretch">\n' +
' <md-icon md-svg-icon="icons:document" ng-class="{\'pip-icon\': pipDocumentIcon}" ng-if="pipDocumentIcon"></md-icon>\n' +
' <span class="pip-documents-text">\n' +
' {{documents.length}} {{::\'DOCUMENTS_ATTACHED\' | translate}}\n' +
' </span>\n' +
'\n' +
' <md-icon class="icon-up" md-svg-icon="icons:triangle-up"></md-icon>\n' +
' <md-icon class="icon-down" md-svg-icon="icons:triangle-down"></md-icon>\n' +
' </div>\n' +
'</md-button>\n' +
'<div pip-focused class="pip-documents-container bm8" ng-class="{\'lp24-flex rp24-flex\': pipDocumentIcon}">\n' +
' <md-button class="pip-document-download md-primary"\n' +
' ng-class="{\'pip-focusable\' : !ngDisabled()}"\n' +
' href="{{::documentUrl(document)}}"\n' +
' target="_blank"\n' +
' ng-disabled="ngDisabled() || document.error"\n' +
' ng-repeat="document in documents track by document.file_id"\n' +
' aria-label="DOCUMENT">\n' +
'\n' +
' <div class="pip-default-icon">\n' +
' <md-icon md-svg-icon="icons:{{::documentList.icon}}"></md-icon>\n' +
' </div>\n' +
' <div class="pip-document-title">\n' +
' {{::document.file_name}}\n' +
' </div>\n' +
' </md-button>\n' +
'</div>');
}]);
})();
(function(module) {
try {
module = angular.module('pipDocuments.Templates');
} catch (e) {
module = angular.module('pipDocuments.Templates', []);
}
module.run(['$templateCache', function($templateCache) {
$templateCache.put('document_list/document_list_collapse.html',
'<!--\n' +
'@file Document list control content (collapsable version)\n' +
'@copyright Digital Living Software Corp. 2014-2016\n' +
'-->\n' +
'\n' +
'<div class="pip-documents-name" ng-click="onTitleClick($event); onResize()">\n' +
' <span class="pip-documents-text">\n' +
' {{documents.length}} {{::\'DOCUMENTS_ATTACHED\' | translate}}\n' +
' </span>\n' +
'\n' +
' <md-icon class="icon-up" md-svg-icon="icons:triangle-up"></md-icon>\n' +
' <md-icon class="icon-down" md-svg-icon="icons:triangle-down"></md-icon>\n' +
'</div>\n' +
'<div pip-focused class="pip-documents-container bm8">\n' +
' <md-button class="pip-document-download pip-focusable md-primary"\n' +
' href="{{::document.url}}"\n' +
' target="_blank"\n' +
' ng-repeat="document in documents track by document.file_id"\n' +
' aria-label="DOCUMENT">\n' +
' <div class="pip-default-icon">\n' +
' <md-icon md-svg-icon="icons:{{::icon}}"></md-icon>\n' +
' </div>\n' +
' <div class="pip-document-title">\n' +
' {{::document.file_name}}\n' +
' </div>\n' +
' </md-button>\n' +
'</div>\n' +
'');
}]);
})();
(function(module) {
try {
module = angular.module('pipDocuments.Templates');
} catch (e) {
module = angular.module('pipDocuments.Templates', []);
}
module.run(['$templateCache', function($templateCache) {
$templateCache.put('document_list_edit/document_list_edit.html',
'<!--\n' +
'@file Document list edit control content\n' +
'@copyright Digital Living Software Corp. 2014-2016\n' +
'-->\n' +
'<div pip-focusable>\n' +
' <div class="pip-document-upload pointer md-primary "\n' +
' ng-class="{\'pip-focusable\' : !ngDisabled(), \'pip-item-error\' : item.state == \'error\'}"\n' +
' ng-keydown="onKeyDown($event, item)"\n' +
' tabindex="{{ ngDisabled() ? -1 : 0 }}"\n' +
' ng-repeat="item in control.items | filter: filterItem">\n' +
'\n' +
' <div class="pip-default-icon"\n' +
' ng-class="{ \'pip-document-new\': item.id == null }">\n' +
' <md-icon pip-cancel-drag="true" class="md-primary" ng-if="item.state == \'original\' || item.state == \'added\'"\n' +
' md-svg-icon="icons:{{::documentList.icon}}">\n' +
' </md-icon>\n' +
' <md-icon pip-cancel-drag="true" class="md-warn" ng-if="item.state == \'error\'"\n' +
' md-svg-icon="icons:{{::documentList.iconError}}">\n' +
' </md-icon>\n' +
' </div>\n' +
'\n' +
' <div class="pip-document-title" pip-cancel-drag="true">\n' +
' {{::item.name}}\n' +
' </div>\n' +
' <md-button ng-click="onDelete(item)"\n' +
' ng-disabled="ngDisabled() || control.uploading"\n' +
' tabindex="-1"\n' +
' ng-hide="ngDisabled()"\n' +
' class="md-icon-button" aria-label="DELETE">\n' +
'\n' +
' <md-icon md-svg-icon="icons:cross" pip-cancel-drag="true"></md-icon>\n' +
' </md-button>\n' +
' <md-progress-linear ng-show="item.uploading" ng-value="item.progress"></md-progress-linear>\n' +
' </div>\n' +
' \n' +
' <button class="pip-document-upload pip-document-drop "\n' +
' ng-class="{\'pip-focusable\' : !ngDisabled()}"\n' +
' ng-keydown="onKeyDown($event)" tabindex="0"\n' +
' ng-file-drop ng-file-select ng-file-change="onSelect($files)"\n' +
' ng-multiple="true"\n' +
' ng-disabled="ngDisabled() || control.uploading"\n' +
' aria-label="UPLOAD">\n' +
'\n' +
' <div class="pip-default-icon">\n' +
' <md-icon pip-cancel-drag="true" md-svg-icon="icons:{{::documentList.icon}}"></md-icon>\n' +
' </div>\n' +
' <div class="pip-default-text">\n' +
' <span>\n' +
' {{documentList.text | translate}}\n' +
' </span>\n' +
' </div>\n' +
' </button>\n' +
' <div class="clearfix"></div>\n' +
'</div>\n' +
'');
}]);
})();
/**
* @file Document list edit control
* @copyright Digital Living Software Corp. 2014-2016
* @todo
* - Improve samples in sampler app
* - Add add/remove/hover animations
*/
(function (angular, _) {
'use strict';
var thisModule = angular.module('pipDocumentListEdit',
['ui.event', 'angularFileUpload', 'pipCore', 'pipFocused', 'pipRest', 'pipDocuments.Templates']);
thisModule.config(['pipTranslateProvider', function (pipTranslateProvider) {
pipTranslateProvider.translations('en', {
DOCUMENT_LIST_EDIT_TEXT: 'Click here to add a document',
ERROR_TRANSACTION_IN_PROGRESS: 'Transaction is in progress. Please, wait until it\'s finished or abort'
});
pipTranslateProvider.translations('ru', {
DOCUMENT_LIST_EDIT_TEXT: 'Нажмите сюда, чтобы добавить документ',
ERROR_TRANSACTION_IN_PROGRESS: 'Транзакция еще не завершена. Подождите окончания или прервите её'
});
}]);
thisModule.directive('pipDocumentListEdit',
function () {
return {
restrict: 'EA',
scope: {
pipDocuments: '=',
pipAddedDocument: '=',
ngDisabled: '&',
pipCreated: '&',
pipChanged: '&'
},
templateUrl: 'document_list_edit/document_list_edit.html',
controller: 'pipDocumentListEditController'
};
}
);
thisModule.controller('pipDocumentListEditController',
['$scope', '$rootScope', '$element', '$attrs', '$parse', '$http', '$upload', '$timeout', 'pipRest', 'pipUtils', function ($scope, $rootScope, $element, $attrs, $parse, $http, $upload, $timeout, pipRest, pipUtils) {
var
$control = $element.children('.pip-picture-drop'),
itemPin = 0;
$scope.documentList = {};
$scope.documentList.text = $attrs.pipDefaultText || 'DOCUMENT_LIST_EDIT_TEXT';
$scope.documentList.icon = $attrs.pipDefaultIcon || 'document';
$scope.documentList.iconError = 'warn-circle';
$scope.documentStartState = pipUtils.toBoolean($scope.pipAddedDocument) ? 'copied' : 'original';
$scope.cancelDrag = pipUtils.toBoolean($attrs.pipCanselDrag) === true;
$scope.control = {
uploading: 0,
items: getItems(),
reset: resetDocument,
save: saveDocument,
abort: onAbort
};
$scope.filterItem = filterItem;
$scope.onDelete = onDelete;
$scope.onKeyDown = onKeyDown;
$scope.onChange = onChange;
$scope.onSelect = onSelect;
// Add class
$element.addClass('pip-document-list-edit');
// Initialize control
$scope.control.reset();
executeCallback();
// Watch for changes
$scope.$watchCollection(
function () {
// Todo: Optimize change tracking
return $scope.pipDocuments;
},
function (newValue) {
if (!_.isEqual(newValue, $scope.pipDocuments)) {
$scope.control.reset();
}
}
);
function getItems() {
var
documents = $scope.pipDocuments,
items = [],
i;
if (documents === null || documents.length === 0) {
return items;
}
for (i = 0; i < documents.length; i++) {
items.push({
pin: itemPin++,
id: documents[i].file_id,
name: documents[i].file_name,
uploading: false,
uploaded: false,
progress: 0,
file: null,
state: $scope.documentStartState // 'original'
});
}
return items;
}
function setItems() {
var item, i;
// Clean the array
if ($scope.pipDocuments && $scope.pipDocuments.length > 0) {
$scope.pipDocuments.splice(0, $scope.pipDocuments.length);
}
for (i = 0; i < $scope.control.items.length; i++) {
item = $scope.control.items[i];
if (item.id) {
$scope.pipDocuments.push({
file_id: item.id,
file_name: item.name
});
}
}
}
function resetDocument() {
$scope.control.uploading = 0;
$scope.control.items = getItems();
}
function getItemIdUrl(item) {
var
serverUrl = pipRest.serverUrl(),
partyId = $rootScope.$party ? $rootScope.$party.id : pipRest.userId();
return serverUrl + '/api/parties/' + partyId + '/files/' + item.id;
}
function addItemUrl(item) {
var
serverUrl = pipRest.serverUrl(),
partyId = $rootScope.$party ? $rootScope.$party.id : pipRest.userId();
return serverUrl + '/api/parties/' + partyId + '/files?name=' + item.file.name;
}
function addItem(item, callback) {
var
file = item.file,
fileReader = new FileReader();
// Avoid double transactions
if (item.uploading || item.file === null || item.state !== 'added') { return; }
fileReader.onload = function (e) {
if (item.uploading) { return; }
item.uploading = true;
item.upload = $upload.http({
url: addItemUrl(item),
headers: { 'Content-Type': file.type },
data: e.target.result
})
.then(
function (response) {
item.id = response.data.id;
item.name = response.data.filename || item.name;
item.uploaded = true;
item.uploading = false;
item.progress = 0;
item.upload = null;
item.file = null;
item.state = 'original';
callback();
},
function () {
item.uploaded = false;
item.uploading = false;
item.progress = 0;
item.upload = null;
item.state = 'error';
callback();
},
function (e) {
// Math.min is to fix IE which reports 200% sometimes
item.progress = Math.min(100, parseInt(100.0 * e.loaded / e.total));
}
);
};
fileReader.readAsArrayBuffer(file);
}
function deleteItem(item, callback) {
var control = $scope.control;
// Avoid double transactions
if (item.upload) {
item.upload.abort();
item.upload = null;
}
if (item.state !== 'deleted') { return; }
$http['delete'](getItemIdUrl(item))
.success(function () {
_.remove(control.items, {pin: item.pin});
callback();
})
.error(function (data) {
// Todo: perform a better processing
if (data == null) {
_.remove(control.items, {pin: item.pin});
} else {
item.uploaded = false;
item.uploading = false;
item.progress = 0;
item.upload = null;
}
callback(data);
});
}
function saveDocument(successCallback, errorCallback) {
var control = $scope.control,
onItemCallback,
item,
i;
if (control.uploading) {
if (errorCallback) {
errorCallback('ERROR_TRANSACTION_IN_PROGRESS');
}
return;
}
control.error = null;
control.uploading = 0;
onItemCallback = function (error) {
// Storing only the first error
if (error && !control.error) {
control.error = error;
}
control.uploading--;
// Finished uploading
if (control.uploading == 0) {
if (control.error) {
if (errorCallback) {
errorCallback(control.error);
} else {
console.error(control.error); // eslint-disable-line no-console
}
} else {
setItems();
if (successCallback) {
successCallback();
}
}
}
};
for (i = 0; i < control.items.length; i++) {
item = control.items[i];
if (item.state === 'added') {
control.uploading++;
addItem(item, onItemCallback);
} else if (item.state === 'deleted') {
control.uploading++;
deleteItem(item, onItemCallback);
}
}
// Nothing was uploaded
if (control.uploading == 0) {
if (successCallback) {
successCallback();
}
}
}
function onAbort() {
var control = $scope.control,
item,
i;
for (i = 0; i < control.items.length; i++) {
item = control.items[i];
if (item.uploading) {
if (item.upload) {
item.upload.abort();
}
item.uploaded = false;
item.uploading = false;
item.progress = 0;
item.upload = null;
}
}
// Abort transaction
control.uploading = 0;
}
// Visualization functions
function filterItem(item) {
return item.state !== 'deleted';
}
// Process user actions
function onSelect($files) {
var file,
i;
$control.focus();
if ($files == null || $files.length === 0) { return; }
for (i = 0; i < $files.length; i++) {
file = $files[i];
$scope.control.items.push({
pin: itemPin++,
id: null,
name: file.name,
uploading: false,
uploaded: false,
progress: 0,
file: file,
state: 'added'
});
}
$scope.onChange();
}
function onDelete(item) {
if (item.state === 'added' || item.state === 'copied') {
_.remove($scope.control.items, {pin: item.pin});
} else {
item.state = 'deleted';
}
$scope.onChange();
}
function onKeyDown($event, item) {
if (item) {
if ($event.keyCode === 46 || $event.keyCode === 8) {
if (item.state === 'added') {
_.remove($scope.control.items, { pin: item.pin });
} else {
item.state = 'deleted';
}
$scope.onChange();
}
} else if ($event.keyCode === 13 || $event.keyCode === 32) {
// !! Avoid clash with $apply()
setTimeout(function () {
$control.trigger('click');
}, 0);
}
}
// On change event
function onChange() {
if ($scope.pipChanged) {
$scope.pipChanged({
$event: { sender: $scope.control },
$control: $scope.control
});
}
}
function executeCallback() {
// Execute callback
if ($scope.pipCreated) {
$scope.pipCreated({
$event: { sender: $scope.control },
$control: $scope.control
});
}
}
}]
);
})(window.angular, window._);
/**
* @file Document list control
* @copyright Digital Living Software Corp. 2014-2016
* @todo
* - Improve samples in sampler app
*/
(function (angular, _) {
'use strict';
var thisModule = angular.module('pipDocumentList', ['pipCore', 'pipRest', 'pipFocused', 'pipDocuments.Templates']);
thisModule.config(['pipTranslateProvider', function (pipTranslateProvider) {
pipTranslateProvider.translations('en', {
DOCUMENTS_ATTACHED: 'document(s) attached',
ERROR_DOCUMENTS_LOADED: 'Error: <%= error_number %> document(s) are not loaded'
});
pipTranslateProvider.translations('ru', {
DOCUMENTS_ATTACHED: 'документов добавлено',
ERROR_DOCUMENTS_LOADED: 'Ошибка: <%= error_number %> документ(ов) не загружено'
});
}]);
thisModule.directive('pipDocumentList',
['$parse', '$rootScope', 'pipUtils', 'pipRest', 'pipToasts', 'pipTranslate', function ($parse, $rootScope, pipUtils, pipRest, pipToasts, pipTranslate) { // eslint-disable-line no-unused-vars
return {
restrict: 'EA',
scope: true,
templateUrl: 'document_list/document_list.html',
link: function ($scope, $element, $attrs) {
var documentsGetter = $parse($attrs.pipDocuments),
$documentsContainer = $element.children('.pip-documents-container'),
$up = $element.find('.icon-up'),
$down = $element.find('.icon-down'),
collapsable = pipUtils.toBoolean($attrs.pipCollapse);
$scope.documentList = {};
$scope.documentList.icon = 'document';
$scope.documents = documentsGetter($scope);
if ($attrs.pipDocumentIcon) {
$scope.pipDocumentIcon = true;
}
$scope.showDocuments = collapsable;
if (!collapsable) {
$up.hide();
$documentsContainer.hide();
} else {
$down.hide();
}
if ($attrs.disabled) {
$up.hide();
$down.hide();
}
// Also optimization to avoid watch if it is unnecessary
if (pipUtils.toBoolean($attrs.pipRebind)) {
$scope.$watch(documentsGetter, function (newValue) {
if (differentDocumentList(newValue)) {
$scope.documents = newValue;
}
});
}
$scope.onTitleClick = onTitleClick;
$scope.documentUrl = documentUrl;
// Add class
$element.addClass('pip-document-list');
function differentDocumentList(newList) {
var i, obj;
if (!$scope.documents && newList) { return true; }
if ($scope.documents && !newList) { return true; }
if ($scope.documents.length !== newList.length) { return true; }
for (i = 0; i < newList.length; i++) {
obj = _.find($scope.documents, {file_id: newList[i].file_id});
if (obj === undefined) { return true; }
}
return false;
}
function onTitleClick(event) {
if (event) { event.stopPropagation(); }
if ($attrs.disabled) { return; }
$scope.showDocuments = !$scope.showDocuments;
$up[$scope.showDocuments ? 'show' : 'hide']();
$down[!$scope.showDocuments ? 'show' : 'hide']();
$documentsContainer[$scope.showDocuments ? 'show' : 'hide']();
}
function documentUrl(document) {
var
serverUrl = pipRest.serverUrl(),
userId = ($rootScope.$user || {}).id,
partyId = ($rootScope.$party || {}).id || userId;
return serverUrl + '/api/parties/' + partyId + '/files/' + document.file_id + '/content';
}
}
};
}]
);
})(window.angular, window._);
//# sourceMappingURL=pip-webui-documents.js.map