pip-webui
Version:
HTML5 UI for LOB applications
1,224 lines (1,065 loc) • 152 kB
JavaScript
/**
* @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;