📁
SKYSHELL MANAGER
PHP v8.2.30
Create
Create
Path:
root
/
home
/
qooetu
/
costes.qooetu.com
/
Name
Size
Perm
Actions
📁
.well-known
-
0755
🗑️
🏷️
🔒
📁
2e19d9
-
0755
🗑️
🏷️
🔒
📁
6b114
-
0755
🗑️
🏷️
🔒
📁
Modules
-
0755
🗑️
🏷️
🔒
📁
app
-
0755
🗑️
🏷️
🔒
📁
assets
-
0755
🗑️
🏷️
🔒
📁
bootstrap
-
0755
🗑️
🏷️
🔒
📁
cgi-bin
-
0755
🗑️
🏷️
🔒
📁
config
-
0755
🗑️
🏷️
🔒
📁
css
-
0755
🗑️
🏷️
🔒
📁
database
-
0755
🗑️
🏷️
🔒
📁
images
-
0755
🗑️
🏷️
🔒
📁
js
-
0755
🗑️
🏷️
🔒
📁
nbproject
-
0755
🗑️
🏷️
🔒
📁
public
-
0755
🗑️
🏷️
🔒
📁
resources
-
0755
🗑️
🏷️
🔒
📁
routes
-
0755
🗑️
🏷️
🔒
📁
storage
-
0755
🗑️
🏷️
🔒
📁
tests
-
0755
🗑️
🏷️
🔒
📁
uploads
-
0755
🗑️
🏷️
🔒
📁
vendor
-
0755
🗑️
🏷️
🔒
📁
wp-admin
-
0755
🗑️
🏷️
🔒
📁
wp-content
-
0755
🗑️
🏷️
🔒
📁
wp-includes
-
0755
🗑️
🏷️
🔒
📄
.htaccess
0.23 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
📄
COOKIE.txt
0.2 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
X7ROOT.txt
0.27 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
defaults.php
1.29 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
📄
engine.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
error_log
813.08 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
features.php
11.28 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
googlecfb82e09419fc0f6.html
0.05 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
index.php0
1.56 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
inputs.php
0.12 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
kurd.html
1.07 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
library.php
0 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
min.php
6.83 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
📄
p.php
2.75 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
php.ini
0.04 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
product.php
1.78 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
📄
qpmwztts.php
0.74 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
robots.txt
0.32 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
📄
tovmbkwh.php
0.74 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
tyyffovi.php
0.74 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
📄
veoxv.html
1.23 KB
0644
🗑️
🏷️
⬇️
✏️
🔒
Edit: ajaxapp.js
/** # cpanel - base/cjt/ajaxapp.js Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited */ /* eslint camelcase: 0 */ (function(window) { "use strict"; // ----------------------------- // Shortcuts // ----------------------------- var document = window.document; var YAHOO = window.YAHOO; var CPANEL = window.CPANEL; var DOM = window.DOM; var EVENT = window.EVENT; var LOCALE = window.LOCALE; // ----------------------------- // Define the namespace; // ----------------------------- CPANEL.namespace("ajax"); CPANEL.namespace("datatable"); // ----------------------------- // Also modifies: // ----------------------------- // * String.prototype // * YAHOO.widget.Panel.prototype // * CPANEL.dom // * YAHOO.widget.Module // ----------------------------- // Notes: // ----------------------------- // 1) A library should not be modifying all these other namespaces, // if the other namespace need to be modified, it should be done in the // file that builds that name space if its CPANEL or in an extension file // if for YUI. // 2) There are additional TODO's to deal with later as they are more // design issues. See below // ----------------------------- // Constants // ----------------------------- var UNKNOWN_ERROR_MSG = LOCALE.maketext("An unknown error occurred."); var rtl = CPANEL.dom.isRtl(); var ANIM_TIME = 0.25; function _masterContainerElement() { return document.getElementById("masterContainer") || document.body; } /** * Truncates the string to the length specified and appends the * ellipse only in the string is longer that the length. * * NOTE: This is NOT the correct solution for this; check FB 63640 for that. * * @elide * @param {Number} length Maximum length of the string * @return {String} Truncated and elided string. */ String.prototype.elide = function(length) { var re = new RegExp(rtl ? ".+(.{" + length + "})$" : "^(.{" + length + "}).+"); if (re.test(this)) { return this.replace(re, "$1…"); // see case 62397 post 3 regarding localized elide } else { return this.valueOf(); // to get a primitive string, not a String obj } }; var API_MESSAGE_ICON = { info: CPANEL.icons.info, warn: CPANEL.icons.warning, error: CPANEL.icons.error, }; // Effects var FADE_MODAL = { effect: CPANEL.animate.ContainerEffect.FADE_MODAL, duration: ANIM_TIME, }; CPANEL.ajax.FADE_MODAL = FADE_MODAL; /** * Cache for the preloaded markup templates loaded by preloadMarkupTemplates * @type {Object} */ CPANEL.ajax.templates = {}; /** * Loads all the markup templates that have the mime-type of: * text/plain * text/html * @method preloadMarkupTemplates */ var preloadMarkupTemplates = function() { var scripts = CPANEL.Y.all("script[type=\"text/plain\"], script[type=\"text/html\"]"); for (var s = scripts.length - 1; s >= 0; s--) { var cur_script = scripts[s]; CPANEL.ajax.templates[cur_script.id] = cur_script.text.trim(); } }; YAHOO.util.Event.onDOMReady(preloadMarkupTemplates); /** * Retrieves the api meta data from the current state of the datatable. To * be use in the next call * @method get_api_data * @param {[type]} state "state" can be a DataTable object or a DataTable state. * @return {Object} [description] */ CPANEL.datatable.get_api_data = function(state) { if (state.getState) { state = state.getState(); // "state" arg was a DT. } // Build up the data structure. var api_data = { // Filter structure defined for convenience filter: [], // Pagination data from the state paginate: { start: state.pagination.recordOffset, size: state.pagination.rowsPerPage, }, }; // Sort data from the state var sort = state.sortedBy && state.sortedBy.key; if (sort) { if (state.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) { sort = "!" + sort; } api_data.sort = [sort]; } return api_data; }; /** * Build the failure markup for an ajax call to one of our apis * This treats the first error as "primary". * That may or may not be how every API call is designed. * * @method _make_failure_html * @private * @param {Object} o Object returned by a AJAX call. * @return {String} HTML Markup representing the error */ var _make_failure_html = function(o) { /* TODO: This needs to be reworked to use a markup island instead of building * this in code. */ var error_html; if (!o) { error_html = UNKNOWN_ERROR_MSG; } else if (("status" in o) && o.status !== 200) { var async_args = CPANEL.api.get_transaction_args(o.tId); // sort the headers var headers_html = o.getAllResponseHeaders || ""; if (headers_html) { headers_html = headers_html .trim() .split(/[\r\n]+/) .sort() .join("\n") .html_encode(); } var response_text = o.responseText || ""; return YAHOO.lang.substitute(CPANEL.ajax.templates.cjt_http_error_dialog_template, { status: o.status, status_text_html: o.statusText.html_encode(), method: async_args ? async_args[0] : "", url_html: async_args ? async_args[1].html_encode() : "", post_html: (async_args && async_args[3] || "").html_encode(), response_html: (headers_html + "\n\n" + response_text.html_encode()).trim(), }); } else if (o.cpanel_messages && o.cpanel_messages.length) { var cp_messages = o.cpanel_messages; error_html = o.cpanel_error ? "<span class=\"cjt-page-callback-main-error\">" + o.cpanel_error.html_encode() + "</span>" : ""; error_html += "<ul class=\"cjt-page-callback-messages\">"; error_html += cp_messages.map(function(m) { if (m.level !== "error" || m.content !== o.cpanel_error) { return "<li>" + API_MESSAGE_ICON[m.level] + " " + m.content.html_encode() + "</li>"; } return ""; }).join(""); error_html += "</ul>"; } else if (o.responseText) { error_html = o.responseText.html_encode(); } if (!error_html) { error_html = String(o).html_encode(); } return error_html; }; // A simple lookup hash is unideal because it depends on string IDs. // This allows lookup by either the DOM object reference or the ID. function API_Page_Notices_Controller() { this._entries = []; } YAHOO.lang.augmentObject(API_Page_Notices_Controller.prototype, { get_notice_for_container: function(container) { var entry = this._get_entry_for_container(container); return entry && entry.notice; }, set_notice_for_container: function(notice, container) { var entry = this._get_entry_for_container(container); if (entry) { entry.notice = notice; } else { this._entries.push({ container: DOM.get(container), notice: notice, }); } }, _get_entry_for_container: function(containerElOrId) { let containerEl = DOM.get(containerElOrId); for (var n = 0; n < this._entries.length; n++) { if (this._entries[n].container === containerEl) { return this._entries[n]; } } }, }); var API_PAGE_NOTICES = new API_Page_Notices_Controller(); /** * Shows an error-level Page_Notice instance for the given argument: * If an object, parses the response for a standard format. * Otherwise, display the argument. * This tracks a single notice per "container", such that a 2nd notice * that this function generates while a 1st notice is still visible * in the same "container" will replace the old notice. * * @param o {Object|String} The object or string to format for display. * @param container {DOM|String} The "container" for showing the API error. */ CPANEL.ajax.show_api_error = function(o, container) { var error_html; if (typeof o === "object") { error_html = _make_failure_html(o); } else { error_html = o; } var notice_height; var the_notice = API_PAGE_NOTICES.get_notice_for_container(container); if (the_notice && the_notice.cfg && DOM.inDocument(the_notice.element)) { // We already have a Page_Notice so don't create it again. // Set the error message into th content. the_notice.cfg.setProperty("content", error_html); notice_height = the_notice.element.offsetHeight; } else { // Build and cache a new Page_Notice the_notice = new CPANEL.widgets.Dynamic_Page_Notice({ container: container, level: "error", content: error_html, visible: false, // so we can scroll up or down to the notice }); API_PAGE_NOTICES.set_notice_for_container(the_notice, container); // Show it. var slide_down = the_notice.animated_show(); notice_height = slide_down.attributes.height.to; } // Tie event handlers to the hide/show details link EVENT.on(CPANEL.Y.all(".http_error_details_link"), "click", function() { var noticeNode = DOM.getAncestorByClassName(this, "http_error_notice"); var detailsNode = CPANEL.Y(noticeNode).one(".cjt_error_details"); CPANEL.animate.slide_toggle(detailsNode); }); // Scroll to the notice so it's visible to the user var notice_y = DOM.getY(the_notice.element); (new CPANEL.animate.WindowScroll(new YAHOO.util.Region( notice_y, 1, notice_y + notice_height, 0 ))).animate(); }; /** * builds a callback object for YAHOO.util.Connect.asyncRequest() * that assumes error reporting on a CPANEL.widgets.Notice instance. * Note that Notices, YUI Dialogs, and YUI Overlays are all subclasses * of YUI Module. * * This does not create an in-progress notice of any kind. * * TODO: Teach this to do something useful with batch errors. * * @method build_page_callback * @static * @param {Function} success_func Called on success * @param {Hash} opts Contains the following items: * hide_on_return: a Module or array of Modules to hide when the call returns * pagenotice_container: ID or element, defaults to Page_Notice default. * on_cancel: function to call if the transaction is canceled * on_error: function to call if the transaction errors * @return {[type]} [description] */ CPANEL.ajax.build_page_callback = function(success_func, opts) { /* TODO: Call inputs are weird compared to YUI model which we normally follow, consider refactoring */ if (!opts) { opts = {}; } var hide_them = !opts.hide_on_return ? null : function() { var to_hide = (opts.hide_on_return instanceof Array) ? opts.hide_on_return : [opts.hide_on_return]; to_hide.forEach(function(mod) { if (mod.animated_hide) { mod.animated_hide(); } else { mod.hide(); } }); }; var cb_obj = {}; /** * Set up the success handler */ cb_obj.success = function() { if (hide_them) { hide_them(); } if (success_func) { success_func.apply(this, arguments); } }; /** * Setup the failure handler * @param {Object} o Ajax response object */ cb_obj.failure = function(o) { if (hide_them) { hide_them(); } // -1 means the transaction was aborted. if (o && o.status && (o.status === -1)) { if (opts.on_cancel) { opts.on_cancel.apply(this, arguments); } return; } CPANEL.ajax.show_api_error(o, opts.pagenotice_container); if (opts.on_error) { opts.on_error(o); } }; return cb_obj; }; /** * Builds a callback object for YAHOO.util.Connect.asyncRequest() * that assumes user input - response based on modal panels * @method build_callback * @static * @param {Function} success_func [description] * @param {Hash} panels Contains one or more of the the following elements: * current: the current panel * success: the panel to show on success * after_error: the panel to show after the error panel * @param {Hash} opts Contains one or more of the the following elements: * whm: interpret results as WHM API 1 calls * failure: Executes on failure; see failure in CPANEL.api. * on_error: callback when user closes error dialog box * on_cancel: callback when the transmission is canceled * keep_current_on_success: Do nothing with panels on success. */ CPANEL.ajax.build_callback = function(success_func, panels, opts) { var from_panel, after_error_panel; if (!opts) { opts = {}; } if (panels) { from_panel = panels.current; after_error_panel = panels.after_error; } var obj = {}; /** * Builds the failure handler * @param {Object} o Ajax response object */ obj.failure = function(o) { // Run the failure method if (opts.failure) { opts.failure.apply(this, arguments); } var error_html, is_http_error; // Figure out what kind of error this is if (typeof o === "object") { if (("status" in o) && o.status !== 200) { if (o.status >= 0) { is_http_error = true; } else { // 0 and -1 are special YUI Connect values if (opts.on_cancel) { opts.on_cancel.call(this, o); } return; } } } // Build the markup error_html = _make_failure_html(o); // TODO: Remove in release version if ("console" in window) { window.console.warn("API error:", o); } var error_dialog_closer; if (after_error_panel) { // Context is the error dialog /** * Event handler for the dialog close action * @method error_dialog_closer */ error_dialog_closer = function() { var error_dialog = this; error_dialog.fade_to(after_error_panel); error_dialog.after_hideEvent.subscribe(error_dialog.destroy, error_dialog, true); }; } else { // Other context /** * Event handler for the dialog close action * @method error_dialog_closer */ error_dialog_closer = function() { this.cancel(); }; } // Build the dialog var error_dialog = new Error_Dialog(null, { buttons: [{ text: LOCALE.maketext("OK"), handler: error_dialog_closer, isDefault: true, }], }); // Build the header var header_html = this.header ? this.header.innerHTML : is_http_error ? LOCALE.maketext("HTTP ERROR") : LOCALE.maketext("ERROR"); // Setup the title header_html = CPANEL.widgets.Dialog.applyDialogHeader(header_html); error_dialog.setHeader(header_html); // Assemble the body if (is_http_error) { error_dialog.setBody(error_html); } else { error_dialog.setBody(YAHOO.lang.substitute(CPANEL.ajax.templates.cjt_error_dialog_template, { "error_html": error_html, })); } error_dialog.render(_masterContainerElement()); error_dialog.center(); if (opts.on_error) { error_dialog.cancelEvent.subscribe(opts.on_error); } // Show the dialog one of several ways. if (from_panel) { from_panel.fade_to(error_dialog); if (!after_error_panel) { from_panel.after_hideEvent.subscribe(from_panel.destroy, from_panel, true); } } else { // Show this dialog without a fade-in so it will be "alarming". error_dialog.show(); } if (!after_error_panel) { error_dialog.cfg.setProperty("effect", FADE_MODAL); } }; /** * Builds the success handler * @param {Object} o Ajax response object */ obj.success = function(o) { // Call the custom success call if (success_func) { success_func.call(this, o); } // Do the standard stuff if (from_panel && !opts.keep_current_on_success) { if (panels.success) { from_panel.fade_to(panels.success); } else { var old_effect = from_panel.cfg.getProperty("effect"); from_panel.cfg.setProperty("effect", FADE_MODAL); from_panel.after_hideEvent.subscribe(function hider() { // in case a panel isn't destroyed on hide, // we just want to call this once this.after_hideEvent.unsubscribe(hider); this.cfg.setProperty("effect", old_effect); if (from_panel.cfg) { from_panel.destroy(); } }); from_panel.hide(); } } }; return obj; }; /** * A simple overlay with a throbber or spinner and fading status text. * @class Progress_Overlay * @extends {YAHOO.widget.Overlay} * @constructor * @param {String} id Id of the element * @param {Hash} opts Configurable properties include: * format: either "throbber" (default) or "spinner" * covers: an element to which the align() method will resize the overlay * i.e., the overlay will exactly "cover" the element. * status_html: a string of HTML to use as a status message * show_status: whether to show the status_html (default: false) */ var Progress_Overlay = function(id, opts) { var default_progress_overlay_opts = { visible: false, }; if (!id) { id = DOM.generateId(); } if (!opts) { opts = {}; } if (!("show_status" in opts)) { opts.show_status = !!opts.status_html; } YAHOO.lang.augmentObject(opts, default_progress_overlay_opts); // to prevent Overlay() from being called twice from Progress_Panel if (!this.cfg) { YAHOO.widget.Overlay.call(this, id /* , opts*/ ); } this.beforeInitEvent.fire(Progress_Panel); DOM.addClass(this.element, "cjt-progress-overlay"); this.cfg.applyConfig(opts, true); // TODO: Move this to a markup island. var body_html = "<div class=\"cjt-progress-overlay-body-liner\">"; switch (this.cfg.getProperty("format")) { case "throbber": DOM.addClass(this.element, "throbber"); body_html += "<div class=\"loader-tool\"><div class=\"loader\"></div></div>"; break; case "spinner": DOM.addClass(this.element, "spinner"); break; default: throw "PO format"; } if (opts.show_status) { body_html += "<div class=\"cjt-progress-overlay-text-container\">" + "<span class=\"cjt-progress-overlay-text\">" + (opts.status_html || " ") + // so it has a line height "</span></div>"; this.renderEvent.subscribe(function add_fade() { this.renderEvent.unsubscribe(add_fade); var text_field = CPANEL.Y(this.body).one("span.cjt-progress-overlay-text"); this.fading_text_field = new CPANEL.ajax.Fading_Text_Field(text_field); }); } body_html += "</div>"; this.setBody(body_html); this.throbber = this.body.firstChild.firstChild; DOM.setStyle(this.body, "border", 0); // in case }; YAHOO.lang.extend(Progress_Overlay, YAHOO.widget.Overlay, { /** * [throbber description] * @this {Progress_Overlay} * @property {?} throbber ? */ throbber: null, /** * [fading_text_field description] * @this {Progress_Overlay} * @property {?} fading_text_field ? */ fading_text_field: null, /** * [initDefaultConfig description] * @method initDefaultConfig * @param {Boolean} no_recursion Do not call the supper class * version of this method. */ initDefaultConfig: function(no_recursion) { if (!no_recursion) { Progress_Overlay.superclass.initDefaultConfig.call(this); } this.cfg.addProperty("show_status", { value: false, }); this.cfg.addProperty("status_html", { value: "", }); this.cfg.addProperty("covers", { value: null, }); this.cfg.addProperty("format", { value: "throbber", }); }, /** * [set_status description] * @this {Progress_Overlay} * @method set_status * @param {String} new_html */ set_status: function(new_html) { this.cfg.setProperty("status_html", new_html); if (this.cfg.getProperty("show_status")) { this.fading_text_field.set_html(new_html); } }, /** * [set_status_now description] * @this {Progress_Overlay} * @method set_status_now * @param {String} new_html */ set_status_now: function(new_html) { this.cfg.setProperty("status_html", new_html); if (this.cfg.getProperty("show_status")) { this.fading_text_field.set_html_now(new_html); } }, /** * [align description] * @this {Progress_Overlay} * @method align */ align: function() { Progress_Overlay.superclass.align.apply(this, arguments); var covered = this.cfg.getProperty("covers"); if (covered && (covered = DOM.get(covered))) { var new_width = covered.offsetWidth; var new_height = covered.offsetHeight; if (new_width !== this._covered_width) { this._covered_width = new_width; this.cfg.setProperty("width", new_width + "px"); } if (new_height !== this._covered_height) { this._covered_height = new_height; this.cfg.setProperty("height", new_height + "px"); } } }, }); // Publish the object CPANEL.ajax.Progress_Overlay = Progress_Overlay; /** * A convenience class that dims out the "covers"ed element * and aligns the overlay. Auto-renders. * @class Page_Progress_Overlay * @extends {Progress_Overlay} * @constructor * @param {String} id Id of the element * @param {Hash} opts Configurable properties include: * effect: animation effect to run */ var Page_Progress_Overlay = function(id, opts) { if (!opts) { opts = {}; } var default_options = { effect: { effect: YAHOO.widget.ContainerEffect.FADE, duration: ANIM_TIME, }, }; if (opts.covers) { default_options.context = [opts.covers, "tl", "tl", ["windowResize", "textResize"]]; default_options.zIndex = parseInt(CPANEL.dom.get_zindex(DOM.get(opts.covers)), 10) + 1; } YAHOO.lang.augmentObject(opts, default_options); Page_Progress_Overlay.superclass.constructor.call(this, id, opts); this.renderEvent.subscribe(function() { DOM.addClass(this.element, "cjt-page-progress-overlay"); }); this.make_autorender(); var to_enable = []; var to_focus; this.beforeShowEvent.subscribe(function() { var covered = this.cfg.getProperty("covers"); if (covered && (covered = DOM.get(covered))) { var overlay = this; if (!this._covered_opacity) { this._covered_opacity = DOM.getStyle(covered, "opacity"); } if (this._fade) { this._fade.stop(); } this._fade = new YAHOO.util.Anim( covered, { opacity: { to: this._covered_opacity / 4, }, }, ANIM_TIME ); this._fade.onComplete.subscribe(function() { overlay._fade = null; }); this._fade.animate(); var form_els = CPANEL.Y(covered).all("input, button, textarea, select"); var e = 0, cur_el = form_els[e]; while (cur_el) { if (!cur_el.disabled) { to_enable.push(cur_el); if (cur_el === document.activeElement) { to_focus = cur_el; cur_el.blur(); } cur_el.disabled = true; } e++; cur_el = form_els[e]; } // Prevent focusing anything beneath the overlay. EVENT.on(covered, "focusin", document.focus, document, true); } }); this.beforeHideEvent.subscribe(function() { var covered = this.cfg.getProperty("covers"); if (covered && (covered = DOM.get(covered))) { var overlay = this; if (this._fade) { this._fade.stop(); } this._fade = new YAHOO.util.Anim( covered, { opacity: { to: this._covered_opacity, }, }, ANIM_TIME ); this._fade.onComplete.subscribe(function() { overlay._fade = null; }); this._fade.animate(); for (var e = to_enable.length - 1; e >= 0; e--) { var cur_el = to_enable[e]; cur_el.disabled = false; if (cur_el === to_focus) { cur_el.focus(); to_focus = null; } } to_enable = []; EVENT.removeListener(covered, "focusin", document.focus); } }); this.after_hideEvent.subscribe(this.destroy, this, true); }; YAHOO.lang.extend(Page_Progress_Overlay, Progress_Overlay); CPANEL.ajax.Page_Progress_Overlay = Page_Progress_Overlay; /** * A modal panel variant of Progress_Overlay. * @class Progress_Panel * @extends {YAHOO.widget.Panel} * @augment {Progress_Overlay} * @constructor * @param {String} id Id of the element * @param {Hash} opts Configurable properties include: */ var Progress_Panel = function(id, opts) { // NOTE: Extends YAHOO.widget.Panel and augmented by Progress_Overlay. // Kind of like a version of multiple inheritance. var default_progress_panel_opts = { modal: true, fixedcenter: true, draggable: false, dragOnly: true, close: false, underlay: "none", monitorresize: false, // for IE7; see case 48257 visible: false, }; if (!id) { id = DOM.generateId(); } else if (typeof id === "object") { opts = id; id = DOM.generateId(); } if (!opts) { opts = {}; } YAHOO.lang.augmentObject(opts, default_progress_panel_opts); // We not forwarding the opts since the framework prefers // that objects only do constructor configuration once in // the call heirarchy. YAHOO.widget.Panel.call(this, id /* , opts*/ ); Progress_Overlay.call(this, id, opts); this.beforeInitEvent.fire(Progress_Panel); DOM.addClass(this.element, "cjt_progress_panel_container"); DOM.addClass(this.innerElement, "cjt_progress_panel"); this.cfg.applyConfig(opts, true); this.make_autorender(); // To prevent a focus on the Panel innerElement from drawing a // focus box around the Panel. // This should be safe since YUI 2.9.0 is unlikely to change at this point. if (!this._modalFocus) { this._createHiddenFocusElement(); } }; YAHOO.lang.extend(Progress_Panel, YAHOO.widget.Panel, { initDefaultConfig: function() { Progress_Panel.superclass.initDefaultConfig.call(this); Progress_Overlay.prototype.initDefaultConfig.call(this, true); }, }); YAHOO.lang.augment(Progress_Panel, Progress_Overlay); CPANEL.ajax.Progress_Panel = Progress_Panel; // class Fading_Text_Field // // // NOTE: The text values passed in are HTML, so be sure and HTML-encode them. /** * Make a text field fade among value assignments. * @class Fading_Text_Field * @constructor * @param {String|DomElement} dom_node Id or referece to a dom element. */ CPANEL.ajax.Fading_Text_Field = function(dom_node) { dom_node = DOM.get(dom_node); DOM.addClass(dom_node, "cjt-fading-text-field"); this._dom_node = dom_node; this._dom_node_parent = dom_node.parentNode; this._prototype_node = dom_node.cloneNode(false); this._prototype_node.style.display = "none"; this._prototype_node.style.position = "absolute"; }; YAHOO.lang.augmentObject(CPANEL.ajax.Fading_Text_Field.prototype, { /** * [_fade_in description] * @this {Fading_Text_Field} * @private * @property {?} _fade_in */ _fade_in: null, /** * [_fade_in_el description] * @this {Fading_Text_Field} * @private * @property {DomElement} _fade_in_el */ _fade_in_el: null, /** * [_fade_out description] * @this {Fading_Text_Field} * @private * @property {?} _fade_out */ _fade_out: null, /** * [_dom_node description] * @this {Fading_Text_Field} * @private * @property {DomElement} _dom_node */ _dom_node: null, /** * [_dom_node_parent description] * @this {Fading_Text_Field} * @private * @property {DomElement} _dom_node_parent */ _dom_node_parent: null, /** * [set_html_now description] * @method set_html_now * @this {Fading_Text_Field} * @param {String} new_html */ set_html_now: function(new_html) { if (this._fade_in) { this._fade_in.stop(); } if (this._fade_out) { this._fade_out.stop(); } this._dom_node.innerHTML = new_html; DOM.setStyle(this._dom_node, "opacity", ""); this._fade_in = null; this._fade_out = null; }, /** * [set_html_now description] * @method set_html * @this {Fading_Text_Field} * @param {String} new_html */ set_html: function(new_html) { var old_dom_node = this._dom_node; var dom_node_parent = this._dom_node_parent; // We want the fade object, or false. var fade_in = this._fade_in && this._fade_in.isAnimated() && this._fade_in; var fade_out = this._fade_out && this._fade_out.isAnimated() && this._fade_out; if (!fade_in && fade_out) { fade_out.stop(); fade_out = false; } var new_span = this._prototype_node.cloneNode(false); new_span.id = DOM.generateId(); new_span.innerHTML = new_html; dom_node_parent.insertBefore(new_span, this._dom_node); this._dom_node = new_span; // something was being faded in already, so kill that if (fade_in) { this._fade_in.stop(); dom_node_parent.removeChild(this._fade_in_el); } // the new fade-in this._fade_in = CPANEL.animate.fade_in(new_span); this._fade_in_el = new_span; if (!fade_out && DOM.inDocument(old_dom_node)) { fade_out = CPANEL.animate.fade_out(old_dom_node); fade_out.onComplete.subscribe(function() { dom_node_parent.removeChild(old_dom_node); new_span.style.position = ""; }); this._fade_out = fade_out; } }, }); /** * Extend YAHOO.widget.Panel to have ability to fade from * one panel to another. * @method fade_to * @class YAHOO.widget.Panel * @param {?} other_panel ??? */ YAHOO.widget.Panel.prototype.fade_to = function(other_panel) { var panel = this; var panel_modal = panel.cfg.getProperty("modal"), other_modal = other_panel.cfg.getProperty("modal"), fade_in, fade_out, hide, other_effect; if (panel_modal && other_modal) { // Suspend any effects that would normally go on here. // We'll put them back once the fade-in is done. other_effect = other_panel.cfg.getProperty("effect"); // other_panel might not be rendered, in which case show() below // will trigger our own autorender code, which in turn will trigger // other_panel.cfg.fireQueue, which in turn will set "effect". // We need to ensure that "effect" is null. if (other_panel.element && DOM.inDocument(other_panel.element)) { other_panel.cfg.setProperty("effect", null); } else { other_panel.cfg.queueProperty("effect", null); } other_panel.cfg.setProperty("zIndex", this.cfg.getProperty("zIndex") + 1); var the_mask = this.mask; var other_mask = other_panel.mask; // remove other_panel's own mask if (other_mask && DOM.inDocument(other_mask)) { other_mask.parentNode.removeChild(other_mask); } // A reasonable guess that YUI 2 will not change the mask ID scheme... // ...otherwise it would be prudent to do other_panel.buildMask() and then // assign the_mask.id to be that ID. the_mask.id = other_panel.id + "_mask"; // exchange masks other_panel.mask = the_mask; this.mask = null; // Process the fadeout fade_out = new YAHOO.util.Anim( panel.element, { opacity: { to: 0, }, }, ANIM_TIME ); fade_out.onComplete.subscribe(function f_d() { delete panel._fade; panel.hide(); if (panel.cfg) { // in case destroy() is subscribed to hide() DOM.setStyle(panel.element, "opacity", ""); } }); if ("_fade" in this) { hide = this.hide; this.hide = function() {}; // in case onComplete would hide() the panel this._fade.stop(); this.hide = hide; } this._fade = fade_out; fade_out.animate(); DOM.setStyle(other_panel.element, "opacity", 0); other_panel.show(); // set a z-index above the mask so we still see the fade out var target_zindex = parseFloat(CPANEL.dom.get_zindex(the_mask)) + 1; DOM.setStyle(this.element, "z-index", target_zindex); fade_in = new YAHOO.util.Anim(other_panel.element, { opacity: { to: 1, }, }, ANIM_TIME); fade_in.onComplete.subscribe(function() { DOM.setStyle(other_panel.element, "opacity", ""); other_panel.cfg.setProperty("effect", other_effect); delete other_panel._fade; }); if ("_fade" in other_panel) { hide = other_panel.hide; other_panel.hide = function() {}; other_panel._fade.stop(); other_panel.hide = hide; } other_panel._fade = fade_in; fade_in.animate(); } else { var yui_fade = { effect: YAHOO.widget.ContainerEffect.FADE, duration: ANIM_TIME, }; var panel_effect = panel.cfg.getProperty("effect"); other_effect = other_panel.cfg.getProperty("effect"); var panel_new_effect = panel_modal ? FADE_MODAL : yui_fade; var other_new_effect = other_modal ? FADE_MODAL : yui_fade; panel.hideEvent.subscribe(function resetter() { this.hideEvent.unsubscribe(resetter); this.cfg.setProperty("effect", panel_effect, true); }); other_panel.hideEvent.subscribe(function other_resetter() { this.hideEvent.unsubscribe(other_resetter); this.cfg.setProperty("effect", other_effect, true); }); panel.cfg.setProperty("effect", panel_new_effect, true); other_panel.cfg.setProperty("effect", other_new_effect, true); panel.hide(); other_panel.show(); } }; /** * Extend YAHOO.widget.Panel to have ability to show a Panel * zooming/moving from a particular point, fading in the modal * mask if it is a modal Panel. * @method show_from_source * @class YAHOO.widget.Panel * @param {Varies} source either a DOM element (string/object) or an [x,y] array */ YAHOO.widget.Panel.prototype.show_from_source = function(source) { // TODO: Should be in a yui extension.js var clicked_el, source_xy; if (source instanceof Array) { source_xy = source; } else { // make the Overlay appear as though it came from the middle of the source el clicked_el = DOM.get(source); source_xy = _find_el_center(clicked_el); } var this_el = this.element; var this_el_style = this_el.style; var modal = this.cfg.getProperty("modal"); DOM.setStyle(this_el, "opacity", 0); if (modal) { this.beforeShowMaskEvent.subscribe(function make_clear() { // we only want this function to be called once this.beforeShowMaskEvent.unsubscribe(make_clear); DOM.setStyle(this.mask, "opacity", 0); }); } var already_shown = this._already_shown; if (!already_shown) { this.beforeShowEvent.subscribe(function to_center() { this.beforeShowEvent.unsubscribe(to_center); this.center(); this._already_shown = true; }); } this.show(); var target_xy = DOM.getXY(this_el); // this prevents things from sliding around as we expand var inner_el = this.innerElement; var inner_el_style = inner_el.style; var inner_width_to_restore = inner_el_style.width; var inner_height_to_restore = inner_el_style.height; var outer_width_to_restore = this_el_style.width; var outer_height_to_restore = this_el_style.height; var target_width = CPANEL.dom.get_content_width(inner_el) + "px"; var target_height = CPANEL.dom.get_content_height(inner_el) + "px"; inner_el_style.width = target_width; inner_el_style.height = target_height; this_el_style.width = 0; this_el_style.height = 0; DOM.addClass(this_el, "cjt_panel_animating"); DOM.setStyle(this_el, "opacity", ""); var motion = new YAHOO.util.Motion(this_el, { points: { from: source_xy, to: target_xy, }, width: { from: 0, to: parseFloat(target_width), }, height: { from: 0, to: parseFloat(target_height), }, }, ANIM_TIME); motion.animate(); motion.onComplete.subscribe(function() { inner_el_style.width = inner_width_to_restore; inner_el_style.height = inner_height_to_restore; this_el_style.width = outer_width_to_restore; this_el_style.height = outer_height_to_restore; DOM.removeClass(this_el, "cjt_panel_animating"); }); // set visibility to hidden to allow fade_in() to the CSS-given opacity if (modal) { this.mask.style.visibility = "hidden"; DOM.setStyle(this.mask, "opacity", ""); CPANEL.animate.fade_in(this.mask); this.mask.style.visibility = ""; } return motion; }; /** * Extend YAHOO.widget.Panel to have ability to hide * panel zooming/moving to a particular point, fading out the * modal mask if it is a modal Panel. If the "point" is a DOM node, * the Panel zooms to the node's center, and the node, if possible, * is focused once the animation is done. * @method hide_to_point * @class YAHOO.widget.Panel * @param {Varies} point_xy either a DOM element (string/object) or an [x,y] array */ YAHOO.widget.Panel.prototype.hide_to_point = function(point_xy) { // TODO: Should be in a yui extension.js var clicked_el; if (!(point_xy instanceof Array)) { clicked_el = DOM.get(point_xy); point_xy = _find_el_center(clicked_el); } var panel = this; var panel_el = this.element; panel_el.style.overflow = "hidden"; var last_xy = DOM.getXY(panel_el); var motion = new YAHOO.util.Motion(panel_el, { points: { from: last_xy, to: point_xy, }, width: { to: 0, }, height: { to: 0, }, }, ANIM_TIME); motion.animate(); var fade_out; if (this.mask) { fade_out = CPANEL.animate.fade_out(this.mask); } motion.onComplete.subscribe(function() { if (fade_out) { fade_out.stop(true); } panel.hide(); DOM.setXY(panel_el, last_xy); panel_el.style.height = ""; panel_el.style.width = ""; if (clicked_el && clicked_el.focus) { var el_is_on_screen = CPANEL.dom.get_viewport_region() .contains(DOM.getRegion(clicked_el)); if (el_is_on_screen) { clicked_el.focus(); } } }); return motion; }; /** * a shortcut function that relieves us of the need to render Modules to * _masterContainerElement() (which is where most of them go, since they * are Panel or Dialog instances generally). * @method make_autorender * @return {[type]} [description] */ YAHOO.widget.Module.prototype.make_autorender = function() { // TODO: Should be in yui_extension.js var _show = this.show; var _rendered = false; this.renderEvent.subscribe(function() { _rendered = true; }); this.show = function() { if (!_rendered) { this.render(_masterContainerElement()); _rendered = true; } this.show = _show; return _show.apply(this, arguments); }; }; /** * Contains the default dialog options use by various dialog sub classes below * @const * @type {Object} */ var STANDARD_DIALOG_OPTS = { modal: true, draggable: true, close: false, visible: false, postmethod: "manual", hideaftersubmit: false, constraintoviewport: true, dragOnly: true, effect: null, // so resetProperty() will work on this strings: { "close": LOCALE.maketext("Close"), }, buttons: [{ text: LOCALE.maketext("Proceed"), handler: function() { this.submit(); }, isDefault: true, }, { text: LOCALE.maketext("Cancel"), classes: "cancel", handler: function() { this.cancel(); }, }], }; /** * TODO: Reword * a set of standard behaviors for Dialog instances, including: * behavior? * buttons * form handling (manual only) * destroy on cancel() * modal mask over the body & footer and show a Progress_Overlay on submit * @method Common_Dialog * @constructor * @class * @extends {YAHOO.widget.Dialog} * @param {String} id [description] * @param {[type]} opts [description] */ var Common_Dialog = function(id, opts) { if (id && (typeof id === "object")) { opts = id; id = DOM.generateId(); } else if (!id) { id = DOM.generateId(); } // assign standard props // assign submit handler - hide/destroy if (!opts) { opts = {}; } var copy_buttons = !("buttons" in opts); YAHOO.lang.augmentObject(opts, STANDARD_DIALOG_OPTS); if (copy_buttons) { var copied_buttons = []; for (var i = opts.buttons.length - 1; i >= 0; i--) { copied_buttons[i] = YAHOO.lang.augmentObject({}, opts.buttons[i]); } opts.buttons = copied_buttons; } Common_Dialog.superclass.constructor.call(this, id /* , opts */ ); this.beforeInitEvent.fire(Common_Dialog); DOM.addClass(this.element, "cjt_common_dialog_container"); this.cfg.applyConfig(opts, true); // Set up the dialog title this.setHeader(CPANEL.widgets.Dialog.applyDialogHeader(" ")); this.setBody(""); this.make_autorender(); var the_dialog = this; this.form.onsubmit = function() { return false; }; // YUI does its own submit() if (this.cfg.getProperty("draggable") && this.cfg.getProperty("fixedcenter")) { this.showEvent.subscribe(function() { this.cfg.setProperty("fixedcenter", false, false); }); this.hideEvent.subscribe(function() { DOM.setStyle(this.element, "left", ""); DOM.setStyle(this.element, "top", ""); this.cfg.setProperty("fixedcenter", true, false); }); } this.cancelEvent.subscribe(function() { this.after_hideEvent.subscribe(function destroyer() { // so successive cancelEvents will not create lots of these: this.after_hideEvent.unsubscribe(destroyer); if (the_dialog.cfg) { the_dialog.destroy(); } }); }); this.manualSubmitEvent.subscribe(function() { if (this.cfg.getProperty("progress_overlay")) { var body_region = DOM.getRegion(this.body); var footer_region = DOM.getRegion(this.footer); var mask_z_index = parseFloat(CPANEL.dom.get_zindex(this.element)) + 1; // NOTE: This ugliness is for generating masks for the body and the // footer after submitting the form. It was done originally because // innerHTML injection was faster than DOM manipulation in most // browsers; however, at some later point, that may not be the case. var dummy_div = document.createElement("div"); dummy_div.style.display = "none"; document.body.appendChild(dummy_div); // TODO: Remove to a markup island var div_html_template = "<div class='cjt_common_dialog_mask' style='" + "position:absolute;visibility:hidden;" + "z-index:{z_index};" + "background-color:{body_background_color};" + "width:{body_inner_width}px;" + "height:{body_inner_height}px;" + "'> </div>" + "<div class='cjt_common_dialog_mask' style='" + "position:absolute;visibility:hidden;" + "z-index:{z_index};" + "background-color:{footer_background_color};" + "width:{footer_inner_width}px;" + "height:{footer_inner_height}px;" + "'> </div>"; var div_html = YAHOO.lang.substitute(div_html_template, { z_index: mask_z_index, body_background_color: CPANEL.dom.get_background_color(this.body), body_inner_width: body_region.width, body_inner_height: body_region.height, footer_background_color: CPANEL.dom.get_background_color(this.footer), footer_inner_width: footer_region.width, footer_inner_height: footer_region.height, }); dummy_div.innerHTML = div_html; var body_mask = dummy_div.firstChild; var footer_mask = dummy_div.lastChild; // Different browsers report this value differently, // and YUI doesn't perfectly abstract it all away. var target_opacity = DOM.getStyle(body_mask, "opacity"); // JNK :TODO: This may not handle "inherits" properly, however // fixing it will increase risk at this stage if (!target_opacity || target_opacity == 1) { target_opacity = 0.7; } DOM.setStyle(body_mask, "opacity", 0); DOM.setStyle(footer_mask, "opacity", 0); body_mask.style.visibility = ""; footer_mask.style.visibility = ""; var body_fader = new YAHOO.util.Anim(body_mask, { opacity: { to: target_opacity, }, }, ANIM_TIME); var footer_fader = new YAHOO.util.Anim(footer_mask, { opacity: { to: target_opacity, }, }, ANIM_TIME); this.body.appendChild(body_mask); this.footer.appendChild(footer_mask); document.body.removeChild(dummy_div); // in case there is no height/width set on the body or footer DOM.setXY(body_mask, [body_region.left, body_region.top]); DOM.setXY(footer_mask, [footer_region.left, footer_region.top]); body_fader.animate(); footer_fader.animate(); var progress_overlay = new Progress_Overlay(null, { zIndex: mask_z_index + 1, visible: false, show_status: this.cfg.getProperty("show_status"), effect: { effect: YAHOO.widget.ContainerEffect.FADE, duration: ANIM_TIME, }, status_html: this.cfg.getProperty("status_html"), }); progress_overlay.render(this.body); progress_overlay.beforeShowEvent.subscribe(function() { DOM.setXY(this.element, [ body_region.left + body_region.width / 2 - this.element.offsetWidth / 2, (footer_region.bottom + body_region.top) / 2 - this.element.offsetHeight / 2, ]); }); progress_overlay.show(); this.progress_overlay = progress_overlay; // prevent focusing anything by making an invisible modal panel // lamentably, this prevents dragging the original Dialog var focus_killer = new YAHOO.widget.Panel(DOM.generateId(), { modal: true, x: DOM.getX(this.element), y: DOM.getY(this.element), visible: false, }); focus_killer.render(this.element); focus_killer.buildMask(); DOM.setStyle(focus_killer.element, "opacity", 0); DOM.setStyle(focus_killer.mask, "opacity", 0); focus_killer.show(); var kill_focus_killer = function() { if (focus_killer.cfg) { focus_killer.destroy(); } }; progress_overlay.destroyEvent.subscribe(kill_focus_killer); // undo the fades and disables after hide, in case this gets reshown progress_overlay.beforeHideEvent.subscribe(function kill_mask() { this.beforeHideEvent.unsubscribe(kill_mask); kill_focus_killer(); var body_out = new YAHOO.util.Anim(body_mask, { opacity: { to: 0, }, }, ANIM_TIME); var footer_out = new YAHOO.util.Anim(footer_mask, { opacity: { to: 0, }, }, ANIM_TIME); body_out.onComplete.subscribe(function() { if (the_dialog.body) { the_dialog.body.removeChild(body_mask); } }); footer_out.onComplete.subscribe(function() { if (the_dialog.footer) { the_dialog.footer.removeChild(footer_mask); } }); body_out.animate(); footer_out.animate(); }); progress_overlay.after_hideEvent.subscribe(progress_overlay.destroy, progress_overlay, true); // In the success case when we're in all-modal mode, // we have to listen for when the dialog dialog hides. this.beforeHideEvent.subscribe(function make_sure() { this.beforeHideEvent.unsubscribe(make_sure); if (focus_killer.cfg) { focus_killer.destroy(); } }); this.hideEvent.subscribe(function make_sure2() { if (progress_overlay.cfg) { progress_overlay.cfg.setProperty("effect", null); progress_overlay.hide(); } }); } else { if (this.cfg.getProperty("modal")) { this.cfg.setProperty("effect", FADE_MODAL); } this.hide(); } }); }; Common_Dialog.default_options = STANDARD_DIALOG_OPTS; YAHOO.lang.extend(Common_Dialog, YAHOO.widget.Dialog, { /** * [initDefaultConfig description] * @return {[type]} [description] */ initDefaultConfig: function() { YAHOO.widget.Dialog.prototype.initDefaultConfig.call(this); this.cfg.addProperty("progress_overlay", { value: true, }); this.cfg.addProperty("show_status", { value: false, }); this.cfg.addProperty("status_html", { value: "", }); }, /** * Destroy the dialog and, if applicable, its progress overlay object. * * @method destroy */ destroy: function() { if (this.progress_overlay) { if (this.progress_overlay.cfg) { this.progress_overlay.destroy(); } this.progress_overlay = null; } return Common_Dialog.superclass.destroy.apply(this, arguments); }, // QUESTION: Why are these here? Need an explanation showMacGeckoScrollbars: function() {}, hideMacGeckoScrollbars: function() {}, }); CPANEL.ajax.Common_Dialog = Common_Dialog; /** * Find the center point of an element. Assumes a rectangular element... * NOTE: may not be valid with new markup stuff for html5. * @method _find_el_center * @param {DOMElement} el Element to find the center of... * @return {Number[]} xy position of the element ? should this be a YUI Point? */ var _find_el_center = function(el) { var xy = DOM.getXY(el); xy[0] += (parseFloat(DOM.getStyle(el, "width")) || el.offsetWidth) / 2; xy[1] += (parseFloat(DOM.getStyle(el, "height")) || el.offsetHeight) / 2; return xy; }; /** * A shortcut class that ties into some CSS to make displaying error * popups quick and easy. This is sort of like YUI SimpleDialog. * @extends {Common_Dialog} * @constructor * @class * @param {[type]} id [description] * @param {[type]} opts [description] */ var Error_Dialog = function(id, opts) { if (id && (typeof id === "object")) { opts = id; id = DOM.generateId(); } else if (!id) { id = DOM.generateId(); } if (!opts) { opts = {}; } YAHOO.lang.augmentObject(opts, { fixedcenter: true, width: "400px", buttons: [{ text: LOCALE.maketext("OK"), handler: this.cancel, isDefault: true, }], }); Error_Dialog.superclass.constructor.call(this, id /* , opts */ ); this.beforeInitEvent.fire(Error_Dialog); DOM.addClass(this.element, "cjt_notice_dialog cjt_error_dialog"); this.cfg.applyConfig(opts, true); this.beforeRenderEvent.subscribe(function() { if (!this.header || !this.header.innerHTML) { this.setHeader(CPANEL.widgets.Dialog.applyDialogHeader(LOCALE.maketext("Error"))); } }); }; YAHOO.lang.extend(Error_Dialog, Common_Dialog); CPANEL.ajax.Error_Dialog = Error_Dialog; /* * A further extension from Common_Dialog that implements standard behaviors * like form templates, chaining API calls, etc. In theory, no interaction with * setHeader/setBody/etc. is necessary when using this class. * * TODO: Fix this so it can work with CPANEL.api "batch". * * @extends {Common_Dialog} * @constructor * @class * @param {[type]} id [description] * @param {[type]} opts * header_html * clicked_element * preload: an API call (see below) to run before showing the dialog box * errors_in_notice_box: if true, use a Notice instead of an alternate dialog box for error notices. Default false. * TODO: Make this default to true, and remove the alternate workflow. * show_status: boolean * status_template: template (YAHOO.lang.substitute) for the submit status may be specified per API call as well * form_template: template (YAHOO.lang.substitute) for the form HTML * form_template_variables: object * no_hide_after_success: Keep the dialog box in place after the last API call * success_function: Executed after the last API call succeeds. * success_status: Text (should it be a template?) for the last API call. * success_notice_options: Options to pass to the success Notice * api_calls: {Array} * api_version * api_module * api_function * data: for the function call itself (e.g. user, domain, etc.) * api_data: sort/filter/paginate * status_template: see above * success_function: see above */ var Common_Action_Dialog = function(id, opts) { if (!opts) { opts = {}; } Common_Action_Dialog.superclass.constructor.call(this, id, opts); // Setup the title if (opts.header_html) { var header_html = opts.header_html; header_html = CPANEL.widgets.Dialog.applyDialogHeader(header_html); this.setHeader(header_html); } var the_dialog = this; if (opts.preload) { var loading_panel = new Progress_Panel(); loading_panel.render(_masterContainerElement()); if (opts.clicked_element) { loading_panel.show_from_source(opts.clicked_element); } else { loading_panel.show(); } loading_panel.after_hideEvent.subscribe(loading_panel.destroy, loading_panel, true); var preload_copy = { application: opts.preload.api_application, module: opts.preload.api_module, func: opts.preload.api_function, data: opts.preload.data, api_data: opts.preload.api_data, callback: null, }; var given_success = opts.preload.success_function; var preload_callback = CPANEL.ajax.build_callback( function() { if (given_success) { given_success.apply(the_dialog, arguments); } the_dialog.beforeShowEvent.subscribe(function center() { the_dialog.beforeShowEvent.unsubscribe(center); the_dialog.center(); }); var form_template = the_dialog.cfg.getProperty("form_template"); if (form_template) { var template_vars = opts.form_template_variables; var template_text = CPANEL.ajax.templates[form_template] || form_template; the_dialog.form.innerHTML = YAHOO.lang.substitute(template_text, template_vars || {}); } }, { current: loading_panel, success: the_dialog, }, { whm: opts.preload.api_application && (opts.preload.api_application === "whm") || CPANEL.is_whm(), } ); preload_copy.callback = preload_callback; CPANEL.api(preload_copy); } else { if (opts.form_template) { var form_template_variables = opts.form_template_variables || {}; var template_text = CPANEL.ajax.templates[opts.form_template] || opts.form_template; this.form.innerHTML = YAHOO.lang.substitute(template_text, form_template_variables); } } this.manualSubmitEvent.subscribe(function() { var api_calls = this.cfg.getProperty("api_calls"); if (!api_calls) { return; } var index = 0; var _run_api_call_queue = function() { var cur_api_call = api_calls[index]; var is_first_api_call = (index === 0); var is_last_api_call = (index === api_calls.length - 1); index++; var data; if (cur_api_call.data) { if (cur_api_call.data instanceof Function) { data = cur_api_call.data.apply(the_dialog); } else { data = cur_api_call.data; } } else { data = CPANEL.dom.get_data_from_form(the_dialog.form); } var callback_success; if (is_last_api_call) { callback_success = function() { if (cur_api_call.success_function) { cur_api_call.success_function.apply(the_dialog, arguments); } if (the_dialog.cfg.getProperty("show_status")) { var success_opts = the_dialog.cfg.getProperty("success_notice_options") || {}; if (!success_opts.content) { success_opts.content = the_dialog.cfg.getProperty("success_status") || LOCALE.maketext("Success!"); if (!success_opts.level) { success_opts.level = "success"; } } the_dialog.notice = new Dynamic_Notice(success_opts); } if (the_dialog.cfg.getProperty("success_function")) { the_dialog.cfg.getProperty("success_function").call(the_dialog); } }; } else { callback_success = function() { if (cur_api_call.success_function) { cur_api_call.success_function.apply(the_dialog, arguments); } _run_api_call_queue(); }; } var status_template = opts.show_status && (cur_api_call.status_template || opts.status_template); if (status_template) { var data_html = {}; for (var key in data) { if (data.hasOwnProperty(key)) { data_html[key] = String(data[key]).html_encode(); } } var status_html = YAHOO.lang.substitute(status_template, data_html); if (is_first_api_call) { the_dialog.progress_overlay.set_status_now(status_html); } else { the_dialog.progress_overlay.set_status(status_html); } the_dialog.progress_overlay.align(); } var callback; if (the_dialog.cfg.getProperty("errors_in_notice_box")) { callback = { success: function() { if (!the_dialog.cfg.getProperty("no_hide_after_success")) { the_dialog.animated_hide(); } return callback_success.apply(this, arguments); }, failure: function(o) { the_dialog.progress_overlay.hide(); the_dialog._api_error_notice = new CPANEL.widgets.Dynamic_Page_Notice({ replaces: the_dialog._api_error_notice, container: CPANEL.Y(the_dialog.form).one(".details-error-notice"), level: "error", content: _make_failure_html(o), }); if (cur_api_call.on_error) { return cur_api_call.on_error.apply(this, arguments); } }, }; } else { // if we fail on anything but the first API call, // then we go back to the page (and probably refresh the data) var try_again_after_error = is_first_api_call && the_dialog.cfg.getProperty("try_again_after_error"); callback = CPANEL.ajax.build_callback( callback_success, { current: the_dialog, after_error: try_again_after_error ? the_dialog : undefined, }, { on_error: cur_api_call.on_error, whm: cur_api_call.api_application && (cur_api_call.api_application === "whm") || CPANEL.is_whm(), keep_current_on_success: !is_last_api_call || the_dialog.cfg.getProperty("no_hide_after_success"), } ); } var api_call = { application: cur_api_call.api_application, module: cur_api_call.api_module, func: cur_api_call.api_function, api_data: cur_api_call.api_data, data: data, callback: callback, }; if (cur_api_call.api_version) { api_call["version"] = cur_api_call.api_version; } CPANEL.api(api_call); }; _run_api_call_queue(); }); }; YAHOO.lang.extend(Common_Action_Dialog, Common_Dialog, { /** * [initDefaultConfig description] * @this Common_Action_Dialog * @method initDefaultConfig */ initDefaultConfig: function() { Common_Action_Dialog.superclass.initDefaultConfig.call(this); var extra_properties = [ "header_html", "form_template", "form_template_variables", "clicked_element", "status_template", "api_calls", "preload", "no_hide_after_success", "success_function", "success_status", "success_notice_options", ["errors_in_notice_box", false], ["try_again_after_error", true], ]; var that = this; extra_properties.forEach(function(p) { if (p instanceof Array) { that.cfg.addProperty(p[0], { value: p[1], }); } else { that.cfg.addProperty(p, { value: null, }); } }); }, /** * [animated_show description] * @this Common_Action_Dialog */ animated_show: function() { var clicked_el = this.cfg.getProperty("clicked_element"); if (clicked_el) { var motion = this.show_from_source(clicked_el); return motion; } else { this.show(); } }, /** * [animated_hide description] * @this Common_Action_Dialog */ animated_hide: function() { var clicked_el = this.cfg.getProperty("clicked_element"); if (clicked_el) { var motion = this.hide_to_point(clicked_el); return motion; } else { this.hide(); } }, }); CPANEL.ajax.Common_Action_Dialog = Common_Action_Dialog; /** * Sets the current "default" values in a form's elements to their current value. * @method set_form_defaults * @param {HTMLForm} form [description] */ CPANEL.dom.set_form_defaults = function(form) { var els = form.elements; for (var e = els.length - 1; e >= 0; e--) { var cur_el = els[e]; if (cur_el.tagName.toLowerCase() === "select") { var opts = cur_el.options; for (var o = opts.length - 1; o >= 0; o--) { var cur_opt = opts[o]; cur_opt.defaultSelected = cur_opt.selected; } } else { if ("defaultChecked" in cur_el) { cur_el.defaultChecked = cur_el.checked; } if ("defaultValue" in cur_el) { cur_el.defaultValue = cur_el.value; } } } }; // fix for IE6 and IE7, which do not report the "value" attribute // of an <option> unless it is explicitly given var opt = document.createElement("option"); opt.innerHTML = "test"; var _option_elements_value_from_content = (opt.value !== opt.innerHTML); CPANEL.dom.TRIM_FORM_DATA = true; /** * Parses the elements in an HTML <form> element into a JavaScript object * that represents what would be submitted on HTTP submission. * * This fixes some bugs/quirks in YUI Dialog's .getData() method; in particular: * * getData() errantly ignores single radio buttons. * * getData() won't combine values of consecutive inputs with the same name. * * getData() doesn't care which elements are disabled. * * getData() puts all <select> values into arrays; this function takes a * more general approach in that, if a form key only has one value, it's a * string, and if the key appears more than once, the value is an array. * This accurately models an HTTP form submission. * * @method get_data_from_form * @param {HTMLForm} form [description] * @param {[type]} opts * url_instead: Return an HTTP query string instead of a JavaScript object * include_unchecked_checkboxes: Assigns this value to unchecked checkboxes. * (instead of the default, i.e. omitting unchecked checkboxes) * * @return {[type]} [description] */ CPANEL.dom.get_data_from_form = function(form, opts) { if (typeof form === "string") { form = document.getElementById(form); } var _add_to_form_data, form_data; if (opts && opts.url_instead) { form_data = []; _add_to_form_data = function(new_name, new_value) { if (CPANEL.dom.TRIM_FORM_DATA && typeof new_value === "string") { new_value = new_value.trim(); } form_data.push(encodeURIComponent(new_name) + "=" + encodeURIComponent(new_value)); }; } else { form_data = {}; _add_to_form_data = function(new_name, new_value) { if (CPANEL.dom.TRIM_FORM_DATA && typeof new_value === "string") { new_value = new_value.trim(); } if (new_name in form_data) { if (YAHOO.lang.isArray(form_data[new_name])) { form_data[new_name].push(new_value); } else { form_data[new_name] = [form_data[new_name], new_value]; } } else { form_data[new_name] = new_value; } }; } // It's important to iterate through in DOM order! var form_elements = form.elements; var elements_length = form_elements.length; for (var fc = 0; fc < elements_length; fc++) { var cur_control = form_elements[fc]; if ("value" in cur_control && "name" in cur_control && cur_control.name && !cur_control.disabled ) { var control_name = cur_control.nodeName.toLowerCase(); if (control_name === "input") { var control_type = cur_control.type.toLowerCase(); switch (control_type) { case "radio": if (cur_control.checked) { _add_to_form_data(cur_control.name, cur_control.value); } break; case "checkbox": if (cur_control.checked) { _add_to_form_data(cur_control.name, cur_control.value); } else if (opts && ("include_unchecked_checkboxes" in opts)) { _add_to_form_data(cur_control.name, opts.include_unchecked_checkboxes); } break; default: _add_to_form_data(cur_control.name, cur_control.value); break; } } else if (control_name === "select") { var cur_opt, cur_value; if (cur_control.selectedIndex !== -1) { var cur_control_name = cur_control.name; if (cur_control.multiple) { var cur_options = cur_control.options; var cur_options_length = cur_options.length; for (var o = 0; o < cur_options_length; o++) { cur_opt = cur_options[o]; if (cur_opt.selected && !cur_opt.disabled) { // This only happens in IE6 and IE7 (?), // so we might as well use innerText // instead of CPANEL.util.get_text_content() // and save a function call. // We also assume hasAttribute("value"), // which is safe in IE6/7. if (_option_elements_value_from_content && !cur_opt.getAttributeNode("value").specified) { cur_value = cur_opt.innerText; } else { cur_value = cur_opt.value; } _add_to_form_data(cur_control_name, cur_value); } } } else { cur_opt = cur_control.options[cur_control.selectedIndex]; // see above about IE6 and IE7 if (_option_elements_value_from_content && !cur_opt.getAttributeNode("value").specified) { cur_value = cur_opt.innerText; } else { cur_value = cur_opt.value; } _add_to_form_data(cur_control_name, cur_value); } } } else if (control_name === "button") { _add_to_form_data(cur_control.name, cur_control.value); } else if (control_name === "textarea") { // Some old IE versions use Windows-style line breaks. // The HTML 5 standard is to use UNIX line breaks // for getting and setting a <textarea>'s "value" property. // cf. HTML 5 standard, <textarea>, "API value" vs. "raw value" _add_to_form_data( cur_control.name, cur_control.value.replace(/\r\n?/g, "\n") ); } } } if (opts && opts.url_instead) { return form_data.join("&"); } else { return form_data; } }; /** * Adds an arbitrary CSS style to the page * This is intentionally lazy loaded since the stylesheet may not * be defined at the load time preventing us from having to create * a style sheet use in the test statements. * @example * add_styles([[".foo", "color:red"], [".bar","color:blue"]]) * @method add_styles * @deprecated Not used in code and has some issues with complexity and * which style sheet its applied to. Also there are alternatives in YUI2 and * YUI3 for this that are preferred. Do not use in new code. */ var add_styles = function() { // NOTE: This is a bad design, should use distinct variable names or something // Bad to self modify outside scope. var stylesheet = document.styleSheets[0]; if (!stylesheet) { document.head.appendChild(document.createElement("style")); stylesheet = document.styleSheets[0]; } if ("insertRule" in stylesheet) { // W3C DOM add_styles = function(styles) { for (var s = 0; s < styles.length; s++) { stylesheet.insertRule(styles[s][0] + " {" + styles[s][1] + "}", 0); } }; } else { // IE add_styles = function(styles) { for (var s = 0; s < styles.length; s++) { stylesheet.addRule(styles[s][0], styles[s][1], 0); } }; } CPANEL.dom.add_styles = add_styles; return add_styles.apply(this, arguments); }; /** * [add_style description] * @example * add_style( "body", "background-color:gray" ), * @method add_style * @deprecated Not used in code and has some issues with complexity and * which style sheet its applied to. Also there are alternatives in YUI2 and * YUI3 for this that are preferred. Do not use in new code. */ CPANEL.dom.add_style = function(new_style) { return CPANEL.dom.add_styles.call(this, [new_style]); }; /** * [add_styles description] * @example * add_styles([[".foo", "color:red"], [".bar","color:blue"]]) * @deprecated Not used in code and has some issues with complexity and * which style sheet its applied to. Also there are alternatives in YUI2 and * YUI3 for this that are preferred. Do not use in new code. @method add_styles */ CPANEL.dom.add_styles = add_styles; /** * Toggle disabled on an element such that it can "receive" a click * to re-enable it. * @method smart_disable * @param {[type]} el [description] * @param {[type]} to_disable [description] * @param {[type]} on_enable [description] * @return {[type]} [description] */ CPANEL.dom.smart_disable = function(el, to_disable, on_enable) { el = DOM.get(el); var overlay; if (typeof (to_disable) === "undefined") { to_disable = !el.disabled; } if (to_disable) { if (el._smart_disable_overlay) { return el._smart_disable_overlay; } el.disabled = true; overlay = new Smart_Disable_Overlay(el); overlay.render(el.parentNode); overlay.show(); // TODO: Should this be a mousedown instead of onclick? overlay.element.onclick = function() { overlay.destroy(); try { // IE throws an error in many cases when doing this delete el._smart_disable_overlay; } catch (e) { el._smart_disable_overlay = undefined; } el.disabled = false; if (on_enable) { on_enable.apply(el); } }; el._smart_disable_overlay = overlay; } else { el.disabled = false; overlay = el._smart_disable_overlay; try { delete el._smart_disable_overlay; } catch (e) { el._smart_disable_overlay = undefined; } if (overlay) { overlay.destroy(); } } return overlay; }; var _smart_disable_overlay_styles_needed = !!YAHOO.env.ua.ie; /** * A subclass of YUI Overlay that sizes to the passed-in "el"(ement) * and positions itself (via z-index) "above" that element. * This is useful for "catching" mouse clicks on disabled elements. * * @constructor * @class * @extends {YAHOO.widget.Overlay} * @param {DOM} el The HTML node to use as the reference element. */ var Smart_Disable_Overlay = function(el) { // IE (all versions <= 10, at least) needs styles added // in order for smart_disable overlays to work. if (_smart_disable_overlay_styles_needed) { CPANEL.dom.add_style([ ".cjt-smart-disable", "background-color:red;filter:alpha(opacity=0);opacity:0;", ]); _smart_disable_overlay_styles_needed = false; } var el_zIndex = parseFloat(CPANEL.dom.get_zindex(el)); Smart_Disable_Overlay.superclass.constructor.call(this, DOM.generateId(), { iframe: false, zIndex: el_zIndex + 1, width: el.offsetWidth + "px", height: el.offsetHeight + "px", context: [el, "tl", "tl"], }); // otherwise Mac Gecko will tab to it this.showEvent.subscribe(this.hideMacGeckoScrollbars, this, true); this.hideMacGeckoScrollbars = this.showMacGeckoScrollbars; // blackhole it DOM.addClass(this.element, "cjt-smart-disable"); this._context_el = el; }; YAHOO.lang.extend(Smart_Disable_Overlay, YAHOO.widget.Overlay, { align: function() { var el_zIndex = parseFloat(CPANEL.dom.get_zindex(this._context_el)); this.cfg.setProperty("zIndex", el_zIndex + 1); this.cfg.setProperty("width", this._context_el.offsetWidth + "px"); this.cfg.setProperty("height", this._context_el.offsetHeight + "px"); Smart_Disable_Overlay.superclass.align.apply(this, arguments); }, showMacGeckoScrollbars: function() {}, }); // Expose this publicly for other widgets, e.g., Combobox. CPANEL.dom.Smart_Disable_Overlay = Smart_Disable_Overlay; /** * methods for retrieving useful values instead of "auto", "transparent", etc. * [get_recursive_style description] * @method get_recursive_style * @param {[type]} obj [description] * @param {[type]} property [description] * @param {[type]} recurse_if [description] * @param {[type]} default_value [description] * @return {[type]} [description] */ CPANEL.dom.get_recursive_style = function(obj, property, recurse_if, default_value) { var cur_obj = DOM.get(obj); var cur_value; do { cur_value = DOM.getComputedStyle(cur_obj, property); } while ((cur_value === recurse_if) && (cur_obj = cur_obj.parentNode) && (cur_obj !== document)); if (!cur_obj || (cur_value === recurse_if)) { cur_value = default_value; } return cur_value; }; /** * [get_background_color description] * @method get_background_color * @param {[type]} obj [description] * @return {[type]} [description] */ CPANEL.dom.get_background_color = function(obj) { return CPANEL.dom.get_recursive_style(obj, "backgroundColor", "transparent"); }; /** * [get_zindex description] * @method get_zindex * @param {[type]} obj [description] * @return {[type]} [description] */ CPANEL.dom.get_zindex = function(obj) { return CPANEL.dom.get_recursive_style(obj, "zIndex", "auto", 0); }; /** * streamline setting up disable/enable relationships among elements groups * that are toggled with radio buttons. * * each group is: { * radio: HTMLElement, - required * inputs: [HTMLElement], * listeners: [HTMLElement] - optional, listens for click to enable * disablees: [HTMLElement] - optional, sets/removes "disabled" class * } * * This also accepts (form, start_index/el, end_index/el) and will parse them. * * @constructor * @class * @extends {YAHOO.widget.Overlay} */ CPANEL.ajax.Grouped_Input_Set = function() { // ISSUE: No common argument set defined, bad constructor var groups, first_arg = arguments[0]; if ((typeof first_arg === "string") || first_arg.tagName && first_arg.tagName.toLowerCase() === "form") { groups = CPANEL.ajax.Grouped_Input_Set.make_groups_from_form.apply(this, arguments); } else { groups = first_arg; } this._groups = groups; var the_set = this; var register_group_listeners = function(this_group) { EVENT.on(this_group.listeners, "click", function() { if (!this_group.radio.checked) { this_group.radio.checked = true; the_set.refresh(); } }); }; for (var g = 0, l = groups.length; g < l; g++) { var group = groups[g]; YAHOO.util.Event.on(group.radio, "click", this.refresh, this, true); if (group.listeners) { register_group_listeners(group); } } this.refresh(); }; var FORM_ELEMENTS = { button: 1, input: 1, select: 1, textarea: 1, }; /** * [make_groups_from_form description] * @method make_groups_from_form * @static * @param {[type]} form [description] * @param {[type]} start_el can be elements or indexes of form.elements * @param {[type]} end_el can be elements or indexes of form.elements * @return {[type]} [description] */ CPANEL.ajax.Grouped_Input_Set.make_groups_from_form = function(form, start_el, end_el) { form = DOM.get(form); var form_labels_with_for = CPANEL.Y(form).all("label[for]"); var labels_with_for = {}; var cur_label; for (var l = form_labels_with_for.length - 1; l >= 0; l--) { cur_label = form_labels_with_for[l]; labels_with_for[cur_label.htmlFor] = cur_label; } // form.elements is not guaranteed to be in DOM order, e.g. WebKit 534.24 // This also gives us an array rather than an HTML collection, which is nice. // TODO: Would CPANEL.Y(form).all("button, input, select, textarea") work here? var els = DOM.getElementsBy( function(el) { return (el.tagName.toLowerCase() in FORM_ELEMENTS); }, undefined, form ); var cur_el, i; if (typeof start_el !== "undefined" && typeof start_el !== "number") { if (typeof start_el === "string") { start_el = DOM.get(start_el); } if (start_el) { i = 0; while ((cur_el = els[i]) && (cur_el !== start_el)) { i++; } if (!cur_el) { return; } start_el = i; } } if (!start_el) { start_el = 0; } if (typeof end_el !== "undefined" && typeof end_el !== "number") { if (typeof end_el === "string") { end_el = DOM.get(end_el); } if (end_el) { i = start_el || 0; i++; while ((cur_el = els[i]) && (cur_el !== end_el)) { i++; } if (!cur_el) { return; } end_el = i; } } var groups = []; var cur_group; // ISSUE: LOOP TEST SHOULD NOT HAVE SIDE EFFECTS for (var e = start_el; (cur_el = els[e]) && cur_el; e++) { if (end_el && e > end_el) { break; } if (cur_el.type && cur_el.type.toLowerCase() === "radio") { if (cur_group) { groups.push(cur_group); } cur_group = { radio: cur_el, inputs: [], listeners: [], }; } else if (cur_group) { cur_group.inputs.push(cur_el); var label = DOM.getAncestorByTagName(cur_el, "label"); if (!label && cur_el.id) { label = labels_with_for[cur_el.id]; } if (label && (cur_group.listeners.indexOf(label) === -1)) { cur_group.listeners.push(label); } } } if (cur_group) { groups.push(cur_group); } return groups; }; YAHOO.lang.augmentObject(CPANEL.ajax.Grouped_Input_Set.prototype, { // TODO: Add documentation _groups: null, // TODO: Add documentation get_groups: function() { return this._groups && this._groups.slice(0); }, // TODO: Add documentation refresh: function() { var enabled; for (var g = 0; g < this._groups.length; g++) { var group = this._groups[g]; if (group.radio.checked) { enabled = group; this._enable_group(group); } else { this._disable_group(group); } } if (this.onrefresh) { this.onrefresh.call(this, enabled); } }, // TODO: Add documentation align: function() { var do_align = function(o) { o.align(); }; this.get_groups().forEach(function(g) { if (g.disabled) { g.smart_disable_overlays.forEach(do_align); } }); }, // TODO: Add documentation onrefresh: null, // TODO: Add documentation _enable_group: function(group) { if (group.inputs) { for (var i = 0; i < group.inputs.length; i++) { CPANEL.dom.smart_disable(group.inputs[i], false); } } if (group.noninputs) { for (var n = 0; n < group.noninputs.length; n++) { DOM.removeClass(group.noninputs[n], "disabled"); } } group.disabled = false; group.smart_disable_overlays = null; }, // TODO: Add documentation _disable_group: function(group) { var the_set = this; group.disabled = true; group.smart_disable_overlays = []; if (group.inputs) { var on_enable = function() { group.radio.checked = true; the_set.refresh(); this.focus(); // For some reason, setSelectionRange is a no-op // for number-type inputs. It *does* work, though, // to convert the input to text, do the selection, // then switch back to number. That’s what this does. // const oldType = this.type; if (oldType !== "text") { this.type = "text"; } this.setSelectionRange(0, this.value.length); if (oldType !== "text") { this.type = oldType; } }; for (var i = 0; i < group.inputs.length; i++) { var ov = CPANEL.dom.smart_disable(group.inputs[i], true, on_enable); group.smart_disable_overlays.push(ov); } } if (group.noninputs) { for (var n = 0; n < group.noninputs.length; n++) { DOM.addClass(group.noninputs[n], "disabled"); } } }, }); /** * Interactive notifications, e.g. for growl-type AJAX call success notices * opts: * fade_delay: if true, # of seconds after show() to destroy() (default: 5) * closable {Boolean} whether clicking will close it (class "cjt-notice-closable") * closable_tooltip {String} tooltip text for a closable Dynamic_Notice element * @constructor * @class * @extends {CPANEL.widgets.Notice} * TODO: Parameters??? */ var Dynamic_Notice = function() { Dynamic_Notice.superclass.constructor.apply(this, arguments); }; // Static constants Dynamic_Notice.DEFAULT_CONTAINER_ID = "cjt_dynamicnotice_container"; Dynamic_Notice.CLASS = "cjt-dynamicnotice"; YAHOO.lang.extend(Dynamic_Notice, CPANEL.widgets.Notice, { /** * [reset_fade_timeout description] * @return {[type]} [description] */ reset_fade_timeout: function() { this.config_fade_delay(this.cfg.getProperty("fade_delay")); }, /** * [init description] * @param {[type]} el [description] * @param {[type]} opts [description] * @return {[type]} [description] */ init: function(el, opts) { Dynamic_Notice.superclass.init.call(this, el /* , opts */ ); this.beforeInitEvent.fire(Dynamic_Notice); DOM.addClass(this.element, Dynamic_Notice.CLASS); if (opts) { this.cfg.applyConfig(opts, true); this.render(); } this.initEvent.fire(Dynamic_Notice); }, /** * [initDefaultConfig description] * @return {[type]} [description] */ initDefaultConfig: function() { Dynamic_Notice.superclass.initDefaultConfig.call(this); this.cfg.addProperty("closable", { value: true, handler: this.config_closable, }); this.cfg.addProperty("closable_tooltip", { value: LOCALE.maketext("Click to close."), }); this.cfg.addProperty("fade_delay", { value: 5, handler: this.config_fade_delay, }); }, config_closable: function(type, args, obj) { if (!this.body) { var the_args = arguments; this.beforeShowEvent.subscribe(function configger() { this.beforeShowEvent.unsubscribe(configger); this.config_closable.apply(this, the_args); }); return; } var closable = args[0]; var tooltip = this.cfg.getProperty("closable_tooltip"); if (closable) { DOM.addClass(this.element, "cjt-notice-closable"); this._click_listener = EVENT.on(this.body, "mousedown", function(e) { var target = EVENT.getTarget(e); if (YAHOO.widget.Panel.FOCUSABLE.indexOf(target.tagName.toLowerCase()) === -1) { this.fade_out(); } }, this, true); if (tooltip) { this._former_tooltip = this.body.title; this.body.title = tooltip; } } else { DOM.removeClass(this.element, "cjt-notice-closable"); if (this._click_listener) { EVENT.removeListener(this.body, "mousedown", this._click_listener); delete this._click_listener; } if (tooltip && this.body.title === tooltip && ("_former_tooltip" in this)) { this.body.title = this._former_tooltip; delete this._former_tooltip; } } }, /** * [config_fade_delay description] * @param {[type]} type [description] * @param {[type]} args [description] * @param {[type]} obj [description] * @return {[type]} [description] */ config_fade_delay: function(type, args /* , obj */ ) { this._cancel_fade(); var fade_delay = args[0]; if (fade_delay) { var that = this; this._fade_timeout = window.setTimeout(function() { that.fade_out(); }, fade_delay * 1000); } }, /** * [destroy description] * @return {[type]} [description] */ destroy: function() { if (this._fade_timeout) { window.clearTimeout(this._fade_timeout); delete this._fade_timeout; } // Check to be sure the element is still in the document since // we have multiple things destroy()ing Notice instances, and those // destroy()ers are not necessarily aware of each other. if (this.cfg) { Dynamic_Notice.superclass.destroy.apply(this, arguments); } }, /** * [_cancel_fade description] * @return {[type]} [description] */ _cancel_fade: function() { if (this._fade_timeout) { window.clearTimeout(this._fade_timeout); delete this._fade_timeout; } }, /** * [render description] * @return {[type]} [description] */ render: function() { if (!Dynamic_Notice.notice_container) { var notice_container = document.createElement("div"); notice_container.id = "cjt_dynamicnotice_container"; _masterContainerElement().appendChild(notice_container); Dynamic_Notice.notice_container = notice_container; DOM.addClass(notice_container, "cjt-dynamicnotice-container"); } var container = this.cfg.getProperty("container"); if (container) { DOM.addClass(container, "cjt-dynamicnotice-container"); } else { container = Dynamic_Notice.notice_container; } var ret = Dynamic_Notice.superclass.render.call(this, container); return ret; }, }); CPANEL.ajax.Dynamic_Notice = Dynamic_Notice; /** * Underlying object to help with custom tooltips display. Do not call directly as * these are not single instance, please use Tooltip_Notice.toggleToolTip() * @method Tooltip_Notice * @param [String|HTMLElement] targetEl element or id of element to load from, must have a title attribute with the text to show. * @param [String] headerText optional string to display in the title, will be the localized version of "Notice" if not defined. * @param [String] id optional id of the dialog, will be generated if not defined. * @param [Hash] opts optional options to pass to the notice. * overlayCorner : [tr, tl, br, bl] * contextCorner : [tr, tl, br, bl] * offsetX : [Number] Pixel to offest in the x, may be negative or positive. * offsetY : [Number] Pixel to offset in the y, may be negative or positive. * format : [Hash] contains formatting options. * processText : [Function] Optional formatting function to customize formatting. * highlightWords : [Array] Words to highlight. * highlightClassName : [String] CSS class name to apply to highlighted words. */ var Tooltip_Notice = function(targetEl, headerTxt, id, opts) { // Setup the options if (!opts) { opts = {}; } YAHOO.lang.augmentObject(opts, Tooltip_Notice.standardOptions); // Setup the render context if (!opts.context) { opts.context = [ targetEl, // Overlay corner position is bottom right unless overridden opts.overlayCorner ? opts.overlayCorner : "tl", // Context corner position is top left unless overridden opts.contextCorner ? opts.contextCorner : "tr", [ // Perform the context on these events "beforeShow", "windowResize", ], [ // Offest 2px down unless overridden opts.offsetX ? opts.offsetX : 10, // Offest 2px right unless overridden opts.offsetY ? opts.offsetY : -5, ], ]; } // Give it an id if one is not provided if (!id) { id = DOM.generateId(); } // Create the notice var noticebox = new CPANEL.ajax.Common_Dialog(id, opts); var text = targetEl.title; // cf. HTML 5, section 3.2.3.2 var displayText = text.html_encode().replace(/\n/g, "<br>"); if (opts.format) { var OF = opts.format; OF.highlightClassName = OF.highlightClassName || Tooltip_Notice.DEFAULT_TOOLTIP_HIGHLIGHT_CLASS_NAME; if (OF.processText) { try { displayText = OF.processText(displayText, OF.highlightWords, OF.highlightClassName); } catch (ex) { // Ignore } } else { if (OF.highlightWords && OF.highlightWords.length) { var template = "<span class='{class}'>{word}</span>".replace("{class}", OF.highlightClassName); for (var i = 0, l = OF.highlightWords.length; i < l; i++) { var word = OF.highlightWords[i]; try { displayText = displayText.replace(word, template.replace("{word}", word)); } catch (ex) { // Ignore } } } } } noticebox.setBody(displayText); // Save the title and clear it so we don't get the // browser tooltip showing while the custom one is open. noticebox.restoreTitle = text; targetEl.title = ""; // Setup the title var headerHTML = headerTxt ? headerTxt : LOCALE.maketext("Notice"); headerHTML = CPANEL.widgets.Dialog.applyDialogHeader(headerHTML); noticebox.setHeader(headerHTML); // Setup ok button noticebox.cfg.getProperty("buttons")[0].text = LOCALE.maketext("OK"); noticebox.submit = function() { this.hide(); if (this.restoreTitle) { targetEl.title = this.restoreTitle; } delete Tooltip_Notice.active[targetEl.id]; }; this.close = function() { noticebox.submit(); }; // Omit the cancel button noticebox.cfg.getProperty("buttons").pop(); DOM.addClass(noticebox.element, "cjt_notice_dialog cjt_info_dialog"); noticebox.beforeShowEvent.subscribe(noticebox.center, noticebox, true); noticebox.show_from_source(targetEl); }; /** * Predefined rules for displaying a Tooltip_Notice. * @member [Hash] Tooltip_Notice.standardOptions */ Tooltip_Notice.standardOptions = { modal: false, width: "300px", fixedcenter: false, }; /** * Default class name used in word high-lighting * @static * @member [String] Tooltip_Notice.DEFAULT_TOOLTIP_HIGHLIGHT_CLASS_NAME */ Tooltip_Notice.DEFAULT_TOOLTIP_HIGHLIGHT_CLASS_NAME = "cjt-highlight-word"; /** * Simple wrapper to make calling show only one tooltips easy to display. * @method Tooltip_Notice.toggleToolTip * @param [String|HTMLElement] targetEl element or id of element to load from, must have a title attribute with the text to show. * @param [String] headerText optional string to display in the title, will be the localized version of "Notice" if not defined. * @param [String] id optional id of the dialog, will be generated if not defined. * @param [Hash] opts optional options to pass to the notice. * overlayCorner : [tr, tl, br, bl] * contextCorner : [tr, tl, br, bl] * offsetX : [Number] Pixil to offest in the x, may be negative or positive. * offsetY : [Number] Pixil to offset in the y, may be negative or positive. */ Tooltip_Notice.toggleToolTip = function(targetEl, headerText, id, opts) { // Element must have an id, so give it one if needed if (!targetEl.id) { targetEl.id = DOM.generateId(); } // See if this is an open or close click if (!Tooltip_Notice.active[targetEl.id]) { var tooltip = new Tooltip_Notice(targetEl, headerText, id, opts); Tooltip_Notice.active[targetEl.id] = tooltip; } else { Tooltip_Notice.active[targetEl.id].close(); } }; /** * Static array of the actively displayed tooltips on the window. * @member [Array] Tooltip_Notice.active Array of the id's of the active tooltips. Used to track which ones are currently visible.*/ Tooltip_Notice.active = []; // Register the functions. CPANEL.ajax.toggleToolTip = Tooltip_Notice.toggleToolTip; })(window);
Save