-One of the traditional problems with independent publishing on the internet has always been the fact that independent publishers often operate as isolated islands within their own website, and spend most of their resources attracting visitors. The rise of corporate providers and social networking services alleviated many of these problems; however centralisation has led to a situation where your content where is no longer under your direct control. It is shared fully with corporate advertisers and governments, but ironically you are now often asked to pay money to ensure that your friends can see it. What if you could have advantages of scale and connections that centralisation typically offers whilst maintaining independent control over your own web presence?
+One of the traditional problems with independent publishing on the internet has always been the fact that independent publishers often operate as isolated islands within their own website, and spend most of their resources attracting visitors. The rise of corporate providers and social networking services alleviated many of these problems; however centralisation has led to a situation where your content is no longer under your direct control. It is shared fully with corporate advertisers and governments, but ironically you are now often asked to pay money to ensure that your friends can see it. What if you could have advantages of scale and connections that centralisation typically offers whilst maintaining independent control over your own web presence?
diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php
index eb08fd79f..78aacf92b 100644
--- a/include/RedDAV/RedBrowser.php
+++ b/include/RedDAV/RedBrowser.php
@@ -238,11 +238,21 @@ class RedBrowser extends DAV\Browser\Plugin {
$quota['limit'] = $limit;
$quota['desc'] = $quotaDesc;
- $html .= replace_macros(get_markup_template('cloud_directory.tpl'), array(
+ $output = '';
+ if ($this->enablePost) {
+ $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output));
+ }
+
+ $html .= replace_macros(get_markup_template('cloud_header.tpl'), array(
'$header' => t('Files') . ": " . $this->escapeHTML($path) . "/",
+ '$quota' => $quota,
+ '$total' => t('Total'),
+ '$actionspanel' => $output
+ ));
+
+ $html .= replace_macros(get_markup_template('cloud_directory.tpl'), array(
'$parentpath' => $parentpath,
'$entries' => $f,
- '$quota' => $quota,
'$name' => t('Name'),
'$type' => t('Type'),
'$size' => t('Size'),
@@ -250,15 +260,9 @@ class RedBrowser extends DAV\Browser\Plugin {
'$parent' => t('parent'),
'$edit' => t('Edit'),
'$delete' => t('Delete'),
- '$total' => t('Total')
+ '$nick' => $this->auth->getCurrentUser()
));
- $output = '';
- if ($this->enablePost) {
- $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output));
- }
- $html .= $output;
-
get_app()->page['content'] = $html;
load_pdl(get_app());
construct_page(get_app());
@@ -371,4 +375,4 @@ class RedBrowser extends DAV\Browser\Plugin {
}
return $id;
}
-}
\ No newline at end of file
+}
diff --git a/library/bootstrap-tagsinput/LICENSE b/library/bootstrap-tagsinput/LICENSE
new file mode 100644
index 000000000..58bc985ba
--- /dev/null
+++ b/library/bootstrap-tagsinput/LICENSE
@@ -0,0 +1,20 @@
+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.
diff --git a/library/bootstrap-tagsinput/README b/library/bootstrap-tagsinput/README
new file mode 100644
index 000000000..a7c96d53a
--- /dev/null
+++ b/library/bootstrap-tagsinput/README
@@ -0,0 +1 @@
+Taken from https://github.com/gdibble/bootstrap-tagsinput/tree/traditionalPlaceholderEffect which has proper support for placeholders
diff --git a/library/bootstrap-tagsinput/README.md b/library/bootstrap-tagsinput/README.md
new file mode 100644
index 000000000..363c0d50b
--- /dev/null
+++ b/library/bootstrap-tagsinput/README.md
@@ -0,0 +1,879 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
bootstrap-tagsinput/README.md at master · TimSchlechter/bootstrap-tagsinput · GitHub
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Skip to content
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Something went wrong with that request. Please try again.
+
+
+
+
+
+
+
+
+
+
diff --git a/library/bootstrap-tagsinput/bootstrap-tagsinput.css b/library/bootstrap-tagsinput/bootstrap-tagsinput.css
new file mode 100644
index 000000000..55f7c09df
--- /dev/null
+++ b/library/bootstrap-tagsinput/bootstrap-tagsinput.css
@@ -0,0 +1,46 @@
+.bootstrap-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;
+ margin-bottom: 10px;
+ color: #555;
+ vertical-align: middle;
+ border-radius: 4px;
+ max-width: 100%;
+ line-height: 22px;
+ cursor: text;
+}
+.bootstrap-tagsinput input {
+ border: none;
+ box-shadow: none;
+ outline: none;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ width: auto !important;
+ max-width: inherit;
+}
+.bootstrap-tagsinput input:focus {
+ border: none;
+ box-shadow: none;
+}
+.bootstrap-tagsinput .tag {
+ margin-right: 2px;
+ color: white;
+}
+.bootstrap-tagsinput .tag [data-role="remove"] {
+ margin-left: 8px;
+ cursor: pointer;
+}
+.bootstrap-tagsinput .tag [data-role="remove"]:after {
+ content: "x";
+ padding: 0px 2px;
+}
+.bootstrap-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);
+}
+.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
diff --git a/library/bootstrap-tagsinput/bootstrap-tagsinput.js b/library/bootstrap-tagsinput/bootstrap-tagsinput.js
new file mode 100644
index 000000000..89cc3b5a4
--- /dev/null
+++ b/library/bootstrap-tagsinput/bootstrap-tagsinput.js
@@ -0,0 +1,643 @@
+(function ($) {
+ "use strict";
+
+ var defaultOptions = {
+ tagClass: function(item) {
+ return 'label label-info';
+ },
+ itemValue: function(item) {
+ return item ? item.toString() : item;
+ },
+ itemText: function(item) {
+ return this.itemValue(item);
+ },
+ freeInput: true,
+ addOnBlur: true,
+ maxTags: undefined,
+ maxChars: undefined,
+ confirmKeys: [13, 44],
+ onTagExists: function(item, $tag) {
+ $tag.hide().fadeIn();
+ },
+ trimValue: false,
+ allowDuplicates: false
+ };
+
+ /**
+ * Constructor function
+ */
+ function TagsInput(element, options) {
+ 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 = $('
');
+ this.$input = $('
').appendTo(this.$container);
+
+ this.$element.after(this.$container);
+
+ var inputWidth = (this.inputSize < 3 ? 3 : this.inputSize) + "em";
+ this.$input.get(0).style.cssText = "width: " + inputWidth + " !important;";
+ this.build(options);
+ }
+
+ 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) {
+ var 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 items = item.split(',');
+ if (items.length > 1) {
+ for (var i = 0; i < items.length; i++) {
+ this.add(items[i], true);
+ }
+
+ if (!dontPushVal)
+ self.pushVal();
+ return;
+ }
+ }
+
+ // raise beforeItemAdd arg
+ var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false });
+ self.$element.trigger(beforeItemAddEvent);
+ if (beforeItemAddEvent.cancel)
+ return;
+
+ item = beforeItemAddEvent.item
+
+ var itemValue = self.options.itemValue(item),
+ itemText = self.options.itemText(item),
+ tagClass = self.options.tagClass(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;
+
+ // register item in internal array and map
+ self.itemsArray.push(item);
+
+ //remove placeholder if there is an item, restore if no item(s)
+ if (self.itemsArray.length)
+ self.$input.attr('placeholder', '');
+ else
+ self.$input.attr('placeholder', self.placeholderText);
+
+ // add a tag element
+ var $tag = $('
' + htmlEncode(itemText) + ' ');
+ $tag.data('item', item);
+ self.findInputWrapper().before($tag);
+ $tag.after(' ');
+
+ // add
if item represents a value not present in one of the
's options
+ if (self.isSelect && !$('option[value="' + encodeURIComponent(itemValue) + '"]',self.$element)[0]) {
+ var $option = $('
' + htmlEncode(itemText) + ' ');
+ $option.data('item', item);
+ $option.attr('value', itemValue);
+ self.$element.append($option);
+ }
+
+ //remove placeholder if there is an item, restore if no item(s)
+ if (self.itemsArray.length)
+ self.$input.attr('placeholder', '');
+ else
+ self.$input.attr('placeholder', self.placeholderText);
+
+ if (!dontPushVal)
+ self.pushVal();
+
+ // Add class when reached maxTags
+ if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
+ self.$container.addClass('bootstrap-tagsinput-max');
+
+ self.$element.trigger($.Event('itemAdded', { item: item }));
+ },
+
+ /**
+ * Removes the given item. Pass true to dontPushVal to prevent updating the
+ * elements val()
+ */
+ remove: function(item, dontPushVal) {
+ 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 });
+ 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);
+ }
+
+ //remove placeholder if there is an item, restore if no item(s)
+ if (self.itemsArray.length)
+ self.$input.attr('placeholder', '');
+ else
+ self.$input.attr('placeholder', self.placeholderText);
+
+ if (!dontPushVal)
+ self.pushVal();
+
+ // Remove class when reached maxTags
+ if (self.options.maxTags > self.itemsArray.length)
+ self.$container.removeClass('bootstrap-tagsinput-max');
+
+ self.$element.trigger($.Event('itemRemoved', { item: item }));
+ },
+
+ /**
+ * Removes all items
+ */
+ removeAll: function() {
+ var self = this;
+
+ $('.tag', self.$container).remove();
+ $('option', self.$element).remove();
+
+ while(self.itemsArray.length > 0)
+ self.itemsArray.pop();
+
+ //remove placeholder if there is an item, restore if no item(s)
+ if (self.itemsArray.length)
+ self.$input.attr('placeholder', '');
+ else
+ self.$input.attr('placeholder', self.placeholderText);
+
+ self.pushVal();
+ },
+
+ /**
+ * 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).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');
+ 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]);
+ },
+ 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, "
$1 " );
+ }
+ }));
+ }
+
+ // typeahead.js
+ if (self.options.typeaheadjs) {
+ var typeaheadjs = self.options.typeaheadjs || {};
+
+ self.$input.typeahead(null, typeaheadjs).on('typeahead:selected', $.proxy(function (obj, datum) {
+ if (typeaheadjs.valueKey)
+ self.add(datum[typeaheadjs.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));
+ }
+
+
+ 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) {
+ self.remove(prev.data('item'));
+ }
+ }
+ break;
+
+ // DELETE
+ case 46:
+ if (doGetCaretPosition($input[0]) === 0) {
+ var next = $inputWrapper.next();
+ if (next) {
+ 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 (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
+ self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
+ $input.val('');
+ 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) {
+ 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
+ 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 = $('
');
+ 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);
diff --git a/mod/filestorage.php b/mod/filestorage.php
index e27087a92..1767ea6bf 100644
--- a/mod/filestorage.php
+++ b/mod/filestorage.php
@@ -146,7 +146,8 @@ function filestorage_content(&$a) {
'$submit' => t('Submit')
));
- return $o;
+ echo $o;
+ killme();
}
goaway(z_root() . '/cloud/' . $which);
diff --git a/view/css/conversation.css b/view/css/conversation.css
index 1e9930443..7a5ffc2f7 100644
--- a/view/css/conversation.css
+++ b/view/css/conversation.css
@@ -8,9 +8,7 @@ code {
/* jot */
-#jot-title,
-#jot-category,
-#jot-pagetitle {
+.jothidden input {
border: 0px;
margin: 0px;
padding: 8px;
diff --git a/view/css/mod_cloud.css b/view/css/mod_cloud.css
new file mode 100644
index 000000000..cc5fe890f
--- /dev/null
+++ b/view/css/mod_cloud.css
@@ -0,0 +1,5 @@
+#files-mkdir-tools,
+#files-upload-tools {
+ display: none;
+}
+
diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js
index fa293fdfd..e62959a6c 100644
--- a/view/js/autocomplete.js
+++ b/view/js/autocomplete.js
@@ -50,9 +50,9 @@ function replace(item) {
smilies = {
match: /(^|\s)(:[a-z]{2,})$/,
index: 2,
- search: function(term, callback) { $.getJSON('https://caterva.eu/smilies/json').done(function(data) { callback($.map(data, function(entry) { return entry['text'].indexOf(term) === 0 ? entry : null })) }) },
+ search: function(term, callback) { $.getJSON('/smilies/json').done(function(data) { callback($.map(data, function(entry) { return entry['text'].indexOf(term) === 0 ? entry : null })) }) },
template: function(item) { return item['icon'] + item['text'] },
- replace: function(item) { return item['text'] + ' '; },
+ replace: function(item) { return "$1"+item['text'] + ' '; },
}
this.textcomplete([contacts,smilies],{});
};
diff --git a/view/js/main.js b/view/js/main.js
index a851e7e63..f49cea78c 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -208,6 +208,7 @@
var pageHasMoreContent = true;
var updateCountsOnly = false;
var divmore_height = 400;
+ var last_filestorage_id = null;
$(function() {
$.ajaxSetup({cache: false});
@@ -842,6 +843,17 @@ function updateConvItems(mode,data) {
});
}
+ function filestorage(event,nick,id) {
+ $('#perms-panel-' + last_filestorage_id).html('');
+ $('#file-edit-' + id).spin('tiny');
+ delete acl;
+ $.get('filestorage/' + nick + '/' + id + '/edit', function(data) {
+ $('#perms-panel-' + id).html(data);
+ $('#file-edit-' + id).spin(false);
+ last_filestorage_id = id;
+ });
+ }
+
function post_comment(id) {
unpause();
commentBusy = true;
diff --git a/view/js/mod_filestorage.js b/view/js/mod_filestorage.js
index 88c1cf7d8..bee37767f 100644
--- a/view/js/mod_filestorage.js
+++ b/view/js/mod_filestorage.js
@@ -13,4 +13,5 @@ $(document).ready(function() {
}
}).trigger('change');
+
});
diff --git a/view/php/theme_init.php b/view/php/theme_init.php
index ff8b54c55..f28f9aa8d 100644
--- a/view/php/theme_init.php
+++ b/view/php/theme_init.php
@@ -11,6 +11,7 @@ head_add_css('library/colorbox/colorbox.css');
head_add_css('view/css/conversation.css');
head_add_css('view/css/widgets.css');
head_add_css('library/justifiedGallery/dist/css/justifiedGallery.css');
+head_add_css('library/bootstrap-tagsinput/bootstrap-tagsinput.css');
head_add_js('jquery.js');
head_add_js('jquery-migrate-1.1.1.js');
@@ -41,6 +42,7 @@ head_add_js('crypto.js');
head_add_js('library/jslider/bin/jquery.slider.min.js');
head_add_js('docready.js');
head_add_js('library/colorbox/jquery.colorbox-min.js');
+head_add_js('library/bootstrap-tagsinput/bootstrap-tagsinput.js');
/**
* Those who require this feature will know what to do with it.
diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css
index 382bed90e..4681ef5ba 100644
--- a/view/theme/redbasic/css/style.css
+++ b/view/theme/redbasic/css/style.css
@@ -1746,44 +1746,23 @@ img.mail-list-sender-photo {
max-width: $converse_width;
}
-#jot-title,
-#jot-category,
-#jot-pagetitle {
+.jothidden {
font-weight: bold;
- border: solid 1px #ffffff;
border-radius: $radiuspx;
}
-#jot-category::-webkit-input-placeholder {
+.jothidden input::-webkit-input-placeholder {
font-weight: bold;
}
-#jot-category:-moz-placeholder {
+.jothidden input::-moz-placeholder {
font-weight: bold;
}
-#jot-title::-webkit-input-placeholder {
- font-weight: bold;
+.jothidden >input, .jothidden >input {
+ border: 1px solid #fff;
}
-
-#jot-title:-moz-placeholder {
- font-weight: bold;
-}
-
-#jot-pagetitle::-webkit-input-placeholder {
- font-weight: bold;
-}
-
-#jot-pagetitle:-moz-placeholder {
- font-weight: bold;
-}
-
-#jot-title:hover,
-#jot-title:focus,
-#jot-pagetitle:hover,
-#jot-pagetitle:focus,
-#jot-category:hover,
-#jot-category:focus {
+.jothidden >input:hover, .jothidden >input:focus {
border: 1px solid #cccccc;
}
@@ -2076,18 +2055,8 @@ nav .dropdown-menu>li>a:hover,nav .dropdown-menu>li>a:focus{
color: $nav_active_icon_colour;
}
-/* Files */
-
-#attach-edit-perms {
- margin-top: 25px;
- margin-bottom: 20px;
- font-weight: bold;
- font-size: 17px;
-}
-
/* bootstrap overrides */
-
blockquote {
font-size: $font_size;
font-style: italic;
@@ -2368,3 +2337,28 @@ aside .nav > li > a:hover, aside .nav > li > a:focus {
.bb_observer img {
border: 3px solid red !important;
}
+
+.bootstrap-tagsinput .tag:before {
+ /* Copied from icon-asterisk, is there a better way to do it? */
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-style: normal;
+ text-decoration: inherit;
+ content:"\f069";
+}
+
+/* Modified original CSS to match input in Redbasic */
+.bootstrap-tagsinput {
+ border-color:#fff;
+ background-color: #fff;
+ box-shadow: none;
+ display: inline-block;
+ border-radius: $radiuspx;
+ cursor: text;
+ padding: 0 6px;
+ width: 70% !important;
+}
+
+.jothidden .bootstrap-tagsinput:hover, .jothidden .bootstrap-tagsinput:focus {
+ border: 1px solid #cccccc;
+}
diff --git a/view/tpl/attach_edit.tpl b/view/tpl/attach_edit.tpl
index a2a4af16b..bf39e6158 100644
--- a/view/tpl/attach_edit.tpl
+++ b/view/tpl/attach_edit.tpl
@@ -1,16 +1,16 @@
-
-
-
{{$header}}
-
-
diff --git a/view/tpl/cloud_actionspanel.tpl b/view/tpl/cloud_actionspanel.tpl
index 00f243c1e..d88bc94c4 100644
--- a/view/tpl/cloud_actionspanel.tpl
+++ b/view/tpl/cloud_actionspanel.tpl
@@ -1,27 +1,17 @@
-
-
-
-
- {{$folder_header}}
-
-
-
-
-
- {{$upload_header}}
-
-
-
-
-
-
+
+ {{$folder_header}}
+
+
+
+ {{$upload_header}}
+
diff --git a/view/tpl/cloud_directory.tpl b/view/tpl/cloud_directory.tpl
index 639a6bf61..f37895e09 100644
--- a/view/tpl/cloud_directory.tpl
+++ b/view/tpl/cloud_directory.tpl
@@ -1,5 +1,5 @@
-
{{$header}}
+
@@ -26,8 +26,9 @@
{{$item.displayName}}
{{if $item.is_owner}}
{{$item.attachIcon}}
-
-
+
+
+
{{else}}
{{/if}}
@@ -35,11 +36,8 @@
{{$item.sizeFormatted}}
{{$item.lastmodified}}
+
{{/foreach}}
-
-
-{{if $quota.limit || $quota.used}}
-
{{$total}} {{$quota.desc}}
-{{/if}}
+
diff --git a/view/tpl/cloud_header.tpl b/view/tpl/cloud_header.tpl
new file mode 100644
index 000000000..f5f123edb
--- /dev/null
+++ b/view/tpl/cloud_header.tpl
@@ -0,0 +1,13 @@
+
+ {{if $actionspanel}}
+
+ Create
+ Upload
+
+ {{/if}}
+
{{$header}}
+
+
+{{if $actionspanel}}
+ {{$actionspanel}}
+{{/if}}
diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl
index 34acce879..00ef77d93 100755
--- a/view/tpl/jot.tpl
+++ b/view/tpl/jot.tpl
@@ -19,17 +19,17 @@
{{$id_seltext}} {{$id_select}}
{{/if}}
-