'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'],
'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'],
'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Core\System;
+use Friendica\Core\Theme;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\Model\Event;
$events[$key]['item'] = $event_item;
}
+ // ACL blocks are loaded in modals in frio
+ $a->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
+ $a->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
+ $a->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
+ $a->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
+
$o = Renderer::replaceMacros($tpl, [
'$tabs' => $tabs,
'$title' => L10n::t('Events'),
$perms = ACL::getDefaultUserPermissions($orig_event);
if (!$cid && in_array($mode, ['new', 'copy'])) {
- $acl = ACL::getFullSelectorHTML($a->user, false, $orig_event);
+ $acl = ACL::getFullSelectorHTML($a->page, $a->user, false, $perms);
} else {
$acl = '';
}
'$cid' => $cid,
'$uri' => $uri,
- '$allow_cid' => json_encode($perms['allow_cid']),
- '$allow_gid' => json_encode($perms['allow_gid']),
- '$deny_cid' => json_encode($perms['deny_cid']),
- '$deny_gid' => json_encode($perms['deny_gid']),
-
'$title' => L10n::t('Event details'),
'$desc' => L10n::t('Starting date and Title are required.'),
'$s_text' => L10n::t('Event Starts:') . ' <span class="required" title="' . L10n::t('Required') . '">*</span>',
(strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) ||
strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
'default_perms' => ACL::getDefaultUserPermissions($a->user),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
(strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) ||
strlen($a->user['deny_cid']) || strlen($a->user['deny_gid']))) ? 'lock' : 'unlock'),
'default_perms' => ACL::getDefaultUserPermissions($a->user),
- 'acl' => ACL::getFullSelectorHTML($a->user, true, $default_permissions),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true, $default_permissions),
'bang' => (($gid || $cid || $nets) ? '!' : ''),
'visitor' => 'block',
'profile_uid' => local_user(),
$tpl = Renderer::getMarkupTemplate('photos_upload.tpl');
- $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML($a->user));
+ $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML($a->page, $a->user));
$o .= Renderer::replaceMacros($tpl,[
'$pagename' => L10n::t('Upload Photos'),
$album_e = $ph[0]['album'];
$caption_e = $ph[0]['desc'];
- $aclselect_e = ACL::getFullSelectorHTML($a->user, false, $ph[0]);
+ $aclselect_e = ACL::getFullSelectorHTML($a->page, $a->user, false, $ph[0]);
$edit = Renderer::replaceMacros($edit_tpl, [
'$id' => $ph[0]['id'],
'$permissions' => L10n::t('Default Post Permissions'),
'$permdesc' => L10n::t("\x28click to open/close\x29"),
'$visibility' => $profile['net-publish'],
- '$aclselect' => ACL::getFullSelectorHTML($a->user),
+ '$aclselect' => ACL::getFullSelectorHTML($a->page, $a->user),
'$suggestme' => $suggestme,
'$blockwall'=> $blockwall, // array('blockwall', L10n::t('Allow friends to post to your profile page:'), !$blockwall, ''),
'$blocktags'=> $blocktags, // array('blocktags', L10n::t('Allow friends to tag your posts:'), !$blocktags, ''),
namespace Friendica\Core;
+use Friendica\App\Page;
use Friendica\BaseObject;
-use Friendica\Content\Feature;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
-use Friendica\Model\GContact;
-use Friendica\Core\Session;
-use Friendica\Util\Network;
use Friendica\Model\Group;
/**
/**
* Return the full jot ACL selector HTML
*
+ * @param Page $page
* @param array $user User array
- * @param bool $show_jotnets
- * @param array $default_permissions Static defaults permission array: ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']
+ * @param bool $for_federation
+ * @param array $default_permissions Static defaults permission array:
+ * [
+ * 'allow_cid' => [],
+ * 'allow_gid' => [],
+ * 'deny_cid' => [],
+ * 'deny_gid' => [],
+ * 'hidewall' => true/false
+ * ]
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public static function getFullSelectorHTML(array $user = null, $show_jotnets = false, array $default_permissions = [])
+ public static function getFullSelectorHTML(Page $page, array $user = null, bool $for_federation = false, array $default_permissions = [])
{
+ $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
+ $page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
+ $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
+ $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
+
// Defaults user permissions
if (empty($default_permissions)) {
$default_permissions = self::getDefaultUserPermissions($user);
}
+ if (count($default_permissions['allow_cid'])
+ + count($default_permissions['allow_gid'])
+ + count($default_permissions['deny_cid'])
+ + count($default_permissions['deny_gid'])) {
+ $visibility = 'custom';
+ } else {
+ $visibility = 'public';
+ // Default permission display for custom panel
+ $default_permissions['allow_gid'] = [Group::FOLLOWERS];
+ }
+
$jotnets_fields = [];
- if ($show_jotnets) {
+ if ($for_federation) {
$mail_enabled = false;
$pubmail_enabled = false;
if (function_exists('imap_open') && !Config::get('system', 'imap_disabled')) {
- $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
+ $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', $user['úid']]);
if (DBA::isResult($mailacct)) {
$mail_enabled = true;
$pubmail_enabled = !empty($mailacct['pubmail']);
}
}
+ $acl_contacts = self::getContactListByUserId($user['uid']);
+
+ $acl_groups = self::getGroupListByUserId($user['uid']);
+
+ $acl_list = array_merge($acl_groups, $acl_contacts);
+
$tpl = Renderer::getMarkupTemplate('acl_selector.tpl');
$o = Renderer::replaceMacros($tpl, [
- '$showall' => L10n::t('Visible to everybody'),
- '$show' => L10n::t('show'),
- '$hide' => L10n::t('don\'t show'),
- '$allowcid' => json_encode(($default_permissions['allow_cid'] ?? '') ?: []), // We need arrays for
- '$allowgid' => json_encode(($default_permissions['allow_gid'] ?? '') ?: []), // Javascript since we
- '$denycid' => json_encode(($default_permissions['deny_cid'] ?? '') ?: []), // call .remove() and
- '$denygid' => json_encode(($default_permissions['deny_gid'] ?? '') ?: []), // .push() on these values
- '$networks' => $show_jotnets,
- '$emailcc' => L10n::t('CC: email addresses'),
- '$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
- '$jotnets_enabled' => empty($default_permissions['hidewall']),
+ '$public_title' => L10n::t('Public'),
+ '$public_desc' => L10n::t('This content will be shown to all your followers and can be seen in the community pages and by anyone with its link.'),
+ '$custom_title' => L10n::t('Limited/Private'),
+ '$custom_desc' => L10n::t('This content will be shown only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.'),
+ '$allow_label' => L10n::t('Show to:'),
+ '$deny_label' => L10n::t('Except to:'),
+ '$emailcc' => L10n::t('CC: email addresses'),
+ '$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
'$jotnets_summary' => L10n::t('Connectors'),
- '$jotnets_fields' => $jotnets_fields,
'$jotnets_disabled_label' => L10n::t('Connectors disabled, since "%s" is enabled.', L10n::t('Hide your profile details from unknown viewers?')),
- '$aclModalTitle' => L10n::t('Permissions'),
- '$aclModalDismiss' => L10n::t('Close'),
- '$features' => [
- 'aclautomention' => !empty($user['uid']) && Feature::isEnabled($user['uid'], 'aclautomention') ? 'true' : 'false'
- ],
+ '$visibility' => $visibility,
+ '$acl_contacts' => $acl_contacts,
+ '$acl_groups' => $acl_groups,
+ '$acl_list' => $acl_list,
+ '$contact_allow' => implode(',', $default_permissions['allow_cid']),
+ '$group_allow' => implode(',', $default_permissions['allow_gid']),
+ '$contact_deny' => implode(',', $default_permissions['deny_cid']),
+ '$group_deny' => implode(',', $default_permissions['deny_gid']),
+ '$for_federation' => $for_federation,
+ '$jotnets_fields' => $jotnets_fields,
+ '$user_hidewall' => $default_permissions['hidewall'],
]);
return $o;
'nickname' => $app->user['nickname'],
'lockstate' => ((is_array($app->user) && ((strlen($app->user['allow_cid'])) || (strlen($app->user['allow_gid'])) || (strlen($app->user['deny_cid'])) || (strlen($app->user['deny_gid'])))) ? 'lock' : 'unlock'),
'default_perms' => ACL::getDefaultUserPermissions($app->user),
- 'acl' => ACL::getFullSelectorHTML($app->user, true),
+ 'acl' => ACL::getFullSelectorHTML($app->page, $app->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'],
'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
|| strlen($a->user['deny_cid'])
|| strlen($a->user['deny_gid'])
) ? 'lock' : 'unlock',
- 'acl' => $is_owner ? ACL::getFullSelectorHTML($a->user, true) : '',
+ 'acl' => $is_owner ? ACL::getFullSelectorHTML($a->page, $a->user, true) : '',
'bang' => '',
'visitor' => $is_owner || $commvisitor ? 'block' : 'none',
'profile_uid' => $a->profile['profile_uid'],
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2013 Tim Schlechter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+/*
+ * friendica-tagsinput v0.8.0
+ *
+ */
+
+.twitter-typeahead .tt-query,
+.twitter-typeahead .tt-hint {
+ margin-bottom: 0;
+}
+
+.twitter-typeahead .tt-hint
+{
+ display: none;
+}
+
+.tt-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ font-size: 14px;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
+ cursor: pointer;
+}
+
+.tt-suggestion {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.428571429;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.tt-suggestion:hover,
+.tt-suggestion:focus {
+ color: #ffffff;
+ text-decoration: none;
+ outline: 0;
+ background-color: #428bca;
+}
--- /dev/null
+/*
+ * friendica-tagsinput v0.8.0
+ *
+ * Non-Bootstrap edition
+ */
+
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: 700;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+}
+
+.label-default {
+ background-color: #777777;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+ background-color: #5e5e5e;
+}
+.label-primary {
+ background-color: #337ab7;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #286090;
+}
+.label-success {
+ background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
+}
+.label-info {
+ background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
+}
+.label-warning {
+ background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
+}
+.label-danger {
+ background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
+}
+
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ background-color: #eeeeee;
+ opacity: 1;
+}
+.form-control[disabled],
+fieldset[disabled] .form-control {
+ cursor: not-allowed;
+}
+
+
+
+
+.friendica-tagsinput {
+ background-color: #fff;
+ border: 1px solid #ccc;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ display: inline-block;
+ padding: 4px 6px;
+ color: #555;
+ vertical-align: middle;
+ border-radius: 4px;
+ max-width: 100%;
+ line-height: 22px;
+ cursor: text;
+ height: auto;
+}
+
+.friendica-tagsinput.input-lg {
+ line-height: 27px;
+}
+
+.friendica-tagsinput input {
+ border: none;
+ box-shadow: none;
+ outline: none;
+ background-color: transparent;
+ padding: 0 6px;
+ margin: 0;
+ width: auto;
+ max-width: inherit;
+}
+
+.friendica-tagsinput.form-control input::-moz-placeholder {
+ color: #777;
+ opacity: 1;
+}
+
+.friendica-tagsinput.form-control input:-ms-input-placeholder {
+ color: #777;
+}
+
+.friendica-tagsinput.form-control input::-webkit-input-placeholder {
+ color: #777;
+}
+
+.friendica-tagsinput input:focus {
+ border: none;
+ box-shadow: none;
+}
+
+.friendica-tagsinput .tag {
+ margin: 0 2px 2px 0;
+ color: white;
+ font-weight: normal;
+}
+
+.friendica-tagsinput .tag img {
+ width: auto;
+ height: 1.5em;
+ vertical-align: text-top;
+ margin-right: 8px;
+}
+
+.friendica-tagsinput .tag [data-role="remove"] {
+ margin-left: 8px;
+ cursor: pointer;
+}
+
+.friendica-tagsinput .tag [data-role="remove"]:after {
+ content: "x";
+ padding: 0px 2px;
+ font-weight: bold;
+}
+
+.friendica-tagsinput .tag [data-role="remove"]:hover {
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.friendica-tagsinput .tag [data-role="remove"]:hover:active {
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
--- /dev/null
+/*
+ * friendica-tagsinput v0.8.0
+ * Based on bootstrap-tagsinput v0.8.0
+ *
+ * Adds:
+ * - optional thumbnail
+ * - copying source input element class to the pseudo-input element
+ *
+ */
+
+(function ($) {
+ "use strict";
+
+ var defaultOptions = {
+ tagClass: function(item) {
+ return 'label label-info';
+ },
+ focusClass: 'focus',
+ itemValue: function(item) {
+ return item ? item.toString() : item;
+ },
+ itemText: function(item) {
+ return this.itemValue(item);
+ },
+ itemTitle: function(item) {
+ return null;
+ },
+ itemThumb: function(item) {
+ return null;
+ },
+ freeInput: true,
+ addOnBlur: true,
+ maxTags: undefined,
+ maxChars: undefined,
+ confirmKeys: [13, 44],
+ delimiter: ',',
+ delimiterRegex: null,
+ cancelConfirmKeysOnEmpty: false,
+ onTagExists: function(item, $tag) {
+ $tag.hide().fadeIn();
+ },
+ trimValue: false,
+ allowDuplicates: false,
+ triggerChange: true
+ };
+
+ /**
+ * Constructor function
+ */
+ function TagsInput(element, options) {
+ this.isInit = true;
+ this.itemsArray = [];
+
+ this.$element = $(element);
+ this.$element.hide();
+
+ this.isSelect = (element.tagName === 'SELECT');
+ this.multiple = (this.isSelect && element.hasAttribute('multiple'));
+ this.objectItems = options && options.itemValue;
+ this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
+ this.inputSize = Math.max(1, this.placeholderText.length);
+
+ this.$container = $('<div class="friendica-tagsinput"></div>');
+ this.$container.addClass(this.$element.attr('class'));
+ this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
+
+ this.$element.before(this.$container);
+
+ this.build(options);
+ this.isInit = false;
+ }
+
+ TagsInput.prototype = {
+ constructor: TagsInput,
+
+ /**
+ * Adds the given item as a new tag. Pass true to dontPushVal to prevent
+ * updating the elements val()
+ */
+ add: function(item, dontPushVal, options) {
+ let self = this;
+
+ if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
+ return;
+
+ // Ignore falsey values, except false
+ if (item !== false && !item)
+ return;
+
+ // Trim value
+ if (typeof item === "string" && self.options.trimValue) {
+ item = $.trim(item);
+ }
+
+ // Throw an error when trying to add an object while the itemValue option was not set
+ if (typeof item === "object" && !self.objectItems)
+ throw("Can't add objects when itemValue option is not set");
+
+ // Ignore strings only containg whitespace
+ if (item.toString().match(/^\s*$/))
+ return;
+
+ // If SELECT but not multiple, remove current tag
+ if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
+ self.remove(self.itemsArray[0]);
+
+ if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
+ var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
+ var items = item.split(delimiter);
+ if (items.length > 1) {
+ for (var i = 0; i < items.length; i++) {
+ this.add(items[i], true);
+ }
+
+ if (!dontPushVal)
+ self.pushVal(self.options.triggerChange);
+ return;
+ }
+ }
+
+ var itemValue = self.options.itemValue(item),
+ itemText = self.options.itemText(item),
+ tagClass = self.options.tagClass(item),
+ itemTitle = self.options.itemTitle(item),
+ itemThumb = self.options.itemThumb(item);
+
+ // Ignore items allready added
+ var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
+ if (existing && !self.options.allowDuplicates) {
+ // Invoke onTagExists
+ if (self.options.onTagExists) {
+ var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
+ self.options.onTagExists(item, $existingTag);
+ }
+ return;
+ }
+
+ // if length greater than limit
+ if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
+ return;
+
+ // raise beforeItemAdd arg
+ var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
+ self.$element.trigger(beforeItemAddEvent);
+ if (beforeItemAddEvent.cancel)
+ return;
+
+ // register item in internal array and map
+ self.itemsArray.push(item);
+
+ // add a tag element
+ var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' +
+ (itemThumb !== null ? '<img src="' + itemThumb + '" alt="">' : '') +
+ htmlEncode(itemText) + '<span data-role="remove"></span>' +
+ '</span>');
+ $tag.data('item', item);
+ self.findInputWrapper().before($tag);
+ $tag.after(' ');
+
+ // Check to see if the tag exists in its raw or uri-encoded form
+ var optionExists = (
+ $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length ||
+ $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length
+ );
+
+ // add <option /> if item represents a value not present in one of the <select />'s options
+ if (self.isSelect && !optionExists) {
+ var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
+ $option.data('item', item);
+ $option.attr('value', itemValue);
+ self.$element.append($option);
+ }
+
+ if (!dontPushVal)
+ self.pushVal(self.options.triggerChange);
+
+ // Add class when reached maxTags
+ if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
+ self.$container.addClass('friendica-tagsinput-max');
+
+ // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
+ if ($('.typeahead, .twitter-typeahead', self.$container).length) {
+ self.$input.typeahead('val', '');
+ }
+
+ if (this.isInit) {
+ self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
+ } else {
+ self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
+ }
+ },
+
+ /**
+ * Removes the given item. Pass true to dontPushVal to prevent updating the
+ * elements val()
+ */
+ remove: function(item, dontPushVal, options) {
+ var self = this;
+
+ if (self.objectItems) {
+ if (typeof item === "object")
+ item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } );
+ else
+ item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } );
+
+ item = item[item.length-1];
+ }
+
+ if (item) {
+ var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
+ self.$element.trigger(beforeItemRemoveEvent);
+ if (beforeItemRemoveEvent.cancel)
+ return;
+
+ $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
+ $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
+ if($.inArray(item, self.itemsArray) !== -1)
+ self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
+ }
+
+ if (!dontPushVal)
+ self.pushVal(self.options.triggerChange);
+
+ // Remove class when reached maxTags
+ if (self.options.maxTags > self.itemsArray.length)
+ self.$container.removeClass('friendica-tagsinput-max');
+
+ self.$element.trigger($.Event('itemRemoved', { item: item, options: options }));
+ },
+
+ /**
+ * Removes all items
+ */
+ removeAll: function() {
+ var self = this;
+
+ $('.tag', self.$container).remove();
+ $('option', self.$element).remove();
+
+ while(self.itemsArray.length > 0)
+ self.itemsArray.pop();
+
+ self.pushVal(self.options.triggerChange);
+ },
+
+ /**
+ * Refreshes the tags so they match the text/value of their corresponding
+ * item.
+ */
+ refresh: function() {
+ var self = this;
+ $('.tag', self.$container).each(function() {
+ var $tag = $(this),
+ item = $tag.data('item'),
+ itemValue = self.options.itemValue(item),
+ itemText = self.options.itemText(item),
+ tagClass = self.options.tagClass(item);
+
+ // Update tag's class and inner text
+ $tag.attr('class', null);
+ $tag.addClass('tag ' + htmlEncode(tagClass));
+ $tag.contents().filter(function() {
+ return this.nodeType == 3;
+ })[0].nodeValue = htmlEncode(itemText);
+
+ if (self.isSelect) {
+ var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
+ option.attr('value', itemValue);
+ }
+ });
+ },
+
+ /**
+ * Returns the items added as tags
+ */
+ items: function() {
+ return this.itemsArray;
+ },
+
+ /**
+ * Assembly value by retrieving the value of each item, and set it on the
+ * element.
+ */
+ pushVal: function() {
+ var self = this,
+ val = $.map(self.items(), function(item) {
+ return self.options.itemValue(item).toString();
+ });
+
+ self.$element.val(val, true);
+
+ if (self.options.triggerChange)
+ self.$element.trigger('change');
+ },
+
+ /**
+ * Initializes the tags input behaviour on the element
+ */
+ build: function(options) {
+ var self = this;
+
+ self.options = $.extend({}, defaultOptions, options);
+ // When itemValue is set, freeInput should always be false
+ if (self.objectItems)
+ self.options.freeInput = false;
+
+ makeOptionItemFunction(self.options, 'itemValue');
+ makeOptionItemFunction(self.options, 'itemText');
+ makeOptionItemFunction(self.options, 'itemThumb');
+ makeOptionFunction(self.options, 'tagClass');
+
+ // Typeahead Bootstrap version 2.3.2
+ if (self.options.typeahead) {
+ var typeahead = self.options.typeahead || {};
+
+ makeOptionFunction(typeahead, 'source');
+
+ self.$input.typeahead($.extend({}, typeahead, {
+ source: function (query, process) {
+ function processItems(items) {
+ var texts = [];
+
+ for (var i = 0; i < items.length; i++) {
+ var text = self.options.itemText(items[i]);
+ map[text] = items[i];
+ texts.push(text);
+ }
+ process(texts);
+ }
+
+ this.map = {};
+ var map = this.map,
+ data = typeahead.source(query);
+
+ if ($.isFunction(data.success)) {
+ // support for Angular callbacks
+ data.success(processItems);
+ } else if ($.isFunction(data.then)) {
+ // support for Angular promises
+ data.then(processItems);
+ } else {
+ // support for functions and jquery promises
+ $.when(data)
+ .then(processItems);
+ }
+ },
+ updater: function (text) {
+ self.add(this.map[text]);
+ return this.map[text];
+ },
+ matcher: function (text) {
+ return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
+ },
+ sorter: function (texts) {
+ return texts.sort();
+ },
+ highlighter: function (text) {
+ var regex = new RegExp( '(' + this.query + ')', 'gi' );
+ return text.replace( regex, "<strong>$1</strong>" );
+ }
+ }));
+ }
+
+ // typeahead.js
+ if (self.options.typeaheadjs) {
+ var typeaheadConfig = null;
+ var typeaheadDatasets = {};
+
+ // Determine if main configurations were passed or simply a dataset
+ var typeaheadjs = self.options.typeaheadjs;
+ if ($.isArray(typeaheadjs)) {
+ typeaheadConfig = typeaheadjs[0];
+ typeaheadDatasets = typeaheadjs[1];
+ } else {
+ typeaheadDatasets = typeaheadjs;
+ }
+
+ self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
+ if (typeaheadDatasets.valueKey)
+ self.add(datum[typeaheadDatasets.valueKey]);
+ else
+ self.add(datum);
+ self.$input.typeahead('val', '');
+ }, self));
+ }
+
+ self.$container.on('click', $.proxy(function(event) {
+ if (! self.$element.attr('disabled')) {
+ self.$input.removeAttr('disabled');
+ }
+ self.$input.focus();
+ }, self));
+
+ if (self.options.addOnBlur && self.options.freeInput) {
+ self.$input.on('focusout', $.proxy(function(event) {
+ // HACK: only process on focusout when no typeahead opened, to
+ // avoid adding the typeahead text as tag
+ if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
+ self.add(self.$input.val());
+ self.$input.val('');
+ }
+ }, self));
+ }
+
+ // Toggle the 'focus' css class on the container when it has focus
+ self.$container.on({
+ focusin: function() {
+ self.$container.addClass(self.options.focusClass);
+ },
+ focusout: function() {
+ self.$container.removeClass(self.options.focusClass);
+ },
+ });
+
+ self.$container.on('keydown', 'input', $.proxy(function(event) {
+ var $input = $(event.target),
+ $inputWrapper = self.findInputWrapper();
+
+ if (self.$element.attr('disabled')) {
+ self.$input.attr('disabled', 'disabled');
+ return;
+ }
+
+ switch (event.which) {
+ // BACKSPACE
+ case 8:
+ if (doGetCaretPosition($input[0]) === 0) {
+ var prev = $inputWrapper.prev();
+ if (prev.length) {
+ self.remove(prev.data('item'));
+ }
+ }
+ break;
+
+ // DELETE
+ case 46:
+ if (doGetCaretPosition($input[0]) === 0) {
+ var next = $inputWrapper.next();
+ if (next.length) {
+ self.remove(next.data('item'));
+ }
+ }
+ break;
+
+ // LEFT ARROW
+ case 37:
+ // Try to move the input before the previous tag
+ var $prevTag = $inputWrapper.prev();
+ if ($input.val().length === 0 && $prevTag[0]) {
+ $prevTag.before($inputWrapper);
+ $input.focus();
+ }
+ break;
+ // RIGHT ARROW
+ case 39:
+ // Try to move the input after the next tag
+ var $nextTag = $inputWrapper.next();
+ if ($input.val().length === 0 && $nextTag[0]) {
+ $nextTag.after($inputWrapper);
+ $input.focus();
+ }
+ break;
+ default:
+ // ignore
+ }
+
+ // Reset internal input's size
+ var textLength = $input.val().length,
+ wordSpace = Math.ceil(textLength / 5),
+ size = textLength + wordSpace + 1;
+ $input.attr('size', Math.max(this.inputSize, $input.val().length));
+ }, self));
+
+ self.$container.on('keypress', 'input', $.proxy(function(event) {
+ var $input = $(event.target);
+
+ if (self.$element.attr('disabled')) {
+ self.$input.attr('disabled', 'disabled');
+ return;
+ }
+
+ var text = $input.val(),
+ maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
+ if (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached) {
+ // Only attempt to add a tag if there is data in the field
+ if (self.options.freeInput && text.length !== 0) {
+ self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
+ $input.val('');
+ }
+
+ // If the field is empty, let the event triggered fire as usual
+ if (self.options.cancelConfirmKeysOnEmpty === false) {
+ event.preventDefault();
+ }
+ }
+
+ // Reset internal input's size
+ var textLength = $input.val().length,
+ wordSpace = Math.ceil(textLength / 5),
+ size = textLength + wordSpace + 1;
+ $input.attr('size', Math.max(this.inputSize, $input.val().length));
+ }, self));
+
+ // Remove icon clicked
+ self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
+ if (self.$element.attr('disabled')) {
+ return;
+ }
+ self.remove($(event.target).closest('.tag').data('item'));
+ }, self));
+
+ // Only add existing value as tags when using strings as tags
+ if (self.options.itemValue === defaultOptions.itemValue) {
+ if (self.$element[0].tagName === 'INPUT') {
+ self.add(self.$element.val());
+ } else {
+ $('option', self.$element).each(function() {
+ self.add($(this).attr('value'), true);
+ });
+ }
+ }
+ },
+
+ /**
+ * Removes all tagsinput behaviour and unregsiter all event handlers
+ */
+ destroy: function() {
+ var self = this;
+
+ // Unbind events
+ self.$container.off('keypress', 'input');
+ self.$container.off('click', '[role=remove]');
+
+ self.$container.remove();
+ self.$element.removeData('tagsinput');
+ self.$element.show();
+ },
+
+ /**
+ * Sets focus on the tagsinput
+ */
+ focus: function() {
+ this.$input.focus();
+ },
+
+ /**
+ * Returns the internal input element
+ */
+ input: function() {
+ return this.$input;
+ },
+
+ /**
+ * Returns the element which is wrapped around the internal input. This
+ * is normally the $container, but typeahead.js moves the $input element.
+ */
+ findInputWrapper: function() {
+ var elt = this.$input[0],
+ container = this.$container[0];
+ while(elt && elt.parentNode !== container)
+ elt = elt.parentNode;
+
+ return $(elt);
+ }
+ };
+
+ /**
+ * Register JQuery plugin
+ */
+ $.fn.tagsinput = function(arg1, arg2, arg3) {
+ var results = [];
+
+ this.each(function() {
+ var tagsinput = $(this).data('tagsinput');
+ // Initialize a new tags input
+ if (!tagsinput) {
+ tagsinput = new TagsInput(this, arg1);
+ $(this).data('tagsinput', tagsinput);
+ results.push(tagsinput);
+
+ if (this.tagName === 'SELECT') {
+ $('option', $(this)).attr('selected', 'selected');
+ }
+
+ // Init tags from $(this).val()
+ $(this).val($(this).val());
+ } else if (!arg1 && !arg2) {
+ // tagsinput already exists
+ // no function, trying to init
+ results.push(tagsinput);
+ } else if(tagsinput[arg1] !== undefined) {
+ // Invoke function on existing tags input
+ if(tagsinput[arg1].length === 3 && arg3 !== undefined){
+ var retVal = tagsinput[arg1](arg2, null, arg3);
+ }else{
+ var retVal = tagsinput[arg1](arg2);
+ }
+ if (retVal !== undefined)
+ results.push(retVal);
+ }
+ });
+
+ if ( typeof arg1 == 'string') {
+ // Return the results from the invoked function calls
+ return results.length > 1 ? results : results[0];
+ } else {
+ return results;
+ }
+ };
+
+ $.fn.tagsinput.Constructor = TagsInput;
+
+ /**
+ * Most options support both a string or number as well as a function as
+ * option value. This function makes sure that the option with the given
+ * key in the given options is wrapped in a function
+ */
+ function makeOptionItemFunction(options, key) {
+ if (typeof options[key] !== 'function') {
+ var propertyName = options[key];
+ options[key] = function(item) { return item[propertyName]; };
+ }
+ }
+ function makeOptionFunction(options, key) {
+ if (typeof options[key] !== 'function') {
+ var value = options[key];
+ options[key] = function() { return value; };
+ }
+ }
+ /**
+ * HtmlEncodes the given value
+ */
+ var htmlEncodeContainer = $('<div />');
+ function htmlEncode(value) {
+ if (value) {
+ return htmlEncodeContainer.text(value).html();
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Returns the position of the caret in the given input field
+ * http://flightschool.acylt.com/devnotes/caret-position-woes/
+ */
+ function doGetCaretPosition(oField) {
+ var iCaretPos = 0;
+ if (document.selection) {
+ oField.focus ();
+ var oSel = document.selection.createRange();
+ oSel.moveStart ('character', -oField.value.length);
+ iCaretPos = oSel.text.length;
+ } else if (oField.selectionStart || oField.selectionStart == '0') {
+ iCaretPos = oField.selectionStart;
+ }
+ return (iCaretPos);
+ }
+
+ /**
+ * Returns boolean indicates whether user has pressed an expected key combination.
+ * @param object keyPressEvent: JavaScript event object, refer
+ * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+ * @param object lookupList: expected key combinations, as in:
+ * [13, {which: 188, shiftKey: true}]
+ */
+ function keyCombinationInList(keyPressEvent, lookupList) {
+ var found = false;
+ $.each(lookupList, function (index, keyCombination) {
+ if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
+ found = true;
+ return false;
+ }
+
+ if (keyPressEvent.which === keyCombination.which) {
+ var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
+ shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
+ ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
+ if (alt && shift && ctrl) {
+ found = true;
+ return false;
+ }
+ }
+ });
+
+ return found;
+ }
+
+ /**
+ * Initialize tagsinput behaviour on inputs and selects which have
+ * data-role=tagsinput
+ */
+ $(function() {
+ $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
+ });
+})(window.jQuery);
-
<div id="acl-wrapper">
- <input id="acl-search" autocomplete="off">
- <a id="acl-showall">{{$showall}}</a>
- <div id="acl-list">
- <div id="acl-list-content">
+ <div class="panel-group" id="visibility-accordion" role="tablist" aria-multiselectable="true">
+ <div class="panel panel-success">
+ <div class="panel-heading{{if $visibility != 'public'}} collapsed{{/if}}" id="visibility-public-heading" aria-expanded="{{if $visibility == 'public'}}true{{else}}false{{/if}}">
+ <label>
+ <input type="radio" name="visibility" id="visibility-public" value="public" tabindex="14" {{if $visibility == 'public'}}checked{{/if}}>
+ <i class="fa fa-globe"></i> {{$public_title}}
+ </label>
+ </div>
+ <fieldset id="visibility-public-panel" class="panel-collapse collapse{{if $visibility == 'public'}} in{{/if}}" role="tabpanel" aria-labelledby="visibility-public-heading" {{if $visibility != 'public'}}disabled{{/if}}>
+ <div class="panel-body">
+ <p>{{$public_desc}}</p>
+ {{if $for_federation}}
+ {{if $user_hidewall}}
+ <h4>{{$jotnets_summary}}</h4>
+ {{$jotnets_disabled_label}}
+ {{elseif $jotnets_fields}}
+ {{if $jotnets_fields|count < 3}}
+ <div class="profile-jot-net">
+ {{else}}
+ <details class="profile-jot-net">
+ <summary>{{$jotnets_summary}}</summary>
+ {{/if}}
+
+ {{foreach $jotnets_fields as $jotnets_field}}
+ {{if $jotnets_field.type == 'checkbox'}}
+ {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
+ {{elseif $jotnets_field.type == 'select'}}
+ {{include file="field_select.tpl" field=$jotnets_field.field}}
+ {{/if}}
+ {{/foreach}}
+
+ {{if $jotnets_fields|count >= 3}}
+ </details>
+ {{else}}
+ </div>
+ {{/if}}
+ {{/if}}
+ {{/if}}
+ </div>
+ </fieldset>
+ </div>
+ <div class="panel panel-info">
+ <div class="panel-heading{{if $visibility != 'custom'}} collapsed{{/if}}" id="visibility-custom-heading" aria-expanded="{{if $visibility == 'custom'}}true{{else}}false{{/if}}">
+ <label>
+ <input type="radio" name="visibility" id="visibility-custom" value="custom" tabindex="15" {{if $visibility == 'custom'}}checked{{/if}}>
+ <i class="fa fa-lock"></i> {{$custom_title}}
+ </label>
+ </div>
+ <fieldset id="visibility-custom-panel" class="panel-collapse collapse{{if $visibility == 'custom'}} in{{/if}}" role="tabpanel" aria-labelledby="visibility-custom-heading" {{if $visibility != 'custom'}}disabled{{/if}}>
+ <input type="hidden" name="group_allow" value="{{$group_allow}}"/>
+ <input type="hidden" name="contact_allow" value="{{$contact_allow}}"/>
+ <input type="hidden" name="group_deny" value="{{$group_deny}}"/>
+ <input type="hidden" name="contact_deny" value="{{$contact_deny}}"/>
+ <div class="panel-body">
+ <p>{{$custom_desc}}</p>
+
+ <div class="form-group">
+ <label for="acl_allow">{{$allow_label}}</label>
+ <input type="text" class="form-control input-lg" id="acl_allow">
+ </div>
+
+ <div class="form-group">
+ <label for="acl_deny">{{$deny_label}}</label>
+ <input type="text" class="form-control input-lg" id="acl_deny">
+ </div>
+ </div>
+ </fieldset>
</div>
</div>
- <span id="acl-fields"></span>
-</div>
-<div class="acl-list-item" rel="acl-template" style="display:none">
- <img data-src="{0}"><p>{1}</p>
- <a class='acl-button-show'>{{$show}}</a>
- <a class='acl-button-hide'>{{$hide}}</a>
-</div>
-{{if $networks}}
-<hr style="clear:both"/>
-<div id="profile-jot-email-label">{{$emailcc}}</div><input type="text" name="emailcc" id="profile-jot-email" title="{{$emtitle}}" />
-<div id="profile-jot-email-end"></div>
-
- {{if $jotnets_fields}}
- {{if $jotnets_fields|count < 3}}
-<div class="profile-jot-net">
- {{else}}
-<details class="profile-jot-net">
- <summary>{{$jotnets_summary}}</summary>
- {{/if}}
-
- {{foreach $jotnets_fields as $jotnets_field}}
- {{if $jotnets_field.type == 'checkbox'}}
- {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
- {{elseif $jotnets_field.type == 'select'}}
- {{include file="field_select.tpl" field=$jotnets_field.field}}
- {{/if}}
- {{/foreach}}
-
- {{if $jotnets_fields|count >= 3}}
-</details>
- {{else}}
-</div>
- {{/if}}
- {{/if}}
+{{if $for_federation}}
+ <div class="form-group">
+ <label for="profile-jot-email" id="profile-jot-email-label">{{$emailcc}}</label>
+ <input type="text" name="emailcc" id="profile-jot-email" class="form-control" title="{{$emtitle}}" />
+ </div>
+ <div id="profile-jot-email-end"></div>
{{/if}}
+</div>
+<script type="text/javascript">
+ $(function() {
+ let $acl_allow_input = $('#acl_allow');
+ let $contact_allow_input = $('[name=contact_allow]');
+ let $group_allow_input = $('[name=group_allow]');
+ let $acl_deny_input = $('#acl_deny');
+ let $contact_deny_input = $('[name=contact_deny]');
+ let $group_deny_input = $('[name=group_deny]');
+ let $visibility_public_panel = $('#visibility-public-panel');
+ let $visibility_custom_panel = $('#visibility-custom-panel');
+ let $visibility_public_radio = $('#visibility-public');
+ let $visibility_custom_radio = $('#visibility-custom');
+
+ // Frio specific
+ if ($.fn.collapse) {
+ $visibility_public_panel.collapse({parent: '#visibility-accordion', toggle: false});
+ $visibility_custom_panel.collapse({parent: '#visibility-accordion', toggle: false});
+ }
+
+ $visibility_public_radio.on('change', function (e) {
+ if ($.fn.collapse) {
+ $visibility_public_panel.collapse('show');
+ }
+
+ $visibility_public_panel.prop('disabled', false);
+ $visibility_custom_panel.prop('disabled', true);
+
+ $('.profile-jot-net input[type=checkbox]').each(function() {
+ // Restores checkbox state if it had been saved
+ if ($(this).attr('data-checked') !== undefined) {
+ $(this).prop('checked', $(this).attr('data-checked') === 'true');
+ }
+ });
+ $('.profile-jot-net input').attr('disabled', false);
+ });
+
+ $visibility_custom_radio.on('change', function(e) {
+ if ($.fn.collapse) {
+ $visibility_custom_panel.collapse('show');
+ }
+
+ $visibility_public_panel.prop('disabled', true);
+ $visibility_custom_panel.prop('disabled', false);
+
+ $('.profile-jot-net input[type=checkbox]').each(function() {
+ // Saves current checkbox state
+ $(this)
+ .attr('data-checked', $(this).prop('checked'))
+ .prop('checked', false);
+ });
+ $('.profile-jot-net input').attr('disabled', 'disabled');
+ });
+
+ // Custom visibility tags inputs
+ let acl_groups = new Bloodhound({
+ local: {{$acl_groups|@json_encode nofilter}},
+ identify: function(obj) { return obj.id; },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ });
+ let acl_contacts = new Bloodhound({
+ local: {{$acl_contacts|@json_encode nofilter}},
+ identify: function(obj) { return obj.id; },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ });
+ let acl = new Bloodhound({
+ local: {{$acl_list|@json_encode nofilter}},
+ identify: function(obj) { return obj.id; },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ });
+ acl.initialize();
+
+ let suggestionTemplate = function (item) {
+ return '<div><img src="' + item.micro + '" alt="" style="float: left; width: auto; height: 2.8em; margin-right: 0.5em;"> <strong>' + item.name + '</strong><br /><em>' + item.addr + '</em></div>';
+ };
+
+ $acl_allow_input.tagsinput({
+ confirmKeys: [13, 44],
+ freeInput: false,
+ tagClass: function(item) {
+ switch (item.type) {
+ case 'group' : return 'label label-primary';
+ case 'contact' :
+ default:
+ return 'label label-info';
+ }
+ },
+ itemValue: 'id',
+ itemText: 'name',
+ itemThumb: 'micro',
+ itemTitle: function(item) {
+ return item.addr;
+ },
+ typeaheadjs: {
+ name: 'contacts',
+ displayKey: 'name',
+ templates: {
+ suggestion: suggestionTemplate
+ },
+ source: acl.ttAdapter()
+ }
+ });
+
+ $acl_deny_input
+ .tagsinput({
+ confirmKeys: [13, 44],
+ freeInput: false,
+ tagClass: function(item) {
+ switch (item.type) {
+ case 'group' : return 'label label-primary';
+ case 'contact' :
+ default:
+ return 'label label-info';
+ }
+ },
+ itemValue: 'id',
+ itemText: 'name',
+ itemThumb: 'micro',
+ itemTitle: function(item) {
+ return item.addr;
+ },
+ typeaheadjs: {
+ name: 'contacts',
+ displayKey: 'name',
+ templates: {
+ suggestion: suggestionTemplate
+ },
+ source: acl.ttAdapter()
+ }
+ });
+
+ // Import existing ACL into the tags input fields.
+
+ $group_allow_input.val().split(',').forEach(function (val) {
+ $acl_allow_input.tagsinput('add', acl_groups.get(val)[0]);
+ });
+ $contact_allow_input.val().split(',').forEach(function (val) {
+ $acl_allow_input.tagsinput('add', acl_contacts.get(val)[0]);
+ });
+ $group_deny_input.val().split(',').forEach(function (val) {
+ $acl_deny_input.tagsinput('add', acl_groups.get(val)[0]);
+ });
+ $contact_deny_input.val().split(',').forEach(function (val) {
+ $acl_deny_input.tagsinput('add', acl_contacts.get(val)[0]);
+ });
+
+ // Anti-duplicate callback + acl fields value generation
+
+ $acl_allow_input.on('itemAdded', function (event) {
+ // Removes duplicate in the opposite acl box
+ $acl_deny_input.tagsinput('remove', event.item);
+
+ // Update the real acl field
+ $group_allow_input.val('');
+ $contact_allow_input.val('');
+ [].forEach.call($acl_allow_input.tagsinput('items'), function (item) {
+ if (item.type === 'group') {
+ $group_allow_input.val($group_allow_input.val() + ',' + item.id);
+ } else {
+ $contact_allow_input.val($contact_allow_input.val() + ',' + item.id);
+ }
+ });
+ });
+
+ $acl_deny_input.on('itemAdded', function (event) {
+ // Removes duplicate in the opposite acl box
+ $acl_allow_input.tagsinput('remove', event.item);
-<script>
-$(document).ready(function() {
- if(typeof acl=="undefined"){
- acl = new ACL(
- baseurl + '/search/acl',
- [ {{$allowcid nofilter}},{{$allowgid nofilter}},{{$denycid nofilter}},{{$denygid nofilter}} ],
- {{$features.aclautomention}},
- {{if $APP->is_mobile}}true{{else}}false{{/if}}
- );
- }
-});
+ // Update the real acl field
+ $group_deny_input.val('');
+ $contact_deny_input.val('');
+ [].forEach.call($acl_deny_input.tagsinput('items'), function (item) {
+ if (item.type === 'group') {
+ $group_deny_input.val($group_allow_input.val() + ',' + item.id);
+ } else {
+ $contact_deny_input.val($contact_allow_input.val() + ',' + item.id);
+ }
+ });
+ });
+ });
</script>
+++ /dev/null
-The MIT License (MIT)
-
-Copyright (c) 2013 Tim Schlechter
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+++ /dev/null
-/*
- * friendica-tagsinput v0.8.0
- *
- */
-
-.twitter-typeahead .tt-query,
-.twitter-typeahead .tt-hint {
- margin-bottom: 0;
-}
-
-.twitter-typeahead .tt-hint
-{
- display: none;
-}
-
-.tt-menu {
- position: absolute;
- top: 100%;
- left: 0;
- z-index: 1000;
- display: none;
- float: left;
- min-width: 160px;
- padding: 5px 0;
- margin: 2px 0 0;
- list-style: none;
- font-size: 14px;
- background-color: #ffffff;
- border: 1px solid #cccccc;
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 4px;
- -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- background-clip: padding-box;
- cursor: pointer;
-}
-
-.tt-suggestion {
- display: block;
- padding: 3px 20px;
- clear: both;
- font-weight: normal;
- line-height: 1.428571429;
- color: #333333;
- white-space: nowrap;
-}
-
-.tt-suggestion:hover,
-.tt-suggestion:focus {
- color: #ffffff;
- text-decoration: none;
- outline: 0;
- background-color: #428bca;
-}
+++ /dev/null
-/*
- * friendica-tagsinput v0.8.0
- *
- */
-
-.friendica-tagsinput {
- background-color: #fff;
- border: 1px solid #ccc;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- display: inline-block;
- padding: 4px 6px;
- color: #555;
- vertical-align: middle;
- border-radius: 4px;
- max-width: 100%;
- line-height: 22px;
- cursor: text;
- height: auto;
-}
-.friendica-tagsinput.input-lg {
- line-height: 27px;
-}
-.friendica-tagsinput input {
- border: none;
- box-shadow: none;
- outline: none;
- background-color: transparent;
- padding: 0 6px;
- margin: 0;
- width: auto;
- max-width: inherit;
-}
-.friendica-tagsinput.form-control input::-moz-placeholder {
- color: #777;
- opacity: 1;
-}
-.friendica-tagsinput.form-control input:-ms-input-placeholder {
- color: #777;
-}
-.friendica-tagsinput.form-control input::-webkit-input-placeholder {
- color: #777;
-}
-.friendica-tagsinput input:focus {
- border: none;
- box-shadow: none;
-}
-.friendica-tagsinput .tag {
- margin: 0 2px 2px 0;
- color: white;
- font-weight: normal;
-}
-.friendica-tagsinput .tag img {
- width: auto;
- height: 1.5em;
- vertical-align: text-top;
- margin-right: 8px;
-}
-.friendica-tagsinput .tag [data-role="remove"] {
- margin-left: 8px;
- cursor: pointer;
-}
-.friendica-tagsinput .tag [data-role="remove"]:after {
- content: "x";
- padding: 0px 2px;
- font-weight: bold;
-}
-.friendica-tagsinput .tag [data-role="remove"]:hover {
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-.friendica-tagsinput .tag [data-role="remove"]:hover:active {
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
-}
+++ /dev/null
-/*
- * friendica-tagsinput v0.8.0
- * Based on bootstrap-tagsinput v0.8.0
- *
- * Adds:
- * - optional thumbnail
- * - copying source input element class to the pseudo-input element
- *
- */
-
-(function ($) {
- "use strict";
-
- var defaultOptions = {
- tagClass: function(item) {
- return 'label label-info';
- },
- focusClass: 'focus',
- itemValue: function(item) {
- return item ? item.toString() : item;
- },
- itemText: function(item) {
- return this.itemValue(item);
- },
- itemTitle: function(item) {
- return null;
- },
- itemThumb: function(item) {
- return null;
- },
- freeInput: true,
- addOnBlur: true,
- maxTags: undefined,
- maxChars: undefined,
- confirmKeys: [13, 44],
- delimiter: ',',
- delimiterRegex: null,
- cancelConfirmKeysOnEmpty: false,
- onTagExists: function(item, $tag) {
- $tag.hide().fadeIn();
- },
- trimValue: false,
- allowDuplicates: false,
- triggerChange: true
- };
-
- /**
- * Constructor function
- */
- function TagsInput(element, options) {
- this.isInit = true;
- this.itemsArray = [];
-
- this.$element = $(element);
- this.$element.hide();
-
- this.isSelect = (element.tagName === 'SELECT');
- this.multiple = (this.isSelect && element.hasAttribute('multiple'));
- this.objectItems = options && options.itemValue;
- this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
- this.inputSize = Math.max(1, this.placeholderText.length);
-
- this.$container = $('<div class="friendica-tagsinput"></div>');
- this.$container.addClass(this.$element.attr('class'));
- this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
-
- this.$element.before(this.$container);
-
- this.build(options);
- this.isInit = false;
- }
-
- TagsInput.prototype = {
- constructor: TagsInput,
-
- /**
- * Adds the given item as a new tag. Pass true to dontPushVal to prevent
- * updating the elements val()
- */
- add: function(item, dontPushVal, options) {
- let self = this;
-
- if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
- return;
-
- // Ignore falsey values, except false
- if (item !== false && !item)
- return;
-
- // Trim value
- if (typeof item === "string" && self.options.trimValue) {
- item = $.trim(item);
- }
-
- // Throw an error when trying to add an object while the itemValue option was not set
- if (typeof item === "object" && !self.objectItems)
- throw("Can't add objects when itemValue option is not set");
-
- // Ignore strings only containg whitespace
- if (item.toString().match(/^\s*$/))
- return;
-
- // If SELECT but not multiple, remove current tag
- if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
- self.remove(self.itemsArray[0]);
-
- if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
- var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
- var items = item.split(delimiter);
- if (items.length > 1) {
- for (var i = 0; i < items.length; i++) {
- this.add(items[i], true);
- }
-
- if (!dontPushVal)
- self.pushVal(self.options.triggerChange);
- return;
- }
- }
-
- var itemValue = self.options.itemValue(item),
- itemText = self.options.itemText(item),
- tagClass = self.options.tagClass(item),
- itemTitle = self.options.itemTitle(item),
- itemThumb = self.options.itemThumb(item);
-
- // Ignore items allready added
- var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
- if (existing && !self.options.allowDuplicates) {
- // Invoke onTagExists
- if (self.options.onTagExists) {
- var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
- self.options.onTagExists(item, $existingTag);
- }
- return;
- }
-
- // if length greater than limit
- if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
- return;
-
- // raise beforeItemAdd arg
- var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
- self.$element.trigger(beforeItemAddEvent);
- if (beforeItemAddEvent.cancel)
- return;
-
- // register item in internal array and map
- self.itemsArray.push(item);
-
- // add a tag element
- var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' +
- (itemThumb !== null ? '<img src="' + itemThumb + '" alt="">' : '') +
- htmlEncode(itemText) + '<span data-role="remove"></span>' +
- '</span>');
- $tag.data('item', item);
- self.findInputWrapper().before($tag);
- $tag.after(' ');
-
- // Check to see if the tag exists in its raw or uri-encoded form
- var optionExists = (
- $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length ||
- $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length
- );
-
- // add <option /> if item represents a value not present in one of the <select />'s options
- if (self.isSelect && !optionExists) {
- var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
- $option.data('item', item);
- $option.attr('value', itemValue);
- self.$element.append($option);
- }
-
- if (!dontPushVal)
- self.pushVal(self.options.triggerChange);
-
- // Add class when reached maxTags
- if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
- self.$container.addClass('friendica-tagsinput-max');
-
- // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
- if ($('.typeahead, .twitter-typeahead', self.$container).length) {
- self.$input.typeahead('val', '');
- }
-
- if (this.isInit) {
- self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
- } else {
- self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
- }
- },
-
- /**
- * Removes the given item. Pass true to dontPushVal to prevent updating the
- * elements val()
- */
- remove: function(item, dontPushVal, options) {
- var self = this;
-
- if (self.objectItems) {
- if (typeof item === "object")
- item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } );
- else
- item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } );
-
- item = item[item.length-1];
- }
-
- if (item) {
- var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
- self.$element.trigger(beforeItemRemoveEvent);
- if (beforeItemRemoveEvent.cancel)
- return;
-
- $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
- $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
- if($.inArray(item, self.itemsArray) !== -1)
- self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
- }
-
- if (!dontPushVal)
- self.pushVal(self.options.triggerChange);
-
- // Remove class when reached maxTags
- if (self.options.maxTags > self.itemsArray.length)
- self.$container.removeClass('friendica-tagsinput-max');
-
- self.$element.trigger($.Event('itemRemoved', { item: item, options: options }));
- },
-
- /**
- * Removes all items
- */
- removeAll: function() {
- var self = this;
-
- $('.tag', self.$container).remove();
- $('option', self.$element).remove();
-
- while(self.itemsArray.length > 0)
- self.itemsArray.pop();
-
- self.pushVal(self.options.triggerChange);
- },
-
- /**
- * Refreshes the tags so they match the text/value of their corresponding
- * item.
- */
- refresh: function() {
- var self = this;
- $('.tag', self.$container).each(function() {
- var $tag = $(this),
- item = $tag.data('item'),
- itemValue = self.options.itemValue(item),
- itemText = self.options.itemText(item),
- tagClass = self.options.tagClass(item);
-
- // Update tag's class and inner text
- $tag.attr('class', null);
- $tag.addClass('tag ' + htmlEncode(tagClass));
- $tag.contents().filter(function() {
- return this.nodeType == 3;
- })[0].nodeValue = htmlEncode(itemText);
-
- if (self.isSelect) {
- var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
- option.attr('value', itemValue);
- }
- });
- },
-
- /**
- * Returns the items added as tags
- */
- items: function() {
- return this.itemsArray;
- },
-
- /**
- * Assembly value by retrieving the value of each item, and set it on the
- * element.
- */
- pushVal: function() {
- var self = this,
- val = $.map(self.items(), function(item) {
- return self.options.itemValue(item).toString();
- });
-
- self.$element.val(val, true);
-
- if (self.options.triggerChange)
- self.$element.trigger('change');
- },
-
- /**
- * Initializes the tags input behaviour on the element
- */
- build: function(options) {
- var self = this;
-
- self.options = $.extend({}, defaultOptions, options);
- // When itemValue is set, freeInput should always be false
- if (self.objectItems)
- self.options.freeInput = false;
-
- makeOptionItemFunction(self.options, 'itemValue');
- makeOptionItemFunction(self.options, 'itemText');
- makeOptionItemFunction(self.options, 'itemThumb');
- makeOptionFunction(self.options, 'tagClass');
-
- // Typeahead Bootstrap version 2.3.2
- if (self.options.typeahead) {
- var typeahead = self.options.typeahead || {};
-
- makeOptionFunction(typeahead, 'source');
-
- self.$input.typeahead($.extend({}, typeahead, {
- source: function (query, process) {
- function processItems(items) {
- var texts = [];
-
- for (var i = 0; i < items.length; i++) {
- var text = self.options.itemText(items[i]);
- map[text] = items[i];
- texts.push(text);
- }
- process(texts);
- }
-
- this.map = {};
- var map = this.map,
- data = typeahead.source(query);
-
- if ($.isFunction(data.success)) {
- // support for Angular callbacks
- data.success(processItems);
- } else if ($.isFunction(data.then)) {
- // support for Angular promises
- data.then(processItems);
- } else {
- // support for functions and jquery promises
- $.when(data)
- .then(processItems);
- }
- },
- updater: function (text) {
- self.add(this.map[text]);
- return this.map[text];
- },
- matcher: function (text) {
- return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
- },
- sorter: function (texts) {
- return texts.sort();
- },
- highlighter: function (text) {
- var regex = new RegExp( '(' + this.query + ')', 'gi' );
- return text.replace( regex, "<strong>$1</strong>" );
- }
- }));
- }
-
- // typeahead.js
- if (self.options.typeaheadjs) {
- var typeaheadConfig = null;
- var typeaheadDatasets = {};
-
- // Determine if main configurations were passed or simply a dataset
- var typeaheadjs = self.options.typeaheadjs;
- if ($.isArray(typeaheadjs)) {
- typeaheadConfig = typeaheadjs[0];
- typeaheadDatasets = typeaheadjs[1];
- } else {
- typeaheadDatasets = typeaheadjs;
- }
-
- self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
- if (typeaheadDatasets.valueKey)
- self.add(datum[typeaheadDatasets.valueKey]);
- else
- self.add(datum);
- self.$input.typeahead('val', '');
- }, self));
- }
-
- self.$container.on('click', $.proxy(function(event) {
- if (! self.$element.attr('disabled')) {
- self.$input.removeAttr('disabled');
- }
- self.$input.focus();
- }, self));
-
- if (self.options.addOnBlur && self.options.freeInput) {
- self.$input.on('focusout', $.proxy(function(event) {
- // HACK: only process on focusout when no typeahead opened, to
- // avoid adding the typeahead text as tag
- if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
- self.add(self.$input.val());
- self.$input.val('');
- }
- }, self));
- }
-
- // Toggle the 'focus' css class on the container when it has focus
- self.$container.on({
- focusin: function() {
- self.$container.addClass(self.options.focusClass);
- },
- focusout: function() {
- self.$container.removeClass(self.options.focusClass);
- },
- });
-
- self.$container.on('keydown', 'input', $.proxy(function(event) {
- var $input = $(event.target),
- $inputWrapper = self.findInputWrapper();
-
- if (self.$element.attr('disabled')) {
- self.$input.attr('disabled', 'disabled');
- return;
- }
-
- switch (event.which) {
- // BACKSPACE
- case 8:
- if (doGetCaretPosition($input[0]) === 0) {
- var prev = $inputWrapper.prev();
- if (prev.length) {
- self.remove(prev.data('item'));
- }
- }
- break;
-
- // DELETE
- case 46:
- if (doGetCaretPosition($input[0]) === 0) {
- var next = $inputWrapper.next();
- if (next.length) {
- self.remove(next.data('item'));
- }
- }
- break;
-
- // LEFT ARROW
- case 37:
- // Try to move the input before the previous tag
- var $prevTag = $inputWrapper.prev();
- if ($input.val().length === 0 && $prevTag[0]) {
- $prevTag.before($inputWrapper);
- $input.focus();
- }
- break;
- // RIGHT ARROW
- case 39:
- // Try to move the input after the next tag
- var $nextTag = $inputWrapper.next();
- if ($input.val().length === 0 && $nextTag[0]) {
- $nextTag.after($inputWrapper);
- $input.focus();
- }
- break;
- default:
- // ignore
- }
-
- // Reset internal input's size
- var textLength = $input.val().length,
- wordSpace = Math.ceil(textLength / 5),
- size = textLength + wordSpace + 1;
- $input.attr('size', Math.max(this.inputSize, $input.val().length));
- }, self));
-
- self.$container.on('keypress', 'input', $.proxy(function(event) {
- var $input = $(event.target);
-
- if (self.$element.attr('disabled')) {
- self.$input.attr('disabled', 'disabled');
- return;
- }
-
- var text = $input.val(),
- maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
- if (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached) {
- // Only attempt to add a tag if there is data in the field
- if (self.options.freeInput && text.length !== 0) {
- self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
- $input.val('');
- }
-
- // If the field is empty, let the event triggered fire as usual
- if (self.options.cancelConfirmKeysOnEmpty === false) {
- event.preventDefault();
- }
- }
-
- // Reset internal input's size
- var textLength = $input.val().length,
- wordSpace = Math.ceil(textLength / 5),
- size = textLength + wordSpace + 1;
- $input.attr('size', Math.max(this.inputSize, $input.val().length));
- }, self));
-
- // Remove icon clicked
- self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
- if (self.$element.attr('disabled')) {
- return;
- }
- self.remove($(event.target).closest('.tag').data('item'));
- }, self));
-
- // Only add existing value as tags when using strings as tags
- if (self.options.itemValue === defaultOptions.itemValue) {
- if (self.$element[0].tagName === 'INPUT') {
- self.add(self.$element.val());
- } else {
- $('option', self.$element).each(function() {
- self.add($(this).attr('value'), true);
- });
- }
- }
- },
-
- /**
- * Removes all tagsinput behaviour and unregsiter all event handlers
- */
- destroy: function() {
- var self = this;
-
- // Unbind events
- self.$container.off('keypress', 'input');
- self.$container.off('click', '[role=remove]');
-
- self.$container.remove();
- self.$element.removeData('tagsinput');
- self.$element.show();
- },
-
- /**
- * Sets focus on the tagsinput
- */
- focus: function() {
- this.$input.focus();
- },
-
- /**
- * Returns the internal input element
- */
- input: function() {
- return this.$input;
- },
-
- /**
- * Returns the element which is wrapped around the internal input. This
- * is normally the $container, but typeahead.js moves the $input element.
- */
- findInputWrapper: function() {
- var elt = this.$input[0],
- container = this.$container[0];
- while(elt && elt.parentNode !== container)
- elt = elt.parentNode;
-
- return $(elt);
- }
- };
-
- /**
- * Register JQuery plugin
- */
- $.fn.tagsinput = function(arg1, arg2, arg3) {
- var results = [];
-
- this.each(function() {
- var tagsinput = $(this).data('tagsinput');
- // Initialize a new tags input
- if (!tagsinput) {
- tagsinput = new TagsInput(this, arg1);
- $(this).data('tagsinput', tagsinput);
- results.push(tagsinput);
-
- if (this.tagName === 'SELECT') {
- $('option', $(this)).attr('selected', 'selected');
- }
-
- // Init tags from $(this).val()
- $(this).val($(this).val());
- } else if (!arg1 && !arg2) {
- // tagsinput already exists
- // no function, trying to init
- results.push(tagsinput);
- } else if(tagsinput[arg1] !== undefined) {
- // Invoke function on existing tags input
- if(tagsinput[arg1].length === 3 && arg3 !== undefined){
- var retVal = tagsinput[arg1](arg2, null, arg3);
- }else{
- var retVal = tagsinput[arg1](arg2);
- }
- if (retVal !== undefined)
- results.push(retVal);
- }
- });
-
- if ( typeof arg1 == 'string') {
- // Return the results from the invoked function calls
- return results.length > 1 ? results : results[0];
- } else {
- return results;
- }
- };
-
- $.fn.tagsinput.Constructor = TagsInput;
-
- /**
- * Most options support both a string or number as well as a function as
- * option value. This function makes sure that the option with the given
- * key in the given options is wrapped in a function
- */
- function makeOptionItemFunction(options, key) {
- if (typeof options[key] !== 'function') {
- var propertyName = options[key];
- options[key] = function(item) { return item[propertyName]; };
- }
- }
- function makeOptionFunction(options, key) {
- if (typeof options[key] !== 'function') {
- var value = options[key];
- options[key] = function() { return value; };
- }
- }
- /**
- * HtmlEncodes the given value
- */
- var htmlEncodeContainer = $('<div />');
- function htmlEncode(value) {
- if (value) {
- return htmlEncodeContainer.text(value).html();
- } else {
- return '';
- }
- }
-
- /**
- * Returns the position of the caret in the given input field
- * http://flightschool.acylt.com/devnotes/caret-position-woes/
- */
- function doGetCaretPosition(oField) {
- var iCaretPos = 0;
- if (document.selection) {
- oField.focus ();
- var oSel = document.selection.createRange();
- oSel.moveStart ('character', -oField.value.length);
- iCaretPos = oSel.text.length;
- } else if (oField.selectionStart || oField.selectionStart == '0') {
- iCaretPos = oField.selectionStart;
- }
- return (iCaretPos);
- }
-
- /**
- * Returns boolean indicates whether user has pressed an expected key combination.
- * @param object keyPressEvent: JavaScript event object, refer
- * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
- * @param object lookupList: expected key combinations, as in:
- * [13, {which: 188, shiftKey: true}]
- */
- function keyCombinationInList(keyPressEvent, lookupList) {
- var found = false;
- $.each(lookupList, function (index, keyCombination) {
- if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
- found = true;
- return false;
- }
-
- if (keyPressEvent.which === keyCombination.which) {
- var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
- shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
- ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
- if (alt && shift && ctrl) {
- found = true;
- return false;
- }
- }
- });
-
- return found;
- }
-
- /**
- * Initialize tagsinput behaviour on inputs and selects which have
- * data-role=tagsinput
- */
- $(function() {
- $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
- });
-})(window.jQuery);
$("#event-preview").empty();
e.preventDefault();
});
-
- // Construct a new ACL. We need this everytime the 'event-edit-form' is loaded
- // without page reloading (e.g. closing an old modal and open a new modal).
- // Otherwise we wouldn't get the ACL data.
- /// @todo: Try to implement some kind of ACL reloading in acl.js.
-
- var eventPerms = document.getElementById('event-edit-form');
-
- acl = new ACL(
- baseurl + '/search/acl',
- [
- JSON.parse(eventPerms.dataset.allow_cid),
- JSON.parse(eventPerms.dataset.allow_gid),
- JSON.parse(eventPerms.dataset.deny_cid),
- JSON.parse(eventPerms.dataset.deny_gid)
- ]
- );
- acl.get(0, 100);
});
// Load the html of the actual event and incect the output to the
// Minimize all tab content wrapper and activate only the selected
// tab panel.
- $('#jot-modal [role=tabpanel]').addClass("minimize").attr("aria-hidden" ,"true");
- $('#jot-modal #' + tabpanel).removeClass("minimize").attr("aria-hidden" ,"false");
+ $('#profile-jot-form > [role=tabpanel]').addClass("minimize").attr("aria-hidden" ,"true");
+ $('#' + tabpanel).removeClass("minimize").attr("aria-hidden" ,"false");
// Set the aria-selected states
- $("#jot-modal .nav-tabs .jot-nav-lnk").attr("aria-selected", "false");
+ $("#jot-modal .modal-header .nav-tabs .jot-nav-lnk").attr("aria-selected", "false");
elm.setAttribute("aria-selected", "true");
// For some some tab panels we need to execute other js functions.
+++ /dev/null
-
-<div id="acl-wrapper">
- <div class="form-group form-group-search">
- <button id="acl-showall" class="btn btn-block btn-default"><i class="fa fa-globe"></i> {{$showall}}</button>
- </div>
- <div class="form-group form-group-search">
- <input type="text" id="acl-search" class="form-control form-search" autocomplete="off">
- </div>
- <div id="acl-list">
- <div id="acl-list-content"></div>
- </div>
- <span id="acl-fields"></span>
-</div>
-
-<div class="acl-list-item" rel="acl-template" style="display:none">
- <img data-src="{0}" alt="{1}"><p>{1}</p>
- <button class='acl-button-hide btn btn-sm btn-default'>{{$hide}}</button>
- <button class='acl-button-show btn btn-sm btn-default'>{{$show}}</button>
-</div>
-
-{{if $networks}}
-<hr style="clear:both"/>
-<div class="form-group">
- <label for="profile-jot-email" id="profile-jot-email-label">{{$emailcc}}</label>
- <input type="text" name="emailcc" id="profile-jot-email" class="form-control" title="{{$emtitle}}" />
-</div>
-<div id="profile-jot-email-end"></div>
-
- {{if $jotnets_fields}}
- {{if $jotnets_fields|count < 3}}
-<div class="profile-jot-net">
- {{else}}
-<details class="profile-jot-net">
- <summary>{{$jotnets_summary}}</summary>
- {{/if}}
-
- {{foreach $jotnets_fields as $jotnets_field}}
- {{if $jotnets_field.type == 'checkbox'}}
- {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
- {{elseif $jotnets_field.type == 'select'}}
- {{include file="field_select.tpl" field=$jotnets_field.field}}
- {{/if}}
- {{/foreach}}
-
- {{if $jotnets_fields|count >= 3}}
-</details>
- {{else}}
-</div>
- {{/if}}
- {{/if}}
-{{/if}}
-
-<script type="text/javascript">
-$(document).ready(function() {
- if(typeof acl=="undefined"){
- acl = new ACL(
- baseurl + '/search/acl',
- [ {{$allowcid nofilter}},{{$allowgid nofilter}},{{$denycid nofilter}},{{$denygid nofilter}} ],
- {{$features.aclautomention}},
- {{if $APP->is_mobile}}true{{else}}false{{/if}}
- );
- }
-});
-</script>
</ul>
<div id="event-edit-form-wrapper">
- <form id="event-edit-form" action="{{$post}}" method="post" data-allow_cid="{{$allow_cid}}" data-allow_gid="{{$allow_gid}}" data-deny_cid="{{$deny_cid}}" data-deny_gid="{{$deny_gid}}">
+ <form id="event-edit-form" action="{{$post}}" method="post">
<input type="hidden" name="event_id" value="{{$eid}}" />
<input type="hidden" name="cid" value="{{$cid}}" />
<div style="display: none;">
<div id="profile-jot-acl-wrapper" style="width:auto;height:auto;overflow:auto;">
{{$acl nofilter}}
- <hr style="clear:both"/>
- <div id="profile-jot-email-label">{{$emailcc}}</div><input type="text" name="emailcc" id="profile-jot-email" title="{{$emtitle}}" />
- <div id="profile-jot-email-end"></div>
{{$jotnets nofilter}}
</div>
</div>