/** * EGroupware eTemplate2 - JS Description object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link http://www.egroupware.org * @author Nathan Gray * @copyright Nathan Gray 2011 * @version $Id$ */ /*egw:uses /vendor/bower-asset/jquery/dist/jquery.js; et2_core_interfaces; et2_core_baseWidget; expose; /vendor/bower-asset/cropper/dist/cropper.min.js; */ /** * Class which implements the "image" XET-Tag * * @augments et2_baseWidget */ var et2_image = (function(){ "use strict"; return expose(et2_baseWidget.extend([et2_IDetachedDOM], { attributes: { "src": { "name": "Image", "type": "string", "description": "Displayed image" }, default_src: { name: "Default image", type: "string", description: "Image to use if src is not found" }, "href": { "name": "Link Target", "type": "string", "description": "Link URL, empty if you don't wan't to display a link.", "default": et2_no_init }, "extra_link_target": { "name": "Link target", "type": "string", "default": "_self", "description": "Link target descriptor" }, "extra_link_popup": { "name": "Popup", "type": "string", "description": "widthxheight, if popup should be used, eg. 640x480" }, "imagemap":{ // TODO: Do something with this "name": "Image map", "description": "Currently not implemented" }, "label": { "name": "Label", "type": "string", "description": "Label for image" }, "expose_view":{ name: "Expose view", type: "boolean", default: false, description: "Clicking on an image with href value would popup an expose view, and will show image referenced by href." } }, legacyOptions: ["href", "extra_link_target", "imagemap", "extra_link_popup", "id"], /** * Constructor * * @memberOf et2_image */ init: function() { this._super.apply(this, arguments); // Create the image or a/image tag this.image = jQuery(document.createElement("img")); if (this.options.label) { this.image.attr("alt", this.options.label).attr("title", this.options.label); } if (this.options.href) { this.image.addClass('et2_clickable'); } if(this.options["class"]) { this.image.addClass(this.options["class"]); } this.setDOMNode(this.image[0]); }, click: function() { if(this.options.href) { this.egw().open_link(this.options.href, this.options.extra_link_target, this.options.extra_link_popup); } else { this._super.apply(this,arguments); } }, transformAttributes: function(_attrs) { this._super.apply(arguments); // Check to expand name if (typeof _attrs["src"] != "undefined") { var manager = this.getArrayMgr("content"); if(manager) { var src = manager.getEntry(_attrs["src"]); if (typeof src != "undefined" && src !== null) { if(typeof src == "object") { src = egw().link('/index.php', src); } _attrs["src"] = src; } } } }, set_label: function(_value) { this.options.label = _value; _value = this.egw().lang(_value); // label is NOT the alt attribute in eTemplate, but the title/tooltip this.image.attr("alt", _value).attr("title", _value); }, setValue: function(_value) { // Value is src, images don't get IDs this.set_src(_value); }, set_href: function (_value) { if (!this.isInTree()) { return false; } this.options.href = _value; this.image.wrapAll('"'); var href = this.options.href; var popup = this.options.extra_link_popup; var target = this.options.extra_link_target; var self = this; this.image.click(function(e) { if (self.options.expose_view) { self._init_blueimp_gallery(e,_value); e.stopImmediatePropagation(); } else { egw.open_link(href,target,popup); } e.preventDefault(); return false; }); return true; }, /** * Set image src * * @param {string} _value image, app/image or url * @return {boolean} true if image was found, false if not (image is either not displayed or default_src is used) */ set_src: function(_value) { if(!this.isInTree()) { return false; } this.options.src = _value; var src = this.egw().image(_value); if (src) { this.image.attr("src", src).show(); return true; } // allow url's too else if (_value[0] == '/' || _value.substr(0,4) == 'http') { this.image.attr('src', _value).show(); return true; } src = null; if (this.options.default_src) { src = this.egw().image(this.options.default_src); } if (src) { this.image.attr("src", src).show(); } else { this.image.css("display","none"); } return false; }, /** * Function to get media content to feed the expose * @param {type} _value * @returns {Array|Array.getMedia.mediaContent} */ getMedia: function (_value) { var base_url = egw.webserverUrl.match(/^\//,'ig')?egw(window).window.location.origin + egw.webserverUrl + '/':egw.webserverUrl + '/'; var mediaContent = []; if (_value) { mediaContent = [{ title: this.options.label, href: base_url + _value, type: this.options.type + "/*", thumbnail: base_url + _value }]; } return mediaContent; }, /** * Implementation of "et2_IDetachedDOM" for fast viewing in gridview * * @param {array} _attrs */ getDetachedAttributes: function(_attrs) { _attrs.push("src", "label", "href"); }, getDetachedNodes: function() { return [this.image[0]]; }, setDetachedAttributes: function(_nodes, _values) { // Set the given DOM-Nodes this.image = jQuery(_nodes[0]); // Set the attributes if (_values["src"]) { this.set_src(_values["src"]); } if (_values["label"]) { this.set_label(_values["label"]); } if(_values["href"]) { this.image.addClass('et2_clickable'); this.set_href(_values["href"]); } } }));}).call(this); et2_register_widget(et2_image, ["image"]); /** * Widget displaying an application icon */ var et2_appicon = (function(){ "use strict"; return et2_image.extend( { attributes: { default_src: { name: "Default image", type: "string", default: "nonav", description: "Image to use if there is no application icon" } }, set_src: function(_app) { if (!_app) _app = this.egw().app_name(); this.image.addClass('et2_appicon'); this._super.call(this, _app == 'sitemgr-link' ? 'sitemgr/sitemgr-link' : // got removed from jdots (this.egw().app(_app, 'icon_app') || _app)+'/'+(this.egw().app(_app, 'icon') || 'navbar')); } });}).call(this); et2_register_widget(et2_appicon, ["appicon"]); /** * Avatar widget to display user profile picture or * user letter avatar based on user's firstname lastname. * * @augments et2_baseWidget */ var et2_avatar = (function(){ "use strict"; return et2_image.extend( { attributes: { "contact_id": { name: "Contact id", type: "string", default: "", description: "Contact id should be either user account_id {account:number} or contact_id {contact:number or number}" }, "default_src": { "ignore": true }, "frame": { name: "Avatar frame", type: "string", default: "circle", description: "Define the shape of frame that avatar will be shown inside it. it can get {circle,rectangle} values which default value is cicle." }, editable: { name: "Edit avatar", type: "boolean", default: false, description: "Make avatar widget editable to be able to crop profile picture or upload a new photo" }, crop: { name: "Crop avatar", type: "boolean", default: false, description: "Create crop container and cropping feature" } }, init: function () { this._super.apply(this,arguments); if (this.options.frame == 'circle') { this.image.attr('style', 'border-radius:50%'); } if (this.options.contact_id) this.setValue(this.options.contact_id); }, /** * Function to set contact id * contact id could be in one of these formats: * 'number', will be consider as contact_id * 'contact:number', similar to above * 'account:number', will be consider as account id * @example: contact_id = "account:4" * * @param {string} _contact_id contact id could be as above mentioned formats */ set_contact_id: function(_contact_id) { var params = { menuaction: 'addressbook.addressbook_ui.photo' }; var id = 'contact_id'; if (!_contact_id) { _contact_id = this.egw().user('account_id'); } else if(_contact_id.match(/account\:/)) { id = 'account_id'; _contact_id = _contact_id.replace('account:',''); } else { id = 'contact_id'; _contact_id = _contact_id.replace('contact:', ''); } params[id] = _contact_id; this.image.addClass('et2_avatar'); var url = egw.link('/index.php',params); this.set_src(url); }, /** * Function to set value * @param {string} _value */ setValue: function(_value) { this.set_contact_id(_value); }, /** * Implementation of "et2_IDetachedDOM" for fast viewing in gridview * * @param {array} _attrs */ getDetachedAttributes: function(_attrs) { _attrs.push("contact_id", "label", "href"); }, setDetachedAttributes: function (_nodes, _values) { // Set the given DOM-Nodes this.image = jQuery(_nodes[0]); if (_values["contact_id"]) { this.set_contact_id(_values["contact_id"]); } if (_values["label"]) { this.set_label(_values["label"]); } if(_values["href"]) { this.image.addClass('et2_clickable'); this.set_href(_values["href"]); } }, /** * Build Editable Mask Layer (EML) in order to show edit/delete actions * on top of profile picture. * @param {boolean} _noDelete disable delete button in initialization */ _buildEditableLayer: function (_noDelete) { var self = this; // editable mask layer (eml) var eml = jQuery(document.createElement('div')) .addClass('eml') .insertAfter(this.image); // edit button var edit = jQuery(document.createElement('div')) .addClass('emlEdit') .click(function(){ var buttons = [ {"button_id": 1,"text": self.egw().lang('save'), id: 'save', image: 'check', "default":true}, {"button_id": 0,"text": self.egw().lang('cancel'), id: 'cancel', image: 'cancelled'} ]; var dialog = function(_title, _value, _buttons, _egw_or_appname) { return et2_createWidget("dialog", { callback: function(_buttons, _value) { if (_buttons == 'save') { var canvas = jQuery('#_cropper_image').cropper('getCroppedCanvas'); self.image.attr('src', canvas.toDataURL("image/jpeg", 1.0)); self.egw().json('addressbook.addressbook_ui.ajax_update_photo', [self.getInstanceManager().etemplate_exec_id, canvas.toDataURL('image/jpeg',1.0)], function(res) { if (res) { del.show(); } }).sendRequest(); } }, title: _title||egw.lang('Input required'), buttons: _buttons||et2_dialog.BUTTONS_OK_CANCEL, value: { content: _value }, width: "90%", height:"90%", resizable: false, position:"top+10", template: egw.webserverUrl+'/api/templates/default/avatar_edit.xet?2' }, et2_dialog._create_parent(_egw_or_appname)); }; dialog(egw.lang('Edit avatar'),{photo:self.options.contact_id},buttons); }) .appendTo(eml); // delete button var del = jQuery(document.createElement('div')) .addClass('emlDelete') .click(function(){ et2_dialog.show_dialog(function(_btn){ if (_btn == et2_dialog.YES_BUTTON) { self.egw().json('addressbook.addressbook_ui.ajax_update_photo', [self.getInstanceManager().etemplate_exec_id, null], function(res) { if (res) { self.image.attr('src',''); del.hide(); egw.refresh('Avatar Deleted.', egw.app_name()); } }).sendRequest(); } }, egw.lang('Delete this photo?'), 'Delete', null, et2_dialog.BUTTONS_YES_NO); }) .appendTo(eml); if (_noDelete) del.hide(); // invisible the mask eml.css('opacity','0'); eml.parent().css('position', "relative"); // bind handler for activating actions on editable mask eml.on({ mouseover:function(){eml.css('opacity','0.9');}, mouseout: function (){eml.css('opacity','0');} }); }, /** * We need to build the Editable Mask Layer after widget gets loaded */ doLoadingFinished: function () { this._super.apply(this,arguments); var self = this; if (this.options.editable) { egw(window).json( 'addressbook.addressbook_ui.ajax_noPhotoExists', [this.options.contact_id], function(noPhotoExists) { if (noPhotoExists) self.image.attr('src',''); self._buildEditableLayer(noPhotoExists); } ).sendRequest(true); } if (this.options.crop) { var cropped = jQuery(this.image).cropper({ aspectRatio: 1/1, crop: function (e){ console.log (e); } }); } } });}).call(this); et2_register_widget(et2_avatar, ["avatar"]); /** * Avatar readonly widget to only display user profile picture or * user letter avatar based on user's firstname lastname. * * @augments et2_baseWidget */ var et2_avatar_ro = (function(){ "use strict"; return et2_avatar.extend( { init: function () { this._super.apply(this,arguments); this.options.editable = false; } });}).call(this); et2_register_widget(et2_avatar_ro, ["avatar_ro"]); jQuery.extend(et2_avatar, { /** * Function runs after uplaod in avatar dialog is finished and it tries to * update image and cropper container. * * @param {type} e * @returns {undefined} */ uploadAvatar_onFinish: function (e){ var file = e.data.resumable.files[0].file; var reader = new FileReader(); reader.onload = function (e) { jQuery('#_cropper_image').attr('src', e.target.result); jQuery('#_cropper_image').cropper('replace',e.target.result); }; reader.readAsDataURL(file); } });