diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2019-01-29 10:15:46 +0100 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2019-01-29 11:07:48 +0100 |
commit | 3ced9cd83db38731a1e82dafc0dcbdfe7db6cc2b (patch) | |
tree | 3da933724684a709f6afa2a8cbaa1fae10baa575 /core/src | |
parent | 2682d672d8076302da61fef3cc15e4d647d50be5 (diff) | |
download | nextcloud-server-3ced9cd83db38731a1e82dafc0dcbdfe7db6cc2b.tar.gz nextcloud-server-3ced9cd83db38731a1e82dafc0dcbdfe7db6cc2b.zip |
Move jQuery plugins into modules and add them to the bundle
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/Util/escapeHTML.js | 36 | ||||
-rw-r--r-- | core/src/globals.js | 2 | ||||
-rw-r--r-- | core/src/jquery/avatar.js | 162 | ||||
-rw-r--r-- | core/src/jquery/contactsmenu.js | 125 | ||||
-rw-r--r-- | core/src/jquery/exists.js | 31 | ||||
-rw-r--r-- | core/src/jquery/filterattr.js | 31 | ||||
-rw-r--r-- | core/src/jquery/index.js | 32 | ||||
-rw-r--r-- | core/src/jquery/ocdialog.js | 270 | ||||
-rw-r--r-- | core/src/jquery/octemplate.js | 104 | ||||
-rw-r--r-- | core/src/jquery/placeholder.js | 174 | ||||
-rw-r--r-- | core/src/jquery/selectrange.js | 43 | ||||
-rw-r--r-- | core/src/jquery/showpassword.js | 149 | ||||
-rw-r--r-- | core/src/jquery/tipsy.js | 83 | ||||
-rw-r--r-- | core/src/jquery/ui-fixes.js | 8 | ||||
-rw-r--r-- | core/src/main.js | 1 |
15 files changed, 1251 insertions, 0 deletions
diff --git a/core/src/Util/escapeHTML.js b/core/src/Util/escapeHTML.js new file mode 100644 index 00000000000..f6cf868a6d0 --- /dev/null +++ b/core/src/Util/escapeHTML.js @@ -0,0 +1,36 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * Sanitizes a HTML string by replacing all potential dangerous characters with HTML entities + * @param {string} s String to sanitize + * @return {string} Sanitized string + */ +export default function escapeHTML (s) { + return s.toString() + .split('&') + .join('&') + .split('<') + .join('<').split('>') + .join('>').split('"') + .join('"').split('\'') + .join('''); +} diff --git a/core/src/globals.js b/core/src/globals.js index 5d077f22486..ad1997c470d 100644 --- a/core/src/globals.js +++ b/core/src/globals.js @@ -51,6 +51,7 @@ import 'strengthify/strengthify.css' import OC from './OC/index' import OCP from './OCP/index' import OCA from './OCA/index' +import escapeHTML from './Util/escapeHTML' window['_'] = _ window['$'] = $ @@ -72,6 +73,7 @@ window['moment'] = moment window['OC'] = OC window['OCP'] = OCP window['OCA'] = OCA +window['escapeHTML'] = escapeHTML /** * translate a string diff --git a/core/src/jquery/avatar.js b/core/src/jquery/avatar.js new file mode 100644 index 00000000000..75aef6d958f --- /dev/null +++ b/core/src/jquery/avatar.js @@ -0,0 +1,162 @@ +/* + * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +import OC from '../OC' + +/** + * This plugin inserts the right avatar for the user, depending on, whether a + * custom avatar is uploaded - which it uses then - or not, and display a + * placeholder with the first letter of the users name instead. + * For this it queries the core_avatar_get route, thus this plugin is fit very + * tightly for owncloud, and it may not work anywhere else. + * + * You may use this on any <div></div> + * Here I'm using <div class="avatardiv"></div> as an example. + * + * There are 5 ways to call this: + * + * 1. $('.avatardiv').avatar('jdoe', 128); + * This will make the div to jdoe's fitting avatar, with a size of 128px. + * + * 2. $('.avatardiv').avatar('jdoe'); + * This will make the div to jdoe's fitting avatar. If the div already has a + * height, it will be used for the avatars size. Otherwise this plugin will + * search for 'size' DOM data, to use for avatar size. If neither are available + * it will default to 64px. + * + * 3. $('.avatardiv').avatar(); + * This will search the DOM for 'user' data, to use as the username. If there + * is no username available it will default to a placeholder with the value of + * "?". The size will be determined the same way, as the second example. + * + * 4. $('.avatardiv').avatar('jdoe', 128, true); + * This will behave like the first example, except it will also append random + * hashes to the custom avatar images, to force image reloading in IE8. + * + * 5. $('.avatardiv').avatar('jdoe', 128, undefined, true); + * This will behave like the first example, but it will hide the avatardiv, if + * it will display the default placeholder. undefined is the ie8fix from + * example 4 and can be either true, or false/undefined, to be ignored. + * + * 6. $('.avatardiv').avatar('jdoe', 128, undefined, true, callback); + * This will behave like the above example, but it will call the function + * defined in callback after the avatar is placed into the DOM. + * + */ + +$.fn.avatar = function (user, size, ie8fix, hidedefault, callback, displayname) { + var setAvatarForUnknownUser = function (target) { + target.imageplaceholder('?'); + target.css('background-color', '#b9b9b9'); + }; + + if (typeof (user) !== 'undefined') { + user = String(user); + } + if (typeof (displayname) !== 'undefined') { + displayname = String(displayname); + } + + if (typeof (size) === 'undefined') { + if (this.height() > 0) { + size = this.height(); + } else if (this.data('size') > 0) { + size = this.data('size'); + } else { + size = 64; + } + } + + this.height(size); + this.width(size); + + if (typeof (user) === 'undefined') { + if (typeof (this.data('user')) !== 'undefined') { + user = this.data('user'); + } else { + setAvatarForUnknownUser(this); + return; + } + } + + // sanitize + user = String(user).replace(/\//g, ''); + + var $div = this; + var url; + + // If this is our own avatar we have to use the version attribute + if (user === OC.getCurrentUser().uid) { + url = OC.generateUrl( + '/avatar/{user}/{size}?v={version}', + { + user: user, + size: Math.ceil(size * window.devicePixelRatio), + version: oc_userconfig.avatar.version + }); + } else { + url = OC.generateUrl( + '/avatar/{user}/{size}', + { + user: user, + size: Math.ceil(size * window.devicePixelRatio) + }); + } + + var img = new Image(); + + // If the new image loads successfully set it. + img.onload = function () { + $div.clearimageplaceholder(); + $div.append(img); + + if (typeof callback === 'function') { + callback(); + } + }; + // Fallback when avatar loading fails: + // Use old placeholder when a displayname attribute is defined, + // otherwise show the unknown user placeholder. + img.onerror = function () { + $div.clearimageplaceholder(); + if (typeof (displayname) !== 'undefined') { + $div.imageplaceholder(user, displayname); + } else { + setAvatarForUnknownUser($div); + } + + if (typeof callback === 'function') { + callback(); + } + }; + + if (size < 32) { + $div.addClass('icon-loading-small'); + } else { + $div.addClass('icon-loading'); + } + img.width = size; + img.height = size; + img.src = url; + img.alt = ''; +}; diff --git a/core/src/jquery/contactsmenu.js b/core/src/jquery/contactsmenu.js new file mode 100644 index 00000000000..8ee97dd51e9 --- /dev/null +++ b/core/src/jquery/contactsmenu.js @@ -0,0 +1,125 @@ +/* + * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +import OC from '../OC' + +const LIST = '' + + '<div class="menu popovermenu menu-left hidden contactsmenu-popover">' + + ' <ul>' + + ' <li>' + + ' <a>' + + ' <span class="icon-loading-small"></span>' + + ' </a>' + + ' </li>' + + ' </ul>' + + '</div>'; + +$.fn.contactsMenu = function (shareWith, shareType, appendTo) { + // 0 - user, 4 - email, 6 - remote + var allowedTypes = [0, 4, 6]; + if (allowedTypes.indexOf(shareType) === -1) { + return; + } + + var $div = this; + appendTo.append(LIST); + var $list = appendTo.find('div.contactsmenu-popover'); + + $div.click(function () { + if (!$list.hasClass('hidden')) { + $list.addClass('hidden'); + $list.hide(); + return; + } + + $list.removeClass('hidden'); + $list.show(); + + if ($list.hasClass('loaded')) { + return; + } + + $list.addClass('loaded'); + $.ajax(OC.generateUrl('/contactsmenu/findOne'), { + method: 'POST', + data: { + shareType: shareType, + shareWith: shareWith + } + }).then(function (data) { + $list.find('ul').find('li').addClass('hidden'); + + var actions; + if (!data.topAction) { + actions = [{ + hyperlink: '#', + title: t('core', 'No action available') + }]; + } else { + actions = [data.topAction].concat(data.actions); + } + + actions.forEach(function (action) { + var template = OC.ContactsMenu.Templates['jquery_entry']; + $list.find('ul').append(template(action)); + }); + + if (actions.length === 0) { + + } + }, function (jqXHR) { + $list.find('ul').find('li').addClass('hidden'); + + var title; + if (jqXHR.status === 404) { + title = t('core', 'No action available'); + } else { + title = t('core', 'Error fetching contact actions'); + } + + var template = OC.ContactsMenu.Templates['jquery_entry']; + $list.find('ul').append(template({ + hyperlink: '#', + title: title + })); + }); + }); + + $(document).click(function (event) { + var clickedList = ($list.has(event.target).length > 0); + var clickedTarget = ($div.has(event.target).length > 0); + + $div.each(function () { + if ($(this).is(event.target)) { + clickedTarget = true; + } + }); + + if (clickedList || clickedTarget) { + return; + } + + $list.addClass('hidden'); + $list.hide(); + }); +}; diff --git a/core/src/jquery/exists.js b/core/src/jquery/exists.js new file mode 100644 index 00000000000..bdf62ab7d4b --- /dev/null +++ b/core/src/jquery/exists.js @@ -0,0 +1,31 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +/** + * check if an element exists. + * allows you to write if ($('#myid').exists()) to increase readability + * @link http://stackoverflow.com/questions/31044/is-there-an-exists-function-for-jquery + */ +$.fn.exists = function () { + return this.length > 0; +}; diff --git a/core/src/jquery/filterattr.js b/core/src/jquery/filterattr.js new file mode 100644 index 00000000000..7675eaa1e2d --- /dev/null +++ b/core/src/jquery/filterattr.js @@ -0,0 +1,31 @@ +/* + * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +/** + * Filter jQuery selector by attribute value + */ +$.fn.filterAttr = function (attrName, attrValue) { + return this.filter(function () { + return $(this).attr(attrName) === attrValue; + }); +}; diff --git a/core/src/jquery/index.js b/core/src/jquery/index.js new file mode 100644 index 00000000000..8e2feb2b65e --- /dev/null +++ b/core/src/jquery/index.js @@ -0,0 +1,32 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import './avatar' +import './contactsmenu' +import './exists' +import './filterattr' +import './ocdialog' +import './octemplate' +import './placeholder' +import './selectrange' +import './showpassword' +import './tipsy' +import './ui-fixes' diff --git a/core/src/jquery/ocdialog.js b/core/src/jquery/ocdialog.js new file mode 100644 index 00000000000..8899d9433d1 --- /dev/null +++ b/core/src/jquery/ocdialog.js @@ -0,0 +1,270 @@ +/* + * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +$.widget('oc.ocdialog', { + options: { + width: 'auto', + height: 'auto', + closeButton: true, + closeOnEscape: true, + modal: false + }, + _create: function () { + var self = this; + + this.originalCss = { + display: this.element[0].style.display, + width: this.element[0].style.width, + height: this.element[0].style.height + }; + + this.originalTitle = this.element.attr('title'); + this.options.title = this.options.title || this.originalTitle; + + this.$dialog = $('<div class="oc-dialog" />') + .attr({ + // Setting tabIndex makes the div focusable + tabIndex: -1, + role: 'dialog' + }) + .insertBefore(this.element); + this.$dialog.append(this.element.detach()); + this.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog); + + this.$dialog.css({ + display: 'inline-block', + position: 'fixed' + }); + + this.enterCallback = null; + + $(document).on('keydown keyup', function (event) { + if ( + event.target !== self.$dialog.get(0) && + self.$dialog.find($(event.target)).length === 0 + ) { + return; + } + // Escape + if ( + event.keyCode === 27 && + event.type === 'keydown' && + self.options.closeOnEscape + ) { + event.stopImmediatePropagation(); + self.close(); + return false; + } + // Enter + if (event.keyCode === 13) { + event.stopImmediatePropagation(); + if (self.enterCallback !== null) { + self.enterCallback(); + event.preventDefault(); + return false; + } + if (event.type === 'keyup') { + event.preventDefault(); + return false; + } + // If no button is selected we trigger the primary + if ( + self.$buttonrow && + self.$buttonrow.find($(event.target)).length === 0 + ) { + var $button = self.$buttonrow.find('button.primary'); + if ($button && !$button.prop('disabled')) { + $button.trigger('click'); + } + } else if (self.$buttonrow) { + $(event.target).trigger('click'); + } + return false; + } + }); + + this._setOptions(this.options); + this._createOverlay(); + }, + _init: function () { + this.$dialog.focus(); + this._trigger('open'); + }, + _setOption: function (key, value) { + var self = this; + switch (key) { + case 'title': + if (this.$title) { + this.$title.text(value); + } else { + var $title = $('<h2 class="oc-dialog-title">' + + value + + '</h2>'); + this.$title = $title.prependTo(this.$dialog); + } + this._setSizes(); + break; + case 'buttons': + if (this.$buttonrow) { + this.$buttonrow.empty(); + } else { + var $buttonrow = $('<div class="oc-dialog-buttonrow" />'); + this.$buttonrow = $buttonrow.appendTo(this.$dialog); + } + if (value.length === 1) { + this.$buttonrow.addClass('onebutton'); + } else if (value.length === 2) { + this.$buttonrow.addClass('twobuttons'); + } else if (value.length === 3) { + this.$buttonrow.addClass('threebuttons'); + } + $.each(value, function (idx, val) { + var $button = $('<button>').text(val.text); + if (val.classes) { + $button.addClass(val.classes); + } + if (val.defaultButton) { + $button.addClass('primary'); + self.$defaultButton = $button; + } + self.$buttonrow.append($button); + $button.click(function () { + val.click.apply(self.element[0], arguments); + }); + }); + this.$buttonrow.find('button') + .on('focus', function (event) { + self.$buttonrow.find('button').removeClass('primary'); + $(this).addClass('primary'); + }); + this._setSizes(); + break; + case 'style': + if (value.buttons !== undefined) { + this.$buttonrow.addClass(value.buttons); + } + break; + case 'closeButton': + if (value) { + var $closeButton = $('<a class="oc-dialog-close"></a>'); + this.$dialog.prepend($closeButton); + $closeButton.on('click', function () { + self.close(); + }); + } else { + this.$dialog.find('.oc-dialog-close').remove(); + } + break; + case 'width': + this.$dialog.css('width', value); + break; + case 'height': + this.$dialog.css('height', value); + break; + case 'close': + this.closeCB = value; + break; + } + //this._super(key, value); + $.Widget.prototype._setOption.apply(this, arguments); + }, + _setOptions: function (options) { + //this._super(options); + $.Widget.prototype._setOptions.apply(this, arguments); + }, + _setSizes: function () { + var lessHeight = 0; + if (this.$title) { + lessHeight += this.$title.outerHeight(true); + } + if (this.$buttonrow) { + lessHeight += this.$buttonrow.outerHeight(true); + } + this.element.css({ + 'height': 'calc(100% - ' + lessHeight + 'px)' + }); + }, + _createOverlay: function () { + if (!this.options.modal) { + return; + } + + var self = this; + this.overlay = $('<div>') + .addClass('oc-dialog-dim') + .appendTo($('#content')); + this.overlay.on('click keydown keyup', function (event) { + if (event.target !== self.$dialog.get(0) && self.$dialog.find($(event.target)).length === 0) { + event.preventDefault(); + event.stopPropagation(); + return; + } + }); + }, + _destroyOverlay: function () { + if (!this.options.modal) { + return; + } + + if (this.overlay) { + this.overlay.off('click keydown keyup'); + this.overlay.remove(); + this.overlay = null; + } + }, + widget: function () { + return this.$dialog; + }, + setEnterCallback: function (callback) { + this.enterCallback = callback; + }, + unsetEnterCallback: function () { + this.enterCallback = null; + }, + close: function () { + this._destroyOverlay(); + var self = this; + // Ugly hack to catch remaining keyup events. + setTimeout(function () { + self._trigger('close', self); + }, 200); + + self.$dialog.remove(); + this.destroy(); + }, + destroy: function () { + if (this.$title) { + this.$title.remove(); + } + if (this.$buttonrow) { + this.$buttonrow.remove(); + } + + if (this.originalTitle) { + this.element.attr('title', this.originalTitle); + } + this.element.removeClass('oc-dialog-content') + .css(this.originalCss).detach().insertBefore(this.$dialog); + this.$dialog.remove(); + } +}); diff --git a/core/src/jquery/octemplate.js b/core/src/jquery/octemplate.js new file mode 100644 index 00000000000..61fbea2af5f --- /dev/null +++ b/core/src/jquery/octemplate.js @@ -0,0 +1,104 @@ +import $ from 'jquery' +import escapeHTML from '../Util/escapeHTML' + +/** + * jQuery plugin for micro templates + * + * Strings are automatically escaped, but that can be disabled by setting + * escapeFunction to null. + * + * Usage examples: + * + * var htmlStr = '<p>Bake, uncovered, until the {greasystuff} is melted and the {pasta} is heated through, about {min} minutes.</p>' + * $(htmlStr).octemplate({greasystuff: 'cheese', pasta: 'macaroni', min: 10}); + * + * var htmlStr = '<p>Welcome back {user}</p>'; + * $(htmlStr).octemplate({user: 'John Q. Public'}, {escapeFunction: null}); + * + * Be aware that the target string must be wrapped in an HTML element for the + * plugin to work. The following won't work: + * + * var textStr = 'Welcome back {user}'; + * $(textStr).octemplate({user: 'John Q. Public'}); + * + * For anything larger than one-liners, you can use a simple $.get() ajax + * request to get the template, or you can embed them it the page using the + * text/template type: + * + * <script id="contactListItemTemplate" type="text/template"> + * <tr class="contact" data-id="{id}"> + * <td class="name"> + * <input type="checkbox" name="id" value="{id}" /><span class="nametext">{name}</span> + * </td> + * <td class="email"> + * <a href="mailto:{email}">{email}</a> + * </td> + * <td class="phone">{phone}</td> + * </tr> + * </script> + * + * var $tmpl = $('#contactListItemTemplate'); + * var contacts = // fetched in some ajax call + * + * $.each(contacts, function(idx, contact) { + * $contactList.append( + * $tmpl.octemplate({ + * id: contact.getId(), + * name: contact.getDisplayName(), + * email: contact.getPreferredEmail(), + * phone: contact.getPreferredPhone(), + * }); + * ); + * }); + */ +/** + * Object Template + * Inspired by micro templating done by e.g. underscore.js + */ +const Template = { + init: function (vars, options, elem) { + // Mix in the passed in options with the default options + this.vars = vars; + this.options = $.extend({}, this.options, options); + + this.elem = elem; + var self = this; + + if (typeof this.options.escapeFunction === 'function') { + var keys = Object.keys(this.vars); + for (var key = 0; key < keys.length; key++) { + if (typeof this.vars[keys[key]] === 'string') { + this.vars[keys[key]] = self.options.escapeFunction(this.vars[keys[key]]); + } + } + } + + var _html = this._build(this.vars); + return $(_html); + }, + // From stackoverflow.com/questions/1408289/best-way-to-do-variable-interpolation-in-javascript + _build: function (o) { + var data = this.elem.attr('type') === 'text/template' ? this.elem.html() : this.elem.get(0).outerHTML; + try { + return data.replace(/{([^{}]*)}/g, + function (a, b) { + var r = o[b]; + return typeof r === 'string' || typeof r === 'number' ? r : a; + } + ); + } catch (e) { + console.error(e, 'data:', data); + } + }, + options: { + escapeFunction: escapeHTML + } +}; + +$.fn.octemplate = function (vars, options) { + vars = vars || {}; + if (this.length) { + var _template = Object.create(Template); + return _template.init(vars, options, this); + } +}; diff --git a/core/src/jquery/placeholder.js b/core/src/jquery/placeholder.js new file mode 100644 index 00000000000..074ad5029b5 --- /dev/null +++ b/core/src/jquery/placeholder.js @@ -0,0 +1,174 @@ +/** + * ownCloud + * + * @author John Molakvoæ + * @copyright 2016-2018 John Molakvoæ <skjnldsv@protonmail.com> + * @author Morris Jobke + * @copyright 2013 Morris Jobke <morris.jobke@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import $ from 'jquery' + +/* + * Adds a background color to the element called on and adds the first character + * of the passed in string. This string is also the seed for the generation of + * the background color. + * + * You have following HTML: + * + * <div id="albumart"></div> + * + * And call this from Javascript: + * + * $('#albumart').imageplaceholder('The Album Title'); + * + * Which will result in: + * + * <div id="albumart" style="background-color: rgb(121, 90, 171); ... ">T</div> + * + * You may also call it like this, to have a different background, than the seed: + * + * $('#albumart').imageplaceholder('The Album Title', 'Album Title'); + * + * Resulting in: + * + * <div id="albumart" style="background-color: rgb(121, 90, 171); ... ">A</div> + * + */ + +/* +* Alternatively, you can use the prototype function to convert your string to rgb colors: +* +* "a6741a86aded5611a8e46ce16f2ad646".toRgb() +* +* Will return the rgb parameters within the following object: +* +* Color {r: 208, g: 158, b: 109} +* +*/ + +String.prototype.toRgb = function () { + // Normalize hash + var hash = this.toLowerCase(); + + // Already a md5 hash? + if (hash.match(/^([0-9a-f]{4}-?){8}$/) === null) { + hash = md5(hash); + } + + hash = hash.replace(/[^0-9a-f]/g, ''); + + function Color (r, g, b) { + this.r = r; + this.g = g; + this.b = b; + } + + function stepCalc (steps, ends) { + var step = new Array(3); + step[0] = (ends[1].r - ends[0].r) / steps; + step[1] = (ends[1].g - ends[0].g) / steps; + step[2] = (ends[1].b - ends[0].b) / steps; + return step; + } + + function mixPalette (steps, color1, color2) { + var count = steps + 1; + var palette = new Array(); + palette.push(color1); + var step = stepCalc(steps, [color1, color2]) + for (var i = 1; i < steps; i++) { + var r = parseInt(color1.r + (step[0] * i)); + var g = parseInt(color1.g + (step[1] * i)); + var b = parseInt(color1.b + (step[2] * i)); + palette.push(new Color(r, g, b)); + } + return palette; + } + + var red = new Color(182, 70, 157); + var yellow = new Color(221, 203, 85); + var blue = new Color(0, 130, 201); // Nextcloud blue + // Number of steps to go from a color to another + // 3 colors * 6 will result in 18 generated colors + var steps = 6; + + var palette1 = mixPalette(steps, red, yellow); + var palette2 = mixPalette(steps, yellow, blue); + var palette3 = mixPalette(steps, blue, red); + + var finalPalette = palette1.concat(palette2).concat(palette3); + + // Convert a string to an integer evenly + function hashToInt (hash, maximum) { + var finalInt = 0; + var result = Array(); + + // Splitting evenly the string + for (var i = 0; i < hash.length; i++) { + // chars in md5 goes up to f, hex:16 + result.push(parseInt(hash.charAt(i), 16) % 16); + } + // Adds up all results + for (var j in result) { + finalInt += result[j]; + } + // chars in md5 goes up to f, hex:16 + // make sure we're always using int in our operation + return parseInt(parseInt(finalInt) % maximum); + } + + return finalPalette[hashToInt(hash, steps * 3)]; +}; + +$.fn.imageplaceholder = function (seed, text, size) { + text = text || seed; + + // Compute the hash + var rgb = seed.toRgb(); + this.css('background-color', 'rgb(' + rgb.r + ', ' + rgb.g + ', ' + rgb.b + ')'); + + // Placeholders are square + var height = this.height() || size || 32; + this.height(height); + this.width(height); + + // CSS rules + this.css('color', '#fff'); + this.css('font-weight', 'normal'); + this.css('text-align', 'center'); + + // calculate the height + this.css('line-height', height + 'px'); + this.css('font-size', (height * 0.55) + 'px'); + + if (seed !== null && seed.length) { + this.html(text[0].toUpperCase()); + } +}; + +$.fn.clearimageplaceholder = function () { + this.css('background-color', ''); + this.css('color', ''); + this.css('font-weight', ''); + this.css('text-align', ''); + this.css('line-height', ''); + this.css('font-size', ''); + this.html(''); + this.removeClass('icon-loading'); + this.removeClass('icon-loading-small'); +}; diff --git a/core/src/jquery/selectrange.js b/core/src/jquery/selectrange.js new file mode 100644 index 00000000000..b7e20361805 --- /dev/null +++ b/core/src/jquery/selectrange.js @@ -0,0 +1,43 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +/** + * select a range in an input field + * @link http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area + * @param {type} start + * @param {type} end + */ +$.fn.selectRange = function (start, end) { + return this.each(function () { + if (this.setSelectionRange) { + this.focus(); + this.setSelectionRange(start, end); + } else if (this.createTextRange) { + var range = this.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', start); + range.select(); + } + }); +}; diff --git a/core/src/jquery/showpassword.js b/core/src/jquery/showpassword.js new file mode 100644 index 00000000000..4dc39340e76 --- /dev/null +++ b/core/src/jquery/showpassword.js @@ -0,0 +1,149 @@ +/* + * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +/* +* @name Show Password +* @description +* @version 1.3 +* @requires Jquery 1.5 +* +* @author Jan Jarfalk +* @author-email jan.jarfalk@unwrongest.com +* @author-website http://www.unwrongest.com +* +* @special-thanks Michel Gratton +* +* @licens MIT License - http://www.opensource.org/licenses/mit-license.php +*/ +$.fn.extend({ + showPassword: function (c) { + + // Setup callback object + var callback = {'fn': null, 'args': {}}; + callback.fn = c; + + // Clones passwords and turn the clones into text inputs + var cloneElement = function (element) { + + var $element = $(element); + + var $clone = $("<input />"); + + // Name added for JQuery Validation compatibility + // Element name is required to avoid script warning. + $clone.attr({ + 'type': 'text', + 'class': $element.attr('class'), + 'style': $element.attr('style'), + 'size': $element.attr('size'), + 'name': $element.attr('name') + '-clone', + 'tabindex': $element.attr('tabindex'), + 'autocomplete': 'off' + }); + + if ($element.attr('placeholder') !== undefined) { + $clone.attr('placeholder', $element.attr('placeholder')); + } + + return $clone; + + }; + + // Transfers values between two elements + var update = function (a, b) { + b.val(a.val()); + }; + + // Shows a or b depending on checkbox + var setState = function (checkbox, a, b) { + + if (checkbox.is(':checked')) { + update(a, b); + b.show(); + a.hide(); + } else { + update(b, a); + b.hide(); + a.show(); + } + + }; + + return this.each(function () { + + var $input = $(this), + $checkbox = $($input.data('typetoggle')); + + // Create clone + var $clone = cloneElement($input); + $clone.insertAfter($input); + + // Set callback arguments + if (callback.fn) { + callback.args.input = $input; + callback.args.checkbox = $checkbox; + callback.args.clone = $clone; + } + + + $checkbox.bind('click', function () { + setState($checkbox, $input, $clone); + }); + + $input.bind('keyup', function () { + update($input, $clone); + }); + + $clone.bind('keyup', function () { + update($clone, $input); + + // Added for JQuery Validation compatibility + // This will trigger validation if it's ON for keyup event + $input.trigger('keyup'); + + }); + + // Added for JQuery Validation compatibility + // This will trigger validation if it's ON for blur event + $clone.bind('blur', function () { + $input.trigger('focusout'); + }); + + setState($checkbox, $input, $clone); + + // set type of password field clone (type=text) to password right on submit + // to prevent browser save the value of this field + $clone.closest('form').submit(function (e) { + // .prop has to be used, because .attr throws + // an error while changing a type of an input + // element + $clone.prop('type', 'password'); + }); + + if (callback.fn) { + callback.fn(callback.args); + } + + }); + } +}); diff --git a/core/src/jquery/tipsy.js b/core/src/jquery/tipsy.js new file mode 100644 index 00000000000..816a93fb9a9 --- /dev/null +++ b/core/src/jquery/tipsy.js @@ -0,0 +1,83 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import $ from 'jquery' + +/** + * $ tipsy shim for the bootstrap tooltip + * @deprecated + */ +$.fn.tipsy = function (argument) { + console.warn('Deprecation warning: tipsy is deprecated. Use tooltip instead.'); + if (typeof argument === 'object' && argument !== null) { + + // tipsy defaults + var options = { + placement: 'bottom', + delay: {'show': 0, 'hide': 0}, + trigger: 'hover', + html: false, + container: 'body' + }; + if (argument.gravity) { + switch (argument.gravity) { + case 'n': + case 'nw': + case 'ne': + options.placement = 'bottom'; + break; + case 's': + case 'sw': + case 'se': + options.placement = 'top'; + break; + case 'w': + options.placement = 'right'; + break; + case 'e': + options.placement = 'left'; + break; + } + } + if (argument.trigger) { + options.trigger = argument.trigger; + } + if (argument.delayIn) { + options.delay.show = argument.delayIn; + } + if (argument.delayOut) { + options.delay.hide = argument.delayOut; + } + if (argument.html) { + options.html = true; + } + if (argument.fallback) { + options.title = argument.fallback; + } + // destroy old tooltip in case the title has changed + $.fn.tooltip.call(this, 'destroy'); + $.fn.tooltip.call(this, options); + } else { + this.tooltip(argument); + $.fn.tooltip.call(this, argument); + } + return this; +};
\ No newline at end of file diff --git a/core/src/jquery/ui-fixes.js b/core/src/jquery/ui-fixes.js new file mode 100644 index 00000000000..d3cebfc346e --- /dev/null +++ b/core/src/jquery/ui-fixes.js @@ -0,0 +1,8 @@ +import $ from 'jquery' + +// Set autocomplete width the same as the related input +// See http://stackoverflow.com/a/11845718 +$.ui.autocomplete.prototype._resizeMenu = function () { + var ul = this.menu.element; + ul.outerWidth(this.element.outerWidth()); +}; diff --git a/core/src/main.js b/core/src/main.js index 85e8200f10c..baded7067b4 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -23,6 +23,7 @@ import '@babel/polyfill' import './globals' import $ from 'jquery' +import './jquery/index' import {registerAppsSlideToggle} from './OC/apps' $(document).ready(function () { |