diff --git a/frappe/public/build.json b/frappe/public/build.json
index 6d6a4283e7..33b6c6d659 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -122,7 +122,8 @@
"public/css/form.css",
"public/css/mobile.css",
"public/css/kanban.css",
- "public/css/controls.css"
+ "public/css/controls.css",
+ "public/css/tags.css"
],
"css/frappe-rtl.css": [
"public/css/bootstrap-rtl.css",
@@ -132,7 +133,6 @@
"js/libs.min.js": [
"public/js/lib/awesomplete/awesomplete.min.js",
"public/js/lib/Sortable.min.js",
- "public/js/lib/taggle/taggle.min.js",
"public/js/lib/jquery/jquery.hotkeys.js",
"public/js/lib/summernote/summernote.js",
"public/js/lib/bootstrap.min.js",
@@ -292,7 +292,6 @@
"public/js/frappe/form/quick_entry.js"
],
"css/list.min.css": [
- "public/js/lib/taggle/taggle.min.css",
"public/css/list.css",
"public/css/calendar.css",
"public/css/role_editor.css",
@@ -307,6 +306,7 @@
"public/js/frappe/ui/filters/filters.js",
"public/js/frappe/ui/filters/edit_filter.html",
"public/js/frappe/ui/tags.js",
+ "public/js/frappe/ui/tag_editor.js",
"public/js/frappe/ui/like.js",
"public/js/frappe/ui/liked_by.html",
"public/html/print_template.html",
diff --git a/frappe/public/css/tags.css b/frappe/public/css/tags.css
new file mode 100644
index 0000000000..fb77c61e81
--- /dev/null
+++ b/frappe/public/css/tags.css
@@ -0,0 +1,19 @@
+.tags-list {
+ float: left;
+ width: 100%;
+ padding-left: 3px;
+}
+.tags-input {
+ width: 100px;
+ font-size: 11px;
+ border: none;
+ outline: none;
+}
+.tags-list-item {
+ display: inline-block;
+ margin: 0px 3px;
+}
+.tags-placeholder {
+ display: inline-block;
+ font-size: 11px;
+}
diff --git a/frappe/public/js/frappe/ui/tag_editor.js b/frappe/public/js/frappe/ui/tag_editor.js
new file mode 100644
index 0000000000..3efab96aa4
--- /dev/null
+++ b/frappe/public/js/frappe/ui/tag_editor.js
@@ -0,0 +1,129 @@
+frappe.ui.TagEditor = Class.extend({
+ init: function(opts) {
+ /* docs:
+ Arguments
+
+ - parent
+ - user_tags
+ - doctype
+ - docname
+ */
+ $.extend(this, opts);
+
+ this.setup_tags();
+
+ if (!this.user_tags) {
+ this.user_tags = "";
+ }
+ this.initialized = true;
+ this.refresh(this.user_tags);
+ },
+ setup_tags: function() {
+ var me = this;
+
+ // hidden form, does not have parent
+ if (!this.parent) {
+ return;
+ }
+
+ this.wrapper = $('
').appendTo(this.parent);
+ if(!this.wrapper.length) return;
+
+ this.tags = new frappe.ui.Tags({
+ parent: this.wrapper,
+ placeholder: "Add a tag ...",
+ onTagAdd: (tag) => {
+ if(me.initialized && !me.refreshing) {
+ tag = toTitle(tag);
+ return frappe.call({
+ method: 'frappe.desk.tags.add_tag',
+ args: me.get_args(tag),
+ callback: function(r) {
+ var user_tags = me.user_tags ? me.user_tags.split(",") : [];
+ user_tags.push(tag)
+ me.user_tags = user_tags.join(",");
+ me.on_change && me.on_change(me.user_tags);
+ }
+ });
+ }
+ },
+ onTagRemove: (tag) => {
+ if(!me.refreshing) {
+ return frappe.call({
+ method: 'frappe.desk.tags.remove_tag',
+ args: me.get_args(tag),
+ callback: function(r) {
+ var user_tags = me.user_tags.split(",");
+ user_tags.splice(user_tags.indexOf(tag), 1);
+ me.user_tags = user_tags.join(",");
+ me.on_change && me.on_change(me.user_tags);
+ }
+ });
+ }
+ }
+ });
+ this.setup_awesomplete();
+ this.setup_complete = true;
+ },
+ setup_awesomplete: function() {
+ var me = this;
+ var $input = this.wrapper.find("input.tags-input");
+ var input = $input.get(0);
+ this.awesomplete = new Awesomplete(input, {
+ minChars: 0,
+ maxItems: 99,
+ list: []
+ });
+ $input.on("awesomplete-open", function(e){
+ $input.attr('state', 'open');
+ });
+ $input.on("awesomplete-close", function(e){
+ $input.attr('state', 'closed');
+ });
+ $input.on("input", function(e) {
+ var value = e.target.value;
+ frappe.call({
+ method:"frappe.desk.tags.get_tags",
+ args:{
+ doctype: me.frm.doctype,
+ txt: value.toLowerCase(),
+ cat_tags: me.list_sidebar ?
+ JSON.stringify(me.list_sidebar.get_cat_tags()) : '[]'
+ },
+ callback: function(r) {
+ me.awesomplete.list = r.message;
+ }
+ });
+ });
+ $input.on("focus", function(e) {
+ if($input.attr('state') != 'open') {
+ $input.trigger("input");
+ }
+ });
+ },
+ get_args: function(tag) {
+ return {
+ tag: tag,
+ dt: this.frm.doctype,
+ dn: this.frm.docname,
+ }
+ },
+ refresh: function(user_tags) {
+ var me = this;
+ if (!this.initialized || !this.setup_complete || this.refreshing) return;
+
+ me.refreshing = true;
+ try {
+ me.tags.clearTags();
+ if(user_tags) {
+ me.tags.addTags(user_tags.split(','));
+ }
+ } catch(e) {
+ me.refreshing = false;
+ // wtf bug
+ setTimeout( function() { me.refresh(); }, 100);
+ }
+ me.refreshing = false;
+
+ }
+})
diff --git a/frappe/public/js/frappe/ui/tags.js b/frappe/public/js/frappe/ui/tags.js
index f99da28ff7..e520d56978 100644
--- a/frappe/public/js/frappe/ui/tags.js
+++ b/frappe/public/js/frappe/ui/tags.js
@@ -1,131 +1,135 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
-frappe.ui.TagEditor = Class.extend({
- init: function(opts) {
- /* docs:
- Arguments
-
- - parent
- - user_tags
- - doctype
- - docname
- */
- $.extend(this, opts);
-
- this.setup_taggle();
-
- if (!this.user_tags) {
- this.user_tags = "";
- }
- this.initialized = true;
- this.refresh(this.user_tags);
- },
- setup_taggle: function() {
- var me = this;
-
- // hidden form, does not have parent
- if (!this.parent) {
- return;
- }
+frappe.ui.Tags = class {
+ constructor({
+ parent, placeholder, tagsList,
+ onTagAdd,
+ onTagRemove,
+ onTagClick,
+ onChange
+ }) {
+ this.tagsList = tagsList || [];
+ this.onTagAdd = onTagAdd;
+ this.onTagRemove = onTagRemove;
+ this.onTagClick = onTagClick;
+ this.onChange = onChange;
+
+ this.setup(parent, placeholder);
+ }
+
+ setup(parent, placeholder) {
+ this.$wrapper = $(`
`).appendTo(parent);
+ this.$ul = $(`
`).appendTo(this.$wrapper);
+ this.$input = $(`
`);
+
+ this.$inputWrapper = this.getListElement(this.$input);
+ this.$placeholder = this.getListElement($(`
${placeholder}`));
+ this.$inputWrapper.appendTo(this.$ul);
+ this.$placeholder.appendTo(this.$ul);
- this.wrapper = $('
').appendTo(this.parent);
- if(!this.wrapper.length) return;
- var id = frappe.dom.set_unique_id(this.wrapper);
- this.taggle = new Taggle(id, {
- placeholder: __('Add a tag') + "...",
- onTagAdd: function(e, tag) {
- if(me.initialized && !me.refreshing) {
- tag = toTitle(tag);
- return frappe.call({
- method: 'frappe.desk.tags.add_tag',
- args: me.get_args(tag),
- callback: function(r) {
- var user_tags = me.user_tags ? me.user_tags.split(",") : [];
- user_tags.push(tag)
- me.user_tags = user_tags.join(",");
- me.on_change && me.on_change(me.user_tags);
- }
- });
- }
- },
- onTagRemove: function(e, tag) {
- if(!me.refreshing) {
- return frappe.call({
- method: 'frappe.desk.tags.remove_tag',
- args: me.get_args(tag),
- callback: function(r) {
- var user_tags = me.user_tags.split(",");
- user_tags.splice(user_tags.indexOf(tag), 1);
- me.user_tags = user_tags.join(",");
- me.on_change && me.on_change(me.user_tags);
- }
- });
- }
+ this.deactivate();
+ this.bind();
+ this.boot();
+ }
+
+ bind() {
+ this.$input.keypress((e) => {
+ if(e.which == 13 || e.keyCode == 13) {
+ this.addTag(this.$input.val());
+ this.$input.val('');
}
});
- this.setup_awesomplete();
- this.setup_complete = true;
- },
- setup_awesomplete: function() {
- var me = this;
- var $input = this.wrapper.find("input.taggle_input");
- var input = $input.get(0);
- this.awesomplete = new Awesomplete(input, {
- minChars: 0,
- maxItems: 99,
- list: []
+
+ this.$input.on('blur', () => {
+ this.deactivate();
});
- $input.on("awesomplete-open", function(e){
- $input.attr('state', 'open');
+
+ this.$placeholder.on('click', () => {
+ this.activate();
});
- $input.on("awesomplete-close", function(e){
- $input.attr('state', 'closed');
+ }
+
+ boot() {
+ this.addTags(this.tagsList);
+ }
+
+ activate() {
+ this.$placeholder.hide();
+ this.$inputWrapper.show();
+ this.$input.focus();
+ }
+
+ deactivate() {
+ this.$inputWrapper.hide();
+ this.$placeholder.show();
+ }
+
+ refresh() {
+ this.deactivate();
+ this.activate();
+ }
+
+ addTag(label) {
+ if(label && !this.tagsList.includes(label)) {
+ let $tag = this.getTag(label);
+ this.getListElement($tag).insertBefore(this.$inputWrapper);
+ this.tagsList.push(label);
+ this.onTagAdd && this.onTagAdd(label);
+
+ this.refresh();
+ }
+ }
+
+ removeTag(label) {
+ if(this.tagsList.includes(label)) {
+ let $tag = this.$ul.find(`.frappe-tag[data-tag-label="${label}"]`);
+ $tag.remove();
+ this.tagsList = this.tagsList.filter(d => d !== label);
+ this.onTagRemove && this.onTagRemove(label);
+ }
+ }
+
+ addTags(labels) {
+ labels.map(this.addTag.bind(this));
+ }
+
+ clearTags() {
+ this.$ul.find('.frappe-tag').remove();
+ this.tagsList = [];
+ }
+
+ getListElement($element, className) {
+ let $li = $(`
`);
+ $element.appendTo($li);
+ return $li;
+ }
+
+ getTag(label) {
+ let $tag = $(`
+
+
`);
+
+ let $removeTag = $tag.find(".remove-tag");
+
+ $removeTag.on("click", () => {
+ this.removeTag($removeTag.attr('data-tag-label'));
});
- $input.on("input", function(e) {
- var value = e.target.value;
- frappe.call({
- method:"frappe.desk.tags.get_tags",
- args:{
- doctype: me.frm.doctype,
- txt: value.toLowerCase(),
- cat_tags: me.list_sidebar ?
- JSON.stringify(me.list_sidebar.get_cat_tags()) : '[]'
- },
- callback: function(r) {
- me.awesomplete.list = r.message;
- }
+
+ if(this.onTagClick) {
+ let $toggle_tag = $tag.find(".toggle-tag");
+ $toggle_tag.on("click", () => {
+ this.onTagClick($toggle_tag.attr('data-tag-label'));
});
- });
- $input.on("focus", function(e) {
- if($input.attr('state') != 'open') {
- $input.trigger("input");
- }
- });
- },
- get_args: function(tag) {
- return {
- tag: tag,
- dt: this.frm.doctype,
- dn: this.frm.docname,
- }
- },
- refresh: function(user_tags) {
- var me = this;
- if (!this.initialized || !this.setup_complete || this.refreshing) return;
-
- me.refreshing = true;
- try {
- me.taggle.removeAll();
- if(user_tags) {
- me.taggle.add(user_tags.split(','));
- }
- } catch(e) {
- me.refreshing = false;
- // wtf bug
- setTimeout( function() { me.refresh(); }, 100);
}
- me.refreshing = false;
+ return $tag;
}
-})
+}
diff --git a/frappe/public/js/lib/taggle/taggle.min.css b/frappe/public/js/lib/taggle/taggle.min.css
deleted file mode 100644
index 310525ba71..0000000000
--- a/frappe/public/js/lib/taggle/taggle.min.css
+++ /dev/null
@@ -1,103 +0,0 @@
-.taggle_list {
- float: left;
- width: 100%;
-}
-
-.taggle_input {
- border: none;
- outline: none;
- font-size: 16px;
- font-weight: 300;
-}
-
-.taggle_list li {
- float: left;
- display: inline-block;
- white-space: nowrap;
- font-weight: 500;
- margin-bottom: 5px;
-}
-
-.taggle_list .taggle {
- margin-right: 8px;
- background: #E2E1DF;
- padding: 5px 10px;
- border-radius: 3px;
- position: relative;
- cursor: pointer;
- transition: all .3s;
- animation-duration: 1s;
- animation-fill-mode: both;
-}
-
-.taggle_list .taggle_hot {
- background: #cac8c4;
-}
-
-.taggle_list .taggle .close {
- font-size: 1.1rem;
- position: absolute;
- top: 5px;
- right: 3px;
- text-decoration: none;
- padding-left: 2px;
- padding-top: 3px;
- line-height: 0.5;
- color: #ccc;
- color: rgba(0, 0, 0, 0.2);
- padding-bottom: 4px;
- display: none;
- border: 0;
- background: none;
- cursor: pointer;
-}
-
-.taggle_list .taggle:hover {
- padding: 5px;
- padding-right: 15px;
- background: #ccc;
- transition: all .3s;
-}
-
-.taggle_list .taggle:hover > .close {
- display: block;
-}
-
-.taggle_list .taggle .close:hover {
- color: #333;
-}
-
-.taggle_placeholder {
- position: absolute;
- color: #CCC;
- top: 12px;
- left: 8px;
- transition: opacity, .25s;
- user-select: none;
-}
-
-.taggle_input {
- padding: 8px;
- padding-left: 0;
- float: left;
- margin-top: -5px;
- background: none;
- width: 100%;
- max-width: 100%;
-}
-
-.taggle_sizer {
- padding: 0;
- margin: 0;
- position: absolute;
- top: -500px;
- z-index: -1;
- visibility: hidden;
-}
-
-.clearfix:before, .clearfix:after {
- display: block;
- content: "";
- line-height: 0;
- clear: both;
-}
\ No newline at end of file
diff --git a/frappe/public/js/lib/taggle/taggle.min.js b/frappe/public/js/lib/taggle/taggle.min.js
deleted file mode 100644
index a70859315e..0000000000
--- a/frappe/public/js/lib/taggle/taggle.min.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
-* @author Sean Coker
-* @version 1.10.1
-* @url http://sean.is/poppin/tags
-* @license MIT
-* @description Taggle is a dependency-less tagging library
-*/
-!function(){Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),window.getComputedStyle||(window.getComputedStyle=function(a){return a.currentStyle}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){"use strict";if(null==this)throw new TypeError;var b,c,d=Object(this),e=d.length>>>0;if(0===e)return-1;if(b=0,arguments.length>1&&(b=Number(arguments[1]),b!=b?b=0:0!=b&&b!=1/0&&b!=-(1/0)&&(b=(b>0||-1)*Math.floor(Math.abs(b)))),b>=e)return-1;for(c=b>=0?b:Math.max(e-Math.abs(b),0);e>c;c++)if(c in d&&d[c]===a)return c;return-1}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c,d;if(null==this)throw new TypeError(" this is null or not defined");var e=Object(this),f=e.length>>>0;if("function"!=typeof a)throw new TypeError(a+" is not a function");for(arguments.length>1&&(c=b),d=0;f>d;){var g;d in e&&(g=e[d],a.call(c,g,d,e)),d++}}),Array.prototype.map||(Array.prototype.map=function(a,b){var c,d,e;if(null==this)throw new TypeError(" this is null or not defined");var f=Object(this),g=f.length>>>0;if("function"!=typeof a)throw new TypeError(a+" is not a function");for(arguments.length>1&&(c=b),d=new Array(g),e=0;g>e;){var h,i;e in f&&(h=f[e],i=a.call(c,h,e,f),d[e]=i),e++}return d})}(),function(){"undefined"==typeof document||"classList"in document.documentElement||!function(a){"use strict";if("HTMLElement"in a||"Element"in a){var b="classList",c="prototype",d=(a.HTMLElement||a.Element)[c],e=Object,f=String[c].trim||function(){return this.replace(/^\s+|\s+$/g,"")},g=Array[c].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1},h=function(a,b){this.name=a,this.code=DOMException[a],this.message=b},i=function(a,b){if(""===b)throw new h("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(b))throw new h("INVALID_CHARACTER_ERR","String contains an invalid character");return g.call(a,b)},j=function(a){for(var b=f.call(a.className),c=b?b.split(/\s+/):[],d=0,e=c.length;e>d;d++)this.push(c[d]);this._updateClassName=function(){a.className=this.toString()}},k=j[c]=[],l=function(){return new j(this)};if(h[c]=Error[c],k.item=function(a){return this[a]||null},k.contains=function(a){return a+="",-1!==i(this,a)},k.add=function(){var a,b=arguments,c=0,d=b.length,e=!1;do a=b[c]+"",-1===i(this,a)&&(this.push(a),e=!0);while(++cb;b++){var d=arguments[b];for(var e in d)d.hasOwnProperty(e)&&(a[e]=d[e])}return a}function b(a){return Array.isArray?Array.isArray(a):"[object Array]"===Object.prototype.toString.call(a)}function c(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent?a.attachEvent("on"+b,c):a["on"+b]=c}function d(a){return a.replace(/^\s+|\s+$/g,"")}function e(a,b){window.attachEvent&&!window.addEventListener?a.innerText=b:a.textContent=b}var f=function(){},g=function(){return!0},h=8,i=188,j=9,k=13,l={additionalTagClasses:"",allowDuplicates:!1,saveOnBlur:!1,duplicateTagClass:"",containerFocusClass:"active",focusInputOnContainerClick:!0,hiddenInputName:"taggles[]",tags:[],delimeter:",",attachTagId:!1,allowedTags:[],disallowedTags:[],maxTags:null,tabIndex:1,placeholder:"Enter tags...",submitKeys:[i,j,k],preserveCase:!1,inputFormatter:f,tagFormatter:f,onBeforeTagAdd:f,onTagAdd:f,onBeforeTagRemove:g,onTagRemove:f},m=function(b,c){this.settings=a({},l,c),this.measurements={container:{rect:null,style:null,padding:null}},this.container=b,this.tag={values:[],elements:[]},this.list=document.createElement("ul"),this.inputLi=document.createElement("li"),this.input=document.createElement("input"),this.sizer=document.createElement("div"),this.pasting=!1,this.placeholder=null,this.settings.placeholder&&(this.placeholder=document.createElement("span")),"string"==typeof b&&(this.container=document.getElementById(b)),this._id=0,this._setMeasurements(),this._setupTextarea(),this._attachEvents()};return m.prototype._setMeasurements=function(){this.measurements.container.rect=this.container.getBoundingClientRect(),this.measurements.container.style=window.getComputedStyle(this.container);var a=this.measurements.container.style,b=parseInt(a["padding-left"]||a.paddingLeft,10),c=parseInt(a["padding-right"]||a.paddingRight,10),d=parseInt(a["border-left-width"]||a.borderLeftWidth,10),e=parseInt(a["border-right-width"]||a.borderRightWidth,10);this.measurements.container.padding=b+c+d+e},m.prototype._setupTextarea=function(){var a;if(this.list.className="taggle_list",this.input.type="text",this.input.style.paddingLeft=0,this.input.style.paddingRight=0,this.input.className="taggle_input",this.input.tabIndex=this.settings.tabIndex,this.sizer.className="taggle_sizer",this.settings.tags.length)for(var b=0,c=this.settings.tags.length;c>b;b++){var d=this._createTag(this.settings.tags[b]);this.list.appendChild(d)}this.placeholder&&(this.placeholder.style.opacity=0,this.placeholder.classList.add("taggle_placeholder"),this.container.appendChild(this.placeholder),e(this.placeholder,this.settings.placeholder),this.settings.tags.length||(this.placeholder.style.opacity=1));var f=this.settings.inputFormatter(this.input);f&&(this.input=f),this.inputLi.appendChild(this.input),this.list.appendChild(this.inputLi),this.container.appendChild(this.list),this.container.appendChild(this.sizer),a=window.getComputedStyle(this.input).fontSize,this.sizer.style.fontSize=a},m.prototype._attachEvents=function(){var a=this;this.settings.focusInputOnContainerClick&&c(this.container,"click",function(){a.input.focus()}),c(this.input,"focus",this._focusInput.bind(this)),c(this.input,"blur",this._blurEvent.bind(this)),c(this.input,"keydown",this._keydownEvents.bind(this)),c(this.input,"keyup",this._keyupEvents.bind(this))},m.prototype._fixInputWidth=function(){var a,b,c,d,e;this._setMeasurements(),this._setInputWidth(),b=this.input.getBoundingClientRect(),c=this.measurements.container.rect,a=~~c.width,a||(a=~~c.right-~~c.left),d=~~b.left-~~c.left,e=this.measurements.container.padding,this._setInputWidth(a-d-e)},m.prototype._canAdd=function(a,b){if(!b)return!1;var c=this.settings.maxTags;if(null!==c&&c<=this.getTagValues().length)return!1;if(this.settings.onBeforeTagAdd(a,b)===!1)return!1;if(!this.settings.allowDuplicates&&this._hasDupes(b))return!1;var d=this.settings.preserveCase,e=this.settings.allowedTags;if(e.length&&!this._tagIsInArray(b,e,d))return!1;var f=this.settings.disallowedTags;return!f.length||!this._tagIsInArray(b,f,d)},m.prototype._tagIsInArray=function(a,b,c){if(c)return-1!==b.indexOf(a);var d=[].slice.apply(b).map(function(a){return a.toLowerCase()});return-1!==d.indexOf(a)},m.prototype._add=function(a,b){var c=this,e=b||"";"string"!=typeof b&&(e=d(this.input.value)),e.split(this.settings.delimeter).map(function(a){return c._formatTag(a)}).forEach(function(b){if(c._canAdd(a,b)){var d=c._createTag(b),e=c.list.children,f=e[e.length-1];c.list.insertBefore(d,f),b=c.tag.values[c.tag.values.length-1],c.settings.onTagAdd(a,b),c.input.value="",c._fixInputWidth(),c._focusInput()}})},m.prototype._checkLastTag=function(a){a=a||window.event;var b=this.container.querySelectorAll(".taggle"),c=b[b.length-1],d="taggle_hot",e=this.input.classList.contains("taggle_back");""!==this.input.value||a.keyCode!==h||e?c.classList.contains(d)&&c.classList.remove(d):c.classList.contains(d)?(this.input.classList.add("taggle_back"),this._remove(c,a),this._fixInputWidth(),this._focusInput()):c.classList.add(d)},m.prototype._setInputWidth=function(a){this.input.style.width=(a||10)+"px"},m.prototype._hasDupes=function(a){var b,c=this.tag.values.indexOf(a),d=this.container.querySelector(".taggle_list");if(this.settings.duplicateTagClass){b=d.querySelectorAll("."+this.settings.duplicateTagClass);for(var e=0,f=b.length;f>e;e++)b[e].classList.remove(this.settings.duplicateTagClass)}return c>-1?(this.settings.duplicateTagClass&&d.childNodes[c].classList.add(this.settings.duplicateTagClass),!0):!1},m.prototype._isConfirmKey=function(a){var b=!1;return this.settings.submitKeys.indexOf(a)>-1&&(b=!0),b},m.prototype._focusInput=function(){this._fixInputWidth(),this.container.classList.contains(this.settings.containerFocusClass)||this.container.classList.add(this.settings.containerFocusClass),this.placeholder&&(this.placeholder.style.opacity=0)},m.prototype._blurEvent=function(a){if(this.container.classList.contains(this.settings.containerFocusClass)&&this.container.classList.remove(this.settings.containerFocusClass),!this.tag.values.length&&this.placeholder&&(this.placeholder.style.opacity=1),this.settings.saveOnBlur){if(a=a||window.event,this._listenForEndOfContainer(),""!==this.input.value)return void this._confirmValidTagEvent(a);this.tag.values.length&&this._checkLastTag(a)}else this.input.value="",this._setInputWidth()},m.prototype._keydownEvents=function(a){a=a||window.event;var b=a.keyCode;return this.pasting=!1,this._listenForEndOfContainer(),86===b&&a.metaKey&&(this.pasting=!0),this._isConfirmKey(b)&&""!==this.input.value?void this._confirmValidTagEvent(a):void(this.tag.values.length&&this._checkLastTag(a))},m.prototype._keyupEvents=function(a){a=a||window.event,this.input.classList.remove("taggle_back"),e(this.sizer,this.input.value),this.pasting&&""!==this.input.value&&(this._add(a),this.pasting=!1)},m.prototype._confirmValidTagEvent=function(a){a=a||window.event,a.preventDefault?a.preventDefault():a.returnValue=!1,this._add(a)},m.prototype._listenForEndOfContainer=function(){var a=this.sizer.getBoundingClientRect().width,b=this.measurements.container.rect.width-this.measurements.container.padding,c=parseInt(this.sizer.style.fontSize,10);a+1.5*c>parseInt(this.input.style.width,10)&&(this.input.style.width=b+"px")},m.prototype._createTag=function(a){var b=document.createElement("li"),d=document.createElement("button"),f=document.createElement("input"),g=document.createElement("span");a=this._formatTag(a),d.innerHTML="×",d.className="close",d.type="button",c(d,"click",this._remove.bind(this,d)),e(g,a),g.className="taggle_text",b.className="taggle "+this.settings.additionalTagClasses,f.type="hidden",f.value=a,f.name=this.settings.hiddenInputName,b.appendChild(g),b.appendChild(d),b.appendChild(f);var h=this.settings.tagFormatter(b);if("undefined"!=typeof h&&(b=h),!(b instanceof HTMLElement)||"LI"!==b.tagName)throw new Error("tagFormatter must return an li element");return this.settings.attachTagId&&(this._id+=1,a={text:a,id:this._id}),this.tag.values.push(a),this.tag.elements.push(b),b},m.prototype._remove=function(a,b){function c(c){c||(a.parentNode.removeChild(a),g.tag.elements.splice(f,1),g.tag.values.splice(f,1),g.settings.onTagRemove(b,d),g._focusInput())}var d,e,f,g=this;"li"!==a.tagName.toLowerCase()&&(a=a.parentNode),e="a"===a.tagName.toLowerCase()?a.parentNode:a,f=this.tag.elements.indexOf(e),d=this.tag.values[f];var h=this.settings.onBeforeTagRemove(b,d,c);h&&c()},m.prototype._formatTag=function(a){return this.settings.preserveCase?a:a.toLowerCase()},m.prototype.getTags=function(){return{elements:this.getTagElements(),values:this.getTagValues()}},m.prototype.getTagElements=function(){return this.tag.elements},m.prototype.getTagValues=function(){return[].slice.apply(this.tag.values)},m.prototype.getInput=function(){return this.input},m.prototype.getContainer=function(){return this.container},m.prototype.add=function(a){var c=b(a);if(c)for(var d=0,e=a.length;e>d;d++)"string"==typeof a[d]&&this._add(null,a[d]);else this._add(null,a);return this},m.prototype.remove=function(a,b){for(var c=this.tag.values.length-1,d=!1;c>-1;){var e=this.tag.values[c];if(this.settings.attachTagId&&(e=e.text),e===a&&(d=!0,this._remove(this.tag.elements[c])),d&&!b)break;c--}return this},m.prototype.removeAll=function(){for(var a=this.tag.values.length-1;a>=0;a--)this._remove(this.tag.elements[a]);return this},m.prototype.setOptions=function(b){return this.settings=a({},this.settings,b||{}),this},m});
\ No newline at end of file
diff --git a/frappe/public/less/tags.less b/frappe/public/less/tags.less
new file mode 100644
index 0000000000..6887a1df85
--- /dev/null
+++ b/frappe/public/less/tags.less
@@ -0,0 +1,22 @@
+.tags-list {
+ float: left;
+ width: 100%;
+ padding-left: 3px;
+}
+
+.tags-input {
+ width: 100px;
+ font-size: 11px;
+ border: none;
+ outline: none;
+}
+
+.tags-list-item {
+ display: inline-block;
+ margin: 0px 3px;
+}
+
+.tags-placeholder {
+ display: inline-block;
+ font-size: 11px;
+}