/*globals define*/ /*jslint nomen: true, regexp: true*/ define('de_epages/shop/linkpicker', [ 'jquery', 'backbone', 'ep', 'util/storage', 'de_epages/shop/linkpicker/linktextview', 'de_epages/shop/linkpicker/pagetreeview', 'de_epages/shop/linkpicker/filemanagerview', 'de_epages/shop/linkpicker/additionalpagesview', 'de_epages/shop/linkpicker/externalurlview', 'de_epages/shop/linkpicker/anchorview', '$tmpl!de_epages/shop/linkpicker', '$dict!./dictionary', 'jquery/ui/tabs', 'ep/ui/dialog' ], function ($, Backbone, ep, storage, LinktextView, PagetreeView, FilemanagerView, AdditionalpagesView, ExternalView, AnchorView, template, dict) { 'use strict'; var View = Backbone.View.extend({ tagName: 'div', template: template({}).dictParse(dict, true), callback: $.noop, activeTab: 0, /* * Initialize child views and set listeners */ initialize: function () { // creating subview instances this.linkTextView = new LinktextView(); this.filemanagerView = new FilemanagerView(); this.additionalPagesView = new AdditionalpagesView(); this.externalUrlView = new ExternalView(); this.pageTreeView = new PagetreeView(); this.anchorView = new AnchorView(); // listen to events the subviews are publishing and call appropriate functions this.listenTo(this.linkTextView, 'linktextchanged', function (_text) { this.is_linkTextChanged = true; this._setLinkText(_text); }); this.listenTo(this.linkTextView, 'targetoptionchanged', this._setLinkTarget); this.listenTo(this.anchorView, 'anchorselected', function (_url) { this._setLinkUrl(_url); this.linkType = 'anchor'; this._clearViewDataExceptFor(this.linkType); }); this.listenTo(this.additionalPagesView, 'linkpicked', function (_text, _url) { this._setLinkText(_text); this._setLinkUrl(this._getCorrectUrl(_url)); this.linkType = 'additional'; this._clearViewDataExceptFor(this.linkType); }); this.listenTo(this.pageTreeView, 'linkpicked', function (_text, _url) { this._setLinkText(_text); this._setLinkUrl(this._getCorrectUrl(_url)); this.linkType = 'tree'; this._clearViewDataExceptFor(this.linkType); }); this.listenTo(this.filemanagerView, 'linkpicked', function (_text, _url, _preview) { this._setLinkText(_text); this._setLinkUrl(this._getCorrectUrl(_url)); this.linkType = 'filemanager'; this._clearViewDataExceptFor(this.linkType); }); this.listenTo(this.externalUrlView, 'linkurlchanged', function (_url) { this._setLinkUrl(this._getCorrectUrl(_url)); this.linkType = 'external'; this._clearViewDataExceptFor(this.linkType); }); // render the view for additional pages (radios) as soon as the ajax request is done (datafetched triggered) this.listenTo(this.additionalPagesView, 'datafetched', function () { this.additionalPagesView.render(this.linkUrl.replace(/.*\?/g,'?')); }); }, /** * Opening the linkpicker dialog or creating one if it doesn't exist * Can be called right from the required linkpicker instance * * @param {object} _options * @param {Boolean} _options.use_absolute Initialize linkpicker to return absolute urls * @param {Boolean} _options.use_ssl Initialize linkpicker to return https urls * @param {String} _options.linkText Text that is displayed as the link's text * @param {String} _options.linkUrl Url the link refers to, * @param {Boolean} _options.is_linkTargetBlank Whether link should be opened in new window or not * @param {String} _options.linkType Type of the link. Can either be 'tree', 'additional', 'filemanager' or 'external' * should be read from data-link-type attribute */ open: function (_options) { // initializing linkpicker options this.use_absoluteUrl = _options.use_absolute; this.use_ssl = _options.use_ssl; this.anchors = _options.anchors || []; // getting elements out of the template for further access (e.g. 'dialog') $.extend(this, $.tmplItem(this.template).elements); var linkpicker = this, callback = _options.callback || $.noop, // initializing dialog options dialogOptions = _options.dialog || { title: dict.translate('EditLink'), width: 920, height: 563, draggable: false, modal: true, autoOpen: false, buttons: [{ text: dict.translate('Apply'), click: function () { $(this).uiDialog('close'); $(this).uiDialog('destroy'); storage.localStorage('linkpickerLastActiveTab', linkpicker._getActiveTabAndSetOptions(linkpicker.linkType)); callback(linkpicker.linkText, linkpicker.linkUrl, linkpicker.is_linkTargetBlank, linkpicker.linkType, linkpicker.pageTreeId); } }, { text: dict.translate('Cancel'), click: function () { $(this).uiDialog('close'); } }] }, dialog = this.dialog; // Setting anchors, link text and url this.linkText = _options.linkText; this.linkUrl = _options.linkUrl; this.anchorView.setAnchors(this.linkUrl, this.anchors); // (Re-)creating the dialog this.dialog.uiDialog(dialogOptions); // omitting template's and backbone's div and appending straight to the dialog this.render().$el.children().children().appendTo(this.dialog); this.dialog.uiDialog('open'); // setting link target this._setLinkTarget(_options.is_linkTargetBlank); //setting members and view options (in case the picker is opened with predefined values) if (_options.linkText) { this.linkTextView.setLinkText(_options.linkText); this._setLinkUrl(_options.linkUrl); this.is_linkTextChanged = true; this.linkType = _options.linkType; } else { this.is_linkTextChanged = false; this._setLinkText(''); this._setLinkUrl(''); // clear all view specific data this.pageTreeView.clearLinkData(); this.additionalPagesView.clearLinkData(); this.externalUrlView.clearLinkData(); this.anchorView.clearLinkData(); } // clear all other tabs this._clearViewDataExceptFor(this.linkType); // converting template node to tabs and selecting last open tab this.tabpanel.tabs({ active: this._getActiveTabAndSetOptions(this.linkType) }); return this; }, /** * Closing the dialog */ close: function () { if (this.dialog.data('uiUiDialog')) { this.dialog.uiDialog('close'); } }, /** * Renders the main view * @return {Object} Backbone view */ render: function () { // appending main template this.$el.append(this.template); // rendering view for linktext // nice to know: when omitting backbone's root div, all events regegistered are also omitted! this.linktext.append(this.linkTextView.render().$el); this.anchor.append(this.anchorView.render(this.linkUrl).$el); this.pagetree.append(this.pageTreeView.render().el); this.filemanager.append(this.filemanagerView.render().el); // the view is rendered as soon as getAdditionalPages() has returned data this.additionalPagesView.getAdditionalPages(); this.additionalpages.append(this.additionalPagesView.el); this.externalurl.append(this.externalUrlView.render().el); return this; }, /* * Determine whether the given protocol is correct depending on the initialization options (use_ssl) * @param {String} _protocol Protocol to be checked * @return {String} Correct protocol based on initialization options (ssl) * @private */ _getCorrectProtocol: function (_protocol) { // correct the protocol if we have an internal link, ignore for externals if (_protocol && this.linkType !== 'external') { if (this.use_ssl && _protocol === 'http') { _protocol = 'https'; } else if (!this.use_ssl && _protocol === 'https') { _protocol = 'http'; } else { // everything is fine, return the absolute url return _protocol; } } // url is relative, no need to touch the protocol (whyever someone called for getting the correct protocol on a realtive url) return _protocol; }, /* * Returns absolute or relative url depending on given url and initialization options of the linkpicker (use_absoluteUrl) * @param {String} _url Url to be checked * @return {String} link url based on initialization options (absoluteUrl) * @private */ _getCorrectUrl: function (_url) { var relativeUrlExpr = /^(\/|\?[\w\W]*)/i, absoluteUrlExpr = /^(http|https):\/\/([\w\W]*)/i, splitUrl = absoluteUrlExpr.exec(_url), sfUrl = ep.config.storeFrontUrl, url = _url, protocol; if (_url) { if (splitUrl) { protocol = splitUrl[1]; url = splitUrl[2]; } if (this.use_absoluteUrl) { if (protocol) { // we got an absolute url, return url with correct protocol url = (this._getCorrectProtocol(protocol) + '://' + url); } else if (this.linkType !== 'external' || (this.linkType === 'external' && _url.match(relativeUrlExpr))) { // we got back a relative url or user entered correct relative url, convert to absolute and return url with correct protocol if (_url.indexOf('/') === 0) { // looks like we have a file url = ep.config.protocolAndServer.replace(/^(http|https)/i, this.use_ssl ? 'https' : 'http') + _url; } else { url = sfUrl.replace(/^(http|https)/i, this.use_ssl ? 'https' : 'http') + _url; } } else { // user probably forgot to specify the protocol but entered absolute url, add protocol (assume http) url = 'http://' + _url; } } else { if (protocol && this.linkType !== 'external') { // we got back an absolute link, convert to relative url = _url.replace(/^.+\?/, '?'); } else if (!protocol && _url.indexOf('/') !== 0 && !_url.match(relativeUrlExpr)) { // no file and no relative url, probably user input without protocol, assume http url = 'http://' + _url; } else { url = _url; } } } return url; }, /* * Gets the active tab for rendering and sets options options based on link type * @param {String} _linkType Type of the link that is currently edited. * Can be either 'filemanager', 'additional', 'external' or 'tree' * @return {Integer} active index * @private */ _getActiveTabAndSetOptions: function (_linkType) { switch (_linkType) { case 'filemanager': this.filemanagerView.setPreviewImage(this.linkUrl); return 1; case 'additional': this.additionalPagesView.setCheckedRadio(this.linkUrl); return 2; case 'external': this.externalUrlView.setUrl(this.linkUrl); return 3; case 'anchor': return 4; case 'tree': return 0; default: // if link was created before new linkpicker implementation (url is given), return external tab to show url var tab; if (this.linkUrl) { tab = 3; this.externalUrlView.setUrl(this.linkUrl); } else { // return last active tab tab = storage.localStorage('linkpickerLastActiveTab') || 0; } return tab; } }, /* * Clearing all view specific data like filemanager preview, selected radio options or entered text * except for the specified view/link type (used when making changes to other tabs) * @param {String} _givenLinkType [LinkType whose view data should NOT be cleared Can be either 'filemanager', 'additional', 'external', 'anchor' or 'tree'] * @private */ _clearViewDataExceptFor: function (_givenLinkType) { switch (_givenLinkType) { case 'filemanager': this.pageTreeView.clearLinkData(); this.additionalPagesView.clearLinkData(); this.externalUrlView.clearLinkData(); this.anchorView.clearLinkData(); break; case 'additional': this.filemanagerView.clearLinkData(); this.pageTreeView.clearLinkData(); this.externalUrlView.clearLinkData(); this.anchorView.clearLinkData(); break; case 'external': this.filemanagerView.clearLinkData(); this.additionalPagesView.clearLinkData(); this.pageTreeView.clearLinkData(); this.anchorView.clearLinkData(); break; case 'anchor': this.filemanagerView.clearLinkData(); this.additionalPagesView.clearLinkData(); this.pageTreeView.clearLinkData(); this.externalUrlView.clearLinkData(); break; default: //tree this.filemanagerView.clearLinkData(); this.additionalPagesView.clearLinkData(); this.externalUrlView.clearLinkData(); this.anchorView.clearLinkData(); } }, /* * Sets the text of the chosen link * @param {String} _text title to be set * @private */ _setLinkText: function (_text) { var text = this.linkTextView.getLinkText(); // input is considered not changed when its value is empty if (this.is_linkTextChanged && text !== '') { this.linkText = text; } else { this.linkTextView.setLinkText(_text); this.linkText = _text; this.is_linkTextChanged = false; } }, /* * Sets the url and title attribute of the chosen link * @param {String} _url url to be set */ _setLinkUrl: function (_url) { this.linkUrl = _url; this.linkTextView.setLinkUrl(_url); }, /* * Sets the target of the link (open in new or same window) * @param {Boolean} _is_targetBlank Whether link should be opened in new window * @private */ _setLinkTarget: function (_is_targetBlank) { this.is_linkTargetBlank = _is_targetBlank; this.linkTextView.setLinkTarget(_is_targetBlank); } }); return new View(); });