UNPKG

pip-webui

Version:

HTML5 UI for LOB applications

1,224 lines (1,065 loc) 152 kB
/** * @file Registration of pictures WebUI controls * @copyright Digital Living Software Corp. 2014-2015 */ /* global angular */ (function () { 'use strict'; angular.module('pipPictures', [ 'pipAddImage', 'pipAvatar', 'pipAvatarEdit', 'pipPicture', 'pipPictureEdit', 'pipCollage', 'pipPictureListEdit', 'pipCameraDialog', 'pipPictureUrlDialog' ]); })(); (function(module) { try { module = angular.module('pipPictures.Templates'); } catch (e) { module = angular.module('pipPictures.Templates', []); } module.run(['$templateCache', function($templateCache) { $templateCache.put('add_image/add_image.html', ' <!--\n' + '@file Add image directive content\n' + '@copyright Digital Living Software Corp. 2014-2015\n' + '-->\n' + '\n' + '<md-menu>\n' + ' <ng-transclude class="pip-add-image-open-button" ng-click="ngDisabled() ? \'\' : $mdOpenMenu()"></ng-transclude>\n' + ' <md-menu-content width="4">\n' + ' <md-menu-item>\n' + ' <md-button class="layout-row layout-align-start-center" accept="image/*"\n' + ' ng-keydown="onKeyDown($event)" ng-multiple="isMulti()"\n' + ' ng-file-select ng-file-change="onFileChange($files)" ng-click="hideMenu()" ng-file-drop>\n' + '\n' + ' <md-icon class="text-headline text-grey rm24-flex" md-svg-icon="icons:folder"></md-icon>\n' + ' <!--<span class="icon-folder text-headline text-grey tp8 rm24-flex"></span>-->\n' + ' <span class="text-grey">{{::\'FILE\' | translate}}</span>\n' + ' </md-button>\n' + ' </md-menu-item>\n' + ' <md-menu-item>\n' + ' <md-button class="layout-row layout-align-start-center" ng-click="onWebLinkClick()">\n' + ' <md-icon class="text-headline text-grey rm24-flex" md-svg-icon="icons:weblink"></md-icon>\n' + ' <!--<span class="icon-weblink text-headline text-grey tp8 rm24-flex"></span>-->\n' + ' <span class="text-grey">{{::\'WEB_LINK\' | translate}}</span>\n' + ' </md-button>\n' + ' </md-menu-item>\n' + ' <md-menu-item>\n' + ' <md-button class="layout-row layout-align-start-center" ng-click="onCameraClick()">\n' + ' <md-icon class="text-headline text-grey rm24-flex" md-svg-icon="icons:camera"></md-icon>\n' + ' <!--<span class="icon-camera text-headline text-grey tp8 rm24-flex"></span>-->\n' + ' <span class="text-grey">{{::\'CAMERA\' | translate}}</span>\n' + ' </md-button>\n' + ' </md-menu-item>\n' + ' <md-menu-item>\n' + ' <md-button class="layout-row layout-align-start-center" ng-click="onGalleryClick()">\n' + ' <md-icon class="text-headline text-grey rm24-flex" md-svg-icon="icons:images"></md-icon>\n' + ' <!--<span class="icon-images text-headline text-grey tp8 rm24-flex"></span>-->\n' + ' <span class="text-grey">{{::\'IMAGE_GALLERY\' | translate}}</span>\n' + ' </md-button>\n' + ' </md-menu-item>\n' + ' </md-menu-content>\n' + ' </md-menu>'); }]); })(); (function(module) { try { module = angular.module('pipPictures.Templates'); } catch (e) { module = angular.module('pipPictures.Templates', []); } module.run(['$templateCache', function($templateCache) { $templateCache.put('camera_dialog/camera_dialog.html', '<!--\n' + '@file Camera dialog content\n' + '@copyright Digital Living Software Corp. 2014-2015\n' + '-->\n' + '\n' + '<md-dialog class="pip-dialog pip-picture-dialog pip-camera-dialog layout-column" md-theme="{{theme}}"\n' + ' ng-show="browser != \'android\'"\n' + ' ng-class="{\'pip-android-dialog\': browser == \'android\' || !browser}">\n' + ' <div class="pip-header" class="layout-row layout-align-start-center">\n' + ' <md-button ng-click="onCancelClick()" class="md-icon-button"\n' + ' aria-label="{{ ::\'CANCEL\' | translate }}">\n' + ' <md-icon class="text-grey" md-svg-icon="icons:arrow-left"></md-icon>\n' + ' </md-button>\n' + ' <h3 class="m0 text-title">{{ ::\'TAKE_PICTURE\' | translate }}</h3>\n' + ' </div>\n' + '\n' + ' <div class="pip-body">\n' + ' <div class="camera-stream" ng-hide="webCamError || browser == \'android\'"></div>\n' + ' <div class="camera-error"\n' + ' ng-show="webCamError || browser == \'android\'"\n' + ' class="layout-row layout-align-center-center">\n' + ' <span>{{ ::\'WEB_CAM_ERROR\' | translate }}</span>\n' + ' </div>\n' + ' </div>\n' + '\n' + ' <div class="pip-footer">\n' + ' <div class="w48">\n' + ' <md-button ng-click="onResetPicture()"\n' + ' ng-hide="!$freeze || webCamError"\n' + ' class="md-icon-button"\n' + ' ng-disabled="transaction.busy()"\n' + ' aria-label="{{ ::\'REMOVE_PICTURE\' | translate }}">\n' + ' <md-icon class="text-grey" md-svg-icon="icons:refresh"></md-icon>\n' + ' </md-button>\n' + ' </div>\n' + ' <div class="flex"></div>\n' + ' <div class="w48">\n' + ' <md-button ng-click="onTakePictureClick()"\n' + ' ng-hide="webCamError"\n' + ' class="md-icon-button"\n' + ' aria-label="{{ ::\'TAKE_PICTURE\' | translate }}">\n' + ' <md-icon class="text-grey icon-button" md-svg-icon="icons:{{$freeze ? \'check\' : \'camera\'}}"></md-icon>\n' + ' </md-button>\n' + '\n' + ' </div>\n' + ' <div class="flex"></div>\n' + ' <div class="w48">\n' + ' <md-button ng-click="onCancelClick()" class="md-icon-button"\n' + ' aria-label="{{ ::\'CANCEL\' | translate }}">\n' + ' <md-icon class="text-grey" md-svg-icon="icons:cross"></md-icon>\n' + ' </md-button>\n' + ' </div>\n' + ' </div>\n' + '\n' + '</md-dialog>'); }]); })(); (function(module) { try { module = angular.module('pipPictures.Templates'); } catch (e) { module = angular.module('pipPictures.Templates', []); } module.run(['$templateCache', function($templateCache) { $templateCache.put('gallery_search_dialog/gallery_search_dialog.html', '<md-dialog class="pip-dialog pip-gallery-search-dialog pip-picture-dialog layout-column"\n' + ' md-theme="{{theme}}">\n' + ' <md-progress-linear\n' + ' ng-show="transaction.busy()" md-mode="indeterminate">\n' + ' </md-progress-linear>\n' + '\n' + ' <md-dialog-content class="pip-body lp0 rp0 tp0 pip-scroll flex layout-row">\n' + ' <div class="layout-column layout-align-start-start flex">\n' + ' <div class="pip-header w-stretch layout-column layout-align-start-start">\n' + ' <h3 class="w-stretch text-title m0 bp8">\n' + ' <md-button class="md-icon-button m0"\n' + ' ng-click="onCancelClick()"\n' + ' aria-label="{{ ::\'CANCEL\' | translate }}">\n' + ' <md-icon class="text-grey" md-svg-icon="icons:arrow-left"></md-icon>\n' + ' </md-button>\n' + ' {{::\'IMAGE_GALLERY\' | translate}}\n' + ' </h3>\n' + ' <div class="w-stretch divider-bottom layout-row layout-start-center">\n' + ' <input class="no-divider rm8 text-subhead1 flex"\n' + ' ng-disabled="transaction.busy()"\n' + ' ng-model="$search" ng-keypress="onKeyPress($event)"\n' + ' placeholder="{{::\'SEARCH_PICTURES\' | translate}}"\n' + ' type="text" tabindex="1">\n' + '\n' + ' <md-button class="md-icon-button md-icon-button-square p0 pip-search-button md-primary"\n' + ' ng-click="onSearchClick()"\n' + ' ng-hide="optionDefault"\n' + ' tabindex="-1" aria-label="SEARCH">\n' + ' <md-icon class="text-opacity md-primary" md-svg-icon="icons:search-square "></md-icon>\n' + ' </md-button>\n' + ' </div>\n' + ' </div>\n' + ' <div class="pip-content flex"\n' + ' ng-show="$images.length > 0">\n' + ' <div class="pip-image-container"\n' + ' ng-click="onImageClick(image)"\n' + ' ng-repeat="image in $images track by $index"\n' + ' ng-class="{\'checked\': image.checked}"\n' + ' tabindex="{{ $index + 2 }}">\n' + '\n' + ' <pip-picture pip-src="image.thumbnail"\n' + ' pip-default-icon="icon-images"\n' + ' pip-rebind="true">\n' + ' </pip-picture>\n' + ' <div class="pip-checked-cover"></div>\n' + ' <div class="pip-checkbox-backdrop">\n' + ' <md-checkbox md-no-ink\n' + ' ng-model="image.checked"\n' + ' ng-click="image.checked = !image.checked"\n' + ' aria-label="CHECKED">\n' + ' </md-checkbox>\n' + ' </div>\n' + ' </div>\n' + ' </div>\n' + ' <div class="pip-no-images w-stretch layout-column layout-align-center-center flex"\n' + ' ng-show="$images.length == 0">\n' + ' <img src="images/add_from_image_library.svg" width="200" height="200">\n' + ' <p class="text-secondary opacity-secondary text-center">{{\'IMAGE_START_SEARCH\' | translate}}</p>\n' + ' <!--<md-icon class="text-grey" md-svg-icon="icons:images"></md-icon> -->\n' + ' </div>\n' + ' </div>\n' + ' </md-dialog-content>\n' + ' <div class="pip-footer">\n' + ' <md-button ng-click="onCancelClick()"\n' + ' ng-hide="transaction.busy()"\n' + ' aria-label="{{ ::\'CANCEL\' | translate }}"\n' + ' tabindex="{{ $images.length + 3 }}">\n' + ' <span class="text-grey">\n' + ' {{ ::\'CANCEL\' | translate }}\n' + ' </span>\n' + ' </md-button>\n' + ' <md-button ng-if="transaction.busy()" ng-click="onStopSearchClick()" class="md-raised md-warn m0"\n' + ' tabindex="5" aria-label="ABORT"\n' + ' pip-test="button-cancel">\n' + ' {{::\'CANCEL\' | translate}}\n' + ' </md-button>\n' + ' <md-button class="md-accent"\n' + ' ng-hide="transaction.busy()"\n' + ' ng-disabled="addButtonDisabled()"\n' + ' ng-click="onAddClick()"\n' + ' aria-label="{{ ::\'ADD\' | translate }}"\n' + ' tabindex="{{ $images.length + 4 }}">\n' + '\n' + ' {{ ::\'ADD\' | translate }}\n' + ' </md-button>\n' + ' </div>\n' + '</md-dialog>'); }]); })(); (function(module) { try { module = angular.module('pipPictures.Templates'); } catch (e) { module = angular.module('pipPictures.Templates', []); } module.run(['$templateCache', function($templateCache) { $templateCache.put('picture_edit/picture_edit.html', '<div class="pip-picture-upload pip-picture-drop md-focused"\n' + ' ng-keydown="onKeyDown($event)"\n' + ' tabindex="{{ ngDisabled() || control.uploading ? -1 : 0 }}"\n' + ' pip-changed="readItemLocally(url, file)"\n' + ' ng-disabled="ngDisabled()"\n' + ' pip-multi="false"\n' + ' ng-focus="onFocus()"\n' + ' ng-blur="onBlur()"\n' + ' pip-add-image>\n' + '\n' + ' <div class="pip-default-icon" ng-show="empty()">\n' + ' <md-icon class="pip-picture-icon" md-svg-icon="icons:{{icon}}"></md-icon>\n' + ' </div>\n' + ' <div class="pip-default-text" ng-show="empty()">\n' + ' <span>{{text | translate}}</span>\n' + ' </div>\n' + '\n' + ' <img class="pip-picture-image"\n' + ' ng-src="{{control.url}}"\n' + ' ng-show="!empty()"\n' + ' ng-class="{ \'pip-image-new\': isUpdated(), \'cursor-default\' : ngDisabled() || control.uploading }"\n' + ' ui-event="{ error: \'onImageError($event)\', load: \'onImageLoad($event)\' }">\n' + '\n' + ' <md-button class="md-icon-button"\n' + ' ng-click="onDeleteClick($event)"\n' + ' tabindex="-1" aria-label="delete"\n' + ' ng-hide="empty() || ngDisabled()"\n' + ' ng-disabled="ngDisabled() || control.uploading">\n' + ' <md-icon md-svg-icon="icons:cross"></md-icon>\n' + ' </md-button>\n' + '\n' + ' <md-progress-linear ng-show="control.uploading"\n' + ' ng-value="control.progress">\n' + ' </md-progress-linear>\n' + '</div>\n' + ''); }]); })(); (function(module) { try { module = angular.module('pipPictures.Templates'); } catch (e) { module = angular.module('pipPictures.Templates', []); } module.run(['$templateCache', function($templateCache) { $templateCache.put('picture_list_edit/picture_list_edit.html', '<div pip-focused>\n' + ' <div class="pip-picture-upload pointer pip-focusable"\n' + ' ng-class="{\'pip-picture-error\': item.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" ng-hide="item.loaded || item.error">\n' + ' <md-icon pip-cancel-drag="true" class="pip-picture-icon" md-svg-icon="icons:{{icon}}"></md-icon>\n' + ' </div>\n' + ' <div class="pip-default-text" ng-show="item.error">\n' + ' <!--span style="color: red">{{ \'ERROR_IMAGE_PRELOADING\' | translate}}</span-->\n' + ' <md-icon pip-cancel-drag="true" md-svg-icon="icons:warn-circle"></md-icon>\n' + ' </div>\n' + ' <img ng-src="{{::item.url}}"\n' + ' pip-cancel-drag="true"\n' + ' ng-hide="item.error"\n' + ' ng-class="{ \'pip-image-new\': item.state == \'added\' }"\n' + ' ui-event="{ error: \'onImageError($event, item)\', load: \'onImageLoad($event, item)\' }">\n' + '\n' + ' <md-button ng-click="onDeleteClick(item)"\n' + ' ng-disabled="ngDisabled() || control.uploading" tabindex="-1"\n' + ' aria-label="delete"\n' + ' class="md-icon-button">\n' + '\n' + ' <md-icon pip-cancel-drag="true" md-svg-icon="icons:cross"></md-icon>\n' + ' </md-button>\n' + ' <md-progress-linear md-mode="indeterminate" ng-show="item.uploading" value="{{ item.progress }}">\n' + ' </md-progress-linear>\n' + ' </div>\n' + '\n' + ' <button class="pip-picture-upload pip-picture-drop pip-focusable"\n' + ' pip-add-image\n' + ' ng-focus="onFocus()"\n' + ' ng-blur="onBlur()"\n' + ' pip-changed="readItemLocally(url, file)"\n' + ' ng-disabled="ngDisabled() || control.uploading">\n' + '\n' + ' <div class="pip-default-icon">\n' + ' <md-icon pip-cancel-drag="true" class="pip-picture-icon" md-svg-icon="icons:{{icon}}"></md-icon>\n' + ' </div>\n' + ' <div class="pip-default-text">\n' + ' <span>{{text | translate}}</span>\n' + ' </div>\n' + ' </button>\n' + ' <div class="clearfix"></div>\n' + '</div>\n' + ''); }]); })(); (function(module) { try { module = angular.module('pipPictures.Templates'); } catch (e) { module = angular.module('pipPictures.Templates', []); } module.run(['$templateCache', function($templateCache) { $templateCache.put('picture_url_dialog/picture_url_dialog.html', '<!--\n' + '@file Picture URL dialog content\n' + '@copyright Digital Living Software Corp. 2014-2016\n' + '-->\n' + '\n' + '<md-dialog class="pip-dialog pip-picture-url-dialog pip-picture-dialog layout-column"\n' + ' md-theme="{{theme}}">\n' + '\n' + ' <md-dialog-content class="pip-body lp0 rp0 tp0 pip-scroll">\n' + ' <div class="pip-header bm16 layout-row layout-align-start-center">\n' + ' <md-button ng-click="onCancelClick()" class="md-icon-button lm0"\n' + ' aria-label="{{ ::\'CANCEL\' | translate }}">\n' + ' <md-icon class="text-grey" md-svg-icon="icons:arrow-left"></md-icon>\n' + ' </md-button>\n' + ' <h3 class="text-title m0">\n' + ' {{ ::\'PICTURE_FROM_WEBLINK\' | translate}}\n' + ' </h3>\n' + ' </div>\n' + '\n' + ' <div class="pip-content">\n' + ' <md-input-container md-no-float class="w-stretch text-subhead1">\n' + ' <input type="text" ng-model="url" ng-change="checkUrl()" placeholder="{{:: \'LINK_PICTURE\' | translate}}"/>\n' + ' </md-input-container>\n' + '\n' + ' <div class="w-stretch layout-row layout-align-center-center"\n' + ' ng-hide="invalid">\n' + ' <img id="url_image"/>\n' + ' </div>\n' + '\n' + ' <div class="pip-no-images layout-row layout-align-center-center" ng-show="invalid">\n' + ' <md-icon class="text-grey" md-svg-icon="icons:images"></md-icon>\n' + ' </div>\n' + '\n' + ' </div>\n' + ' </md-dialog-content>\n' + ' <div class="pip-footer">\n' + ' <md-button ng-click="onCancelClick()" aria-label="{{ ::\'CANCEL\' | translate }}">\n' + ' {{ ::\'CANCEL\' | translate }}\n' + ' </md-button>\n' + '\n' + ' <md-button class="md-accent" ng-click="onAddClick()" ng-disabled="invalid"\n' + ' aria-label="{{ ::\'ADD\' | translate }}">\n' + ' {{ ::\'ADD\' | translate }}\n' + ' </md-button>\n' + ' </div>\n' + '</md-dialog>'); }]); })(); /** * @file Add image control * @copyright Digital Living Software Corp. 2014-2015 */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipAddImage", ['pipCameraDialog', 'pipPictureUrlDialog', 'pipGallerySearchDialog', 'pipCore']); thisModule.config(['pipTranslateProvider', function(pipTranslateProvider) { pipTranslateProvider.translations('en', { 'FILE' : 'Upload pictures', 'WEB_LINK' : 'Use web link', 'CAMERA' : 'Take photo', 'IMAGE_GALLERY': 'Use image library', }); pipTranslateProvider.translations('ru', { 'FILE' : 'Загрузить картинку', 'WEB_LINK' : 'Вставить веб ссылка', 'CAMERA' : 'Использовать камеру', 'IMAGE_GALLERY': 'Открыть галерею изображений' }); }]); thisModule.directive('pipAddImage', function() { return { restrict: 'AC', scope: { $images: '=pipImages', onChange: '&pipChanged', multi: '&pipMulti', ngDisabled: '&' }, transclude: true, templateUrl: 'add_image/add_image.html', controller: 'pipAddImageController' } } ); thisModule.controller('pipAddImageController', ['$scope', '$element', '$mdMenu', '$timeout', 'pipCameraDialog', 'pipPictureUrlDialog', 'pipGallerySearchDialog', 'pipUtils', function ($scope, $element, $mdMenu, $timeout, pipCameraDialog, pipPictureUrlDialog, pipGallerySearchDialog, pipUtils) { $scope.hideMenu = hideMenu; $scope.onFileChange = onFileChange; $scope.onWebLinkClick = onWebLinkClick; $scope.onCameraClick = onCameraClick; $scope.onGalleryClick = onGalleryClick; $scope.isMulti = isMulti; $element.click(function () { if (!$scope.ngDisabled()) openMenu(); }); return; function openMenu() { $($element).find('.pip-add-image-open-button').scope().$mdOpenMenu(); } function isMulti() { if ($scope.multi() !== undefined) return pipUtils.toBoolean($scope.multi()); else return true; } function hideMenu () { $mdMenu.hide(); } function dataURItoBlob(dataURI) { var byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) byteString = atob(dataURI.split(',')[1]); else byteString = unescape(dataURI.split(',')[1]); var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], {type:mimeString}); } function addImages(images) { if (images === undefined) return; if (Array.isArray(images)) { images.forEach(function (img) { if ($scope.onChange) $scope.onChange({url: img.url, file: img.file}); }); } else { if ($scope.onChange) $scope.onChange({url: images.url, file: images.file}); } if ($scope.$images === undefined || !Array.isArray($scope.$images)) return; if (Array.isArray(images)) { images.forEach(function (img) { $scope.$images.push(img.url); }); } else { $scope.$images.push(images.url); } } // Process user actions function onFileChange ($files) { if ($files == null || $files.length == 0) return; $files.forEach(function (file) { if (file.type.indexOf('image') > -1) { $timeout(function() { var fileReader = new FileReader(); fileReader.readAsDataURL(file); fileReader.onload = function(e) { $timeout(function() { addImages({url: e.target.result, file: file}); }); } }); } }); } function onWebLinkClick () { pipPictureUrlDialog.show(function (result) { var blob = null; if (result.substring(0, 10) == 'data:image') { blob = dataURItoBlob(result); blob.name = result.slice(result.lastIndexOf('/') + 1, result.length).split('?')[0]; } addImages({url: result, file: blob}); }); } function onCameraClick () { pipCameraDialog.show(function (result) { var blob = dataURItoBlob(result); blob.name = 'camera'; addImages({url: result, file: blob}); }); } function onGalleryClick () { pipGallerySearchDialog.show(function (result) { var imgs = []; result.forEach(function (url) { imgs.push({url: url, file: null}); }); addImages(imgs); }, $scope.isMulti()); } }]); })(); /** * @file Avatar control * @copyright Digital Living Software Corp. 2014-2015 * @todo * - Improve samples in sampler app * - Replace placeholder with default image generated on server * - Generate file url based on entity type, do not set it as full url via pipSrc */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipAvatarEdit", ['ui.event', 'angularFileUpload', 'pipPicturePaste', 'pipRest', 'pipImageUtils', 'pipPictures.Templates']); thisModule.config(['pipTranslateProvider', function(pipTranslateProvider) { pipTranslateProvider.translations('en', { 'PICTURE_EDIT_TEXT': 'Click here to upload a picture' }); pipTranslateProvider.translations('ru', { 'PICTURE_EDIT_TEXT': 'Нажмите сюда для загрузки картинки' }); }]); thisModule.directive('pipAvatarEdit', function() { return { restrict: 'EA', scope: { ngDisabled: '&', pipPartyId: '&', pipEntityType: '&', pipId: '&', pipCreated: '&', pipChanged: '&', pipReset: '&' }, templateUrl: 'picture_edit/picture_edit.html', controller: 'pipAvatarEditController' }; } ); thisModule.controller('pipAvatarEditController', ['$scope', '$element', '$attrs', '$http', '$upload', '$rootScope', 'pipPicturePaste', 'pipRest', 'pipImageUtils', function($scope, $element, $attrs, $http, $upload, $rootScope, pipPicturePaste, pipRest, pipImageUtils) { var $control = $element.children('.pip-picture-upload'), $input = $control.children('input[type=file]'), entityTypes = pipImageUtils.getEntityTypes(), serverUrl = pipRest.serverUrl(); $scope.text = $attrs.pipDefaultText || 'PICTURE_EDIT_TEXT'; $scope.icon = $attrs.pipDefaultIcon || 'picture-no-border'; $scope.control = { url: '', progress: 0, uploaded: false, uploading: false, loaded: false, file: null, state: 'original' }; $scope.control.reset = reset; $scope.control.save = save; $scope.empty = empty; $scope.isUpdated = isUpdated; $scope.readItemLocally = readItemLocally; $scope.onDeleteClick = onDeleteClick; $scope.onKeyDown = onKeyDown; $scope.onImageError = onImageError; $scope.onImageLoad = onImageLoad; $scope.onChange = onChange; // Watching for changes $scope.$watch($scope.pipId, function(newValue) { if ($scope.pipReset() !== false) $scope.control.reset(); }); $scope.$watch($scope.pipPartyId, function(newValue) { if ($scope.pipReset() !== false) $scope.control.reset(); }); $scope.$watch($scope.ngDisabled, function(newValue) { $input.attr('disabled', $scope.control.disabled); }); // Add paste listener $element.children('.pip-picture-upload').focus(function () { pipPicturePaste.addPasteListener(function (item) { $scope.readItemLocally(item.url, item.file); }); }); $element.children('.pip-picture-upload').blur(function () { pipPicturePaste.removePasteListener(); }); // Add class $element.addClass('pip-picture-edit'); // Execute callback if ($scope.pipCreated) { $scope.pipCreated({ $event: { sender: $scope.control }, $control: $scope.control }); } // Initialize control $scope.control.reset(); return; // -------------------------------- // Utility functions function reset(afterDeleting) { $scope.control.progress = 0; $scope.control.uploading = false; $scope.control.uploaded = false; $scope.control.file = null; $scope.control.state = 'original'; $scope.control.url = ''; if (!afterDeleting) { var url = pipImageUtils.getAvatarUrl($scope.pipPartyId(), '', $scope.pipId(), $scope.pipEntityType(), false, true); if (!url) return; //pipImageUtils.addHttpHeaders(); $scope.control.progress = 0; $scope.control.url = url; $scope.control.uploaded = $scope.control.url != ''; $scope.onChange(); } else $scope.onChange(); }; function generateUrl() { if ($scope.pipEntityType() && $scope.pipId() && $scope.pipPartyId()) { return serverUrl + '/api/parties/' + $scope.pipPartyId() + '/' + entityTypes[$scope.pipEntityType()] + '/' + $scope.pipId() + '/avatar'; } else { if ($scope.pipPartyId() && !$scope.pipEntityType()) { if ($attrs.pipEntityType || $attrs.pipId) return ''; return serverUrl + '/api/parties/' + $scope.pipPartyId() + '/avatar'; } } return ''; } function saveItemUrl() { var url = $scope.control.url, FILE_URL = generateUrl(); var name = url.slice(url.lastIndexOf('/') + 1, url.length).split('?')[0]; return FILE_URL + '?name=' + name + '&url=' + url }; function savePicture(successCallback, errorCallback) { var control = $scope.control, file = control.file; if ($scope.control.file !== null) { var fileReader = new FileReader(), FILE_URL = generateUrl(); fileReader.onload = function (e) { control.uploading = true; var upload = $upload.http({ url: FILE_URL + '?name=' + file.name, headers: { 'Content-Type': file.type }, data: e.target.result }) .then( function (response) { control.reset(); if (successCallback) successCallback(response); }, function (error) { control.uploading = false; control.upload = false; control.progress = 0; if (errorCallback) errorCallback(error); else console.error(error); }, function (e) { // Math.min is to fix IE which reports 200% sometimes control.progress = Math.min(100, parseInt(100.0 * e.loaded / e.total)); } ); }; fileReader.readAsArrayBuffer(file); } else { var url = saveItemUrl(); control.uploading = true; $http['post'](url) .success(function (response) { control.reset(); if (successCallback) successCallback(response); }) .error(function (error) { control.uploading = false; control.upload = false; if (errorCallback) errorCallback(error); else console.error(error); }); } }; function deletePicture(successCallback, errorCallback) { var control = $scope.control; $http['delete'](generateUrl()) .success(function (data) { control.reset(true); if (successCallback) successCallback(); }) .error(function (error) { control.uploading = false; control.upload = false; control.progress = 0; //$scope.$apply(); if (errorCallback) errorCallback(error); else console.error(error); }); }; function save(successCallback, errorCallback) { // Process changes of the image if ($scope.control.state == 'changed') { savePicture(successCallback, errorCallback); } // Process deletion of the image else if ($scope.control.state == 'deleted') { deletePicture(successCallback, errorCallback); } // Process if no changes were made else { if (successCallback) successCallback(); } }; // Visual functions function empty() { return $scope.control.url == '' && !$scope.control.uploading; }; function isUpdated() { return $scope.control.state != 'original'; }; // Process user events function readItemLocally(url, file) { $scope.control.file = file; $scope.control.url = url; $scope.control.state = 'changed'; $scope.onChange(); }; function onDeleteClick($event) { $event.stopPropagation(); $control.focus(); $scope.control.file = null; $scope.control.url = ''; $scope.control.state = 'deleted'; $scope.onChange(); }; function onKeyDown($event) { if ($event.keyCode == 13 || $event.keyCode == 32) { setTimeout(function() { $control.trigger('click'); }, 0); } else if ($event.keyCode == 46 || $event.keyCode == 8) { $scope.control.file = null; $scope.control.url = ''; $scope.control.state = 'deleted'; $scope.onChange(); } else if ($event.keyCode == 27) { $scope.control.reset(); } }; // Clean up url to remove broken icon function onImageError($event) { $scope.$apply(function() { $scope.control.url = ''; var image = $($event.target); pipImageUtils.setErrorImageCSS(image, {width: 'auto', height: '100%'}); }); }; // When image is loaded resize/reposition it function onImageLoad($event) { var image = $($event.target); pipImageUtils.setImageMarginCSS({clientWidth: 80, clientHeight: 80}, image); $scope.control.uploading = false; }; // On change event function onChange() { if ($scope.pipChanged) { $scope.pipChanged({ $event: { sender: $scope.control }, $control: $scope.control }); } }; }] ); })(); /** * @file Avatar control * @copyright Digital Living Software Corp. 2014-2015 * @todo * - Improve samples in sampler app * - Replace placeholder with default image generated on server * - Fix resizing problem */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipAvatar", ['pipCore', 'pipRest', 'pipImageUtils']); thisModule.directive('pipAvatar', function () { return { restrict: 'EA', scope: false, template: '<md-icon></md-icon><img/>' + '<div><md-icon class="default_icon" id="icon-film" md-svg-icon="icons:film"></md-icon>' + '<md-icon class="default_icon" id="icon-task" md-svg-icon="icons:task"></md-icon>' + '<md-icon class="default_icon" id="icon-folder" md-svg-icon="icons:folder"></md-icon></div>', controller: 'pipAvatarController' } } ); thisModule.controller('pipAvatarController', ['$scope', '$rootScope', '$element', '$attrs', '$parse', 'pipUtils', 'pipStrings', 'pipRest', '$http', 'pipImageUtils', function ($scope, $rootScope, $element, $attrs, $parse, pipUtils, pipStrings, pipRest, $http, pipImageUtils) { var $svg = $element.children('md-icon'), $image = $element.children('img'), $defaultBlock = $element.children('div'), $iconFilm = $element.find('#icon-film'), $iconTask = $element.find('#icon-task'), $iconFolder = $element.find('#icon-folder'), image = null, partyIdGetter = $parse($attrs.pipPartyId), partyNameGetter = $parse($attrs.pipPartyName), typeGetter = $parse($attrs.pipEntityType), idGetter = $parse($attrs.pipId), urlGetter = $parse($attrs.pipImageUrl), colors = pipImageUtils.getAvatarColors(), colorClasses = pipImageUtils.getColorClasses(), entityTypes = pipImageUtils.getEntityTypes(); // When image is loaded resize/reposition it $image.load(function ($event) { image = $($event.target); pipImageUtils.setImageMarginCSS($element, image); }); // Add class $element.addClass('pip-avatar flex-fixed'); if ($attrs.ngClass) { $scope.$watch($attrs.ngClass, function () { setTimeout(function () { pipImageUtils.setImageMarginCSS($element, image); }, 50); }); } // Optimization to avoid binding bindControl(); if (pipUtils.toBoolean($attrs.pipRebindAvatar)) { $rootScope.$on('pipPartyAvatarUpdated', refreshAvatar); } // Also optimization to avoid watch if it is unnecessary if (pipUtils.toBoolean($attrs.pipRebind)) { $scope.$watch(partyIdGetter, function (newValue, oldValue) { if (oldValue !== newValue) bindControl(); }); $scope.$watch(idGetter, function (newValue, oldValue) { if (oldValue !== newValue) bindControl(); }); $scope.$watch(urlGetter, function (newValue, oldValue) { if (oldValue !== newValue) bindControl(); }); } return; function refreshAvatar() { $iconTask.css('display', 'none'); $iconFilm.css('display', 'none'); $iconFolder.css('display', 'none'); $defaultBlock.css('display', 'none'); $image.attr('src', ''); $svg.css('display', 'none'); $image.css('display', 'inline-block'); bindControl(); }; function bindControl() { var partyName = partyNameGetter($scope), partyId = partyIdGetter($scope), id = idGetter($scope), type = typeGetter($scope), transUrl = urlGetter($scope); $iconTask.css('display', 'none'); $iconFilm.css('display', 'none'); $iconFolder.css('display', 'none'); $defaultBlock.css('display', 'none'); // Timestamp to avoid caching images for too long var url = transUrl || pipImageUtils.getAvatarUrl(partyId, partyName, id, type, false, false); if ((type && id && partyId) || (partyId && partyName) || transUrl) { if (type && id && partyId) { if (type == 'category') return; if (entityTypes[type] == 'goals' || entityTypes[type] == 'areas' ) { $image.attr('src', url); $svg.css('display', 'none'); $image.css('display', 'inline-block'); } else { $defaultBlock.css('display', 'block'); var colorClassIndex = pipStrings.hashCode(id) % colors.length; $element.addClass(colorClasses[colorClassIndex]); switch(type) { case 'vision': $svg.css('display', 'none'); $iconFilm.css('display', 'inline-block'); $iconTask.css('display', 'none'); $iconFolder.css('display', 'none'); $image.css('display', 'none'); break; case 'event': $svg.css('display', 'none'); $iconTask.css('display', 'inline-block'); $iconFilm.css('display', 'none'); $iconFolder.css('display', 'none'); $image.css('display', 'none'); break; case 'note': $svg.css('display', 'none'); $iconFolder.css('display', 'inline-block'); $iconTask.css('display', 'none'); $iconFilm.css('display', 'none'); $image.css('display', 'none'); break; } } } else { $image.attr('src', url); $svg.css('display', 'none'); $image.css('display', 'inline-block'); } } }; }] ); })(); /** * @file Camera dialog * @copyright Digital Living Software Corp. 2014-2015 * @todo * - Add sample to sampler app */ /* global angular, Webcam */ (function () { 'use strict'; var thisModule = angular.module('pipCameraDialog', ['ngMaterial', 'pipCore', 'pipPictures.Templates']); thisModule.config(['pipTranslateProvider', function (pipTranslateProvider) { pipTranslateProvider.translations('en', { 'TAKE_PICTURE': 'Take a picture', 'WEB_CAM_ERROR': 'Webcam is missing or was not found' }); pipTranslateProvider.translations('ru', { 'TAKE_PICTURE': 'Сделать фото', 'WEB_CAM_ERROR': 'Web-камера отсутствует или не найдена' }); }]); thisModule.factory('pipCameraDialog', ['$mdDialog', function ($mdDialog) { return { show: function (successCallback) { $mdDialog.show({ templateUrl: 'camera_dialog/camera_dialog.html', clickOutsideToClose: true, controller: 'pipCameraController' }).then(function (result) { Webcam.reset(); console.log(result); if (successCallback) { successCallback(result); } }, function () { Webcam.reset(); }); } }; }]); thisModule.controller('pipCameraController', ['$scope', '$rootScope', '$timeout', '$mdMenu', '$mdDialog', 'pipUtils', function ($scope, $rootScope, $timeout, $mdMenu, $mdDialog, pipUtils) { // $cordovaCamera $scope.browser = pipUtils.getBrowser().os; $scope.theme = $rootScope.$theme; if ($scope.browser !== 'android') { console.log('webcam'); Webcam.init(); setTimeout(function () { Webcam.attach('.camera-stream'); }, 0); Webcam.on('error', function (err) { $scope.webCamError = true; console.error(err); }); Webcam.set({ width: 400, height: 300, dest_width: 400, dest_height: 300, crop_width: 400, crop_height: 300, image_format: 'jpeg', jpeg_quality: 90 }); //Webcam.setSWFLocation('../../../dist/webcam.swf'); Webcam.setSWFLocation('webcam.swf'); } else { document.addEventListener("deviceready",onDeviceReady,false); } // todo add logic in callbacks function onDeviceReady() { navigator.camera.getPicture(onSuccess, onFail, { sourceType: Camera.PictureSourceType.CAMERA, correctOrientation: true, quality: 75, targetWidth: 200, destinationType: Camera.DestinationType.DATA_URL }); } function onSuccess(imageData) { var picture = imageData; var picture = 'data:image/jpeg;base64,' + imageData; $mdDialog.hide(picture); } function onFail(message) { alert('Failed because: ' + message); $mdDialog.hide(); } $scope.$freeze = false; $scope.onTakePictureClick = onTakePictureClick; $scope.onResetPicture = onResetPicture; $scope.onCancelClick = onCancelClick; return; function onTakePictureClick() { if (Webcam) { if ($scope.$freeze) { Webcam.snap(function (dataUri) { $scope.$freeze = false; $mdDialog.hide(dataUri); }); } else { $scope.$freeze = true;