JsTag
How do I make an jstag?
Http://eranhirs.github.io/jsTag/ https://github.com/eranhirs/jsTag http://eranhirs.github.io/jsTag/assets/angular-typeahead.js. What is a jstag? How do you make a jstag? This script and codes were developed by Kevin on 28 August 2022, Sunday.
JsTag - Script Codes HTML Codes
<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>jsTag</title> <link rel="stylesheet" href="css/style.css">
</head>
<body> <div class="bs-example"> <div ng-controller="MoreControlController"> <js-tag js-tag-options="jsTagOptions"></js-tag> Number of tags: {{tags.getNumberOfTags()}} </div>
</div>
<!--
<div class="bs-example"> <div ng-controller="CustomizedController"> <js-tag js-tag-options='{ "texts": {"inputPlaceHolder": "Type text here"}, "defaultTags": ["Default Tag #1", "Default Tag #2"]}'> </js-tag> </div>
</div>
<div class="bs-example"> <div ng-controller="NoneditableController"> <js-tag js-tag-options="jsTagOptions"></js-tag> </div>
</div>
<div class="bs-example"> <div ng-controller="TypeaheadController"> <js-tag js-tag-mode="typeahead" js-tag-options="jsTagOptions"></js-tag> Number of tags: {{tags.getNumberOfTags()}} </div>
</div>
--> <script src='http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js'></script>
<script src='http://eranhirs.github.io/jsTag/assets/angular-typeahead.js'></script> <script src="js/index.js"></script>
</body>
</html>
JsTag - Script Codes CSS Codes
/* * Container * Explaining some of the CSS: # background-color: #FFFFFF; | To simulate an input box - in case background of the whole page is different
*/
.jt-editor { padding: 6px 12px 5px 12px; display: block; height: auto; vertical-align: middle; font-size: 14px; line-height: 1.428571429; color: #555; border: 1px solid #BFBFBF; border-radius: 2px; cursor: text; background-color: #FFFFFF;
}
.jt-editor.focused-true { border-color: #66AFE9; outline: 0;
}
/* Tag */
.jt-tag { background: #DEE7F8; border: 1px solid #949494; padding: 0px 0px 20px 2px; cursor: default; display: inline-block; -webkit-border-radius: 2px 3px 3px 2px; -moz-border-radius: 2px 3px 3px 2px; border-radius: 2px 3px 3px 2px; height: 22px;
}
/* Because bootstrap uses it and we don't want that if bootstrap is not included to look different */
.jt-tag { box-sizing: border-box;
}
.jt-tag:hover { border-color: #BCBCBC;
}
/* Value inside jt-tag */
.jt-tag .value { padding-left: 4px;
}
/* Tag when active */
.jt-tag.active-true { border-color: rgba(82, 168, 236, 0.8);
}
/* Tag remove button ('x') */
.jt-tag .remove-button { cursor: pointer; padding-right: 4px;
}
.jt-tag .remove-button:hover { font-weight: bold;
}
/* New tag input & Edit tag input */
.jt-tag-new, .jt-tag-edit { border: none; outline: 0px; min-width: 50px; /* Will keep autogrow from lowering width more than 60 */
}
/* New tag input & Edit tag input & Tag */
.jt-tag-new, .jt-tag-edit, .jt-tag { margin: 1px 4px 1px 1px;
}
/* Should not be displayed, only used to capture keydown */
.jt-fake-input { float: left; position: absolute; left: -10000px; width: 1px; border: 0px;
}
JsTag - Script Codes JS Codes
angular.module("jsTag", []) .constant("jsTagDefaults", { 'edit': true, 'defaultTags': [], 'breakCodes': [13, 44], 'splitter': ',', 'texts': { 'inputPlaceHolder': 'Input text', 'removeSymbol': String.fromCharCode(215) } }) .run(["$templateCache", function($templateCache){ $templateCache.put("jsTag/source/templates/default/js-tag.html", '<div class="jt-editor" ng-click="inputService.focusInput()" ><span ng-repeat="tag in tagsCollection.tags | toArray:orderBy:\'id\'" ng-switch="tagsCollection.isTagEdited(tag)"> <span ng-switch-when="false" class="jt-tag active-{{tagsCollection.isTagActive(tag)}}"> <span class="value" ng-click="tagsInputService.tagClicked(tag)" ng-dblclick="tagsInputService.tagDblClicked(tag)"> {{tag.value}} </span> <span class="remove-button" ng-click="tagsCollection.removeTag(tag.id)">{{options.texts.removeSymbol}}</span> </span> <span ng-switch-when="true"> <input type="text" class="jt-tag-edit" focus-once ng-model="tag.value" data-tag-id="{{tag.id}}" ng-keydown="inputService.tagInputKeydown(tagsCollection, {$event: $event})" placeholder="{{options.texts.inputPlaceHolder}}" auto-grow /> </span> </span> <input class="jt-tag-new" type="text" focus-me="inputService.isWaitingForInput" ng-model="inputService.input" ng-hide="isThereAnEditedTag" ng-keydown="inputService.onKeydown(inputService, tagsCollection, {$event: $event})" placeholder="{{options.texts.inputPlaceHolder}}" ng-blur="inputService.onBlur(tagsCollection)" auto-grow /> <input class="jt-fake-input" focus-me="isThereAnActiveTag" ng-keydown="tagsInputService.onActiveTagKeydown(inputService, {$event: $event})" ng-blur="tagsInputService.onActiveTagBlur()" /></div>'); }]);
angular.module("jsTag") .filter('inArray', function(){ return function(needle, haystack){ for(var key in haystack){ if(needle === haystack[key]){ return true; } } return false; }; }) .filter('toArray', function(){ return function(input){ var objectsArray = []; for(var key in input){ objectsArray.push(input[key]); } return objectsArray; }; });
angular.module("jsTag") .factory("JSTag", function(){ function JSTag(value, id){ this.value = value; this.id = id; } return JSTag; }) .factory("JSTagsCollection", ['JSTag', '$filter', function(JSTag, $filter){ function JSTagsCollection(defaultTags){ this.tags = {}; this.tagsCounter = 0; for(var defaultTagKey in defaultTags){ var defaultTagValue = defaultTags[defaultTagKey]; this.addTag(defaultTagValue); } this._onAddListenerList = []; this._onRemoveListenerList = []; this.unsetActiveTags(); this.unsetEditedTag(); } //TODO: Object manipulation methods JSTagsCollection.prototype.addTag = function(value) { var tagIndex = this.tagsCounter; this.tagsCounter++; var newTag = new JSTag(value, tagIndex); this.tags[tagIndex] = newTag; angular.forEach(this._onAddListenerList, function (callback) { callback(newTag); }); }; JSTagsCollection.prototype.removeTag = function(tagIndex) { var tag = this.tags[tagIndex]; delete this.tags[tagIndex]; angular.forEach(this._onRemoveListenerList, function (callback) { callback(tag); }); }; JSTagsCollection.prototype.onAdd = function onAdd(callback) { this._onAddListenerList.push(callback); }; JSTagsCollection.prototype.onRemove = function onRemove(callback) { this._onRemoveListenerList.push(callback); }; JSTagsCollection.prototype.getNumberOfTags = function() { return getNumberOfProperties(this.tags); }; JSTagsCollection.prototype.getTagValues = function() { var tagValues = []; for (var tag in this.tags) { tagValues.push(this.tags[tag].value); } return tagValues; }; JSTagsCollection.prototype.getPreviousTag = function(tag) { var firstTag = getFirstProperty(this.tags); if (firstTag.id === tag.id) { // Return same tag if we reached the beginning return tag; } else { return getPreviousProperty(this.tags, tag.id); } }; JSTagsCollection.prototype.getNextTag = function(tag) { var lastTag = getLastProperty(this.tags); if (tag.id === lastTag.id) { // Return same tag if we reached the end return tag; } else { return getNextProperty(this.tags, tag.id); } }; //TODO: Active methods JSTagsCollection.prototype.isTagActive = function(tag) { return $filter("inArray")(tag, this._activeTags); }; JSTagsCollection.prototype.setActiveTag = function(tag) { if (!this.isTagActive(tag)) { this._activeTags.push(tag); } }; JSTagsCollection.prototype.setLastTagActive = function() { if (getNumberOfProperties(this.tags) > 0) { var lastTag = getLastProperty(this.tags); this.setActiveTag(lastTag); } }; JSTagsCollection.prototype.unsetActiveTag = function(tag) { var removedTag = this._activeTags.splice(this._activeTags.indexOf(tag), 1); }; JSTagsCollection.prototype.unsetActiveTags = function() { this._activeTags = []; }; JSTagsCollection.prototype.getActiveTag = function() { var activeTag = null; if (this._activeTags.length === 1) { activeTag = this._activeTags[0]; } return activeTag; }; JSTagsCollection.prototype.getNumOfActiveTags = function() { return this._activeTags.length; }; //TODO: Edit methods JSTagsCollection.prototype.getEditedTag = function() { return this._editedTag; }; JSTagsCollection.prototype.isTagEdited = function(tag) { return tag === this._editedTag; }; JSTagsCollection.prototype.setEditedTag = function(tag) { this._editedTag = tag; }; JSTagsCollection.prototype.unsetEditedTag = function() { // Kill empty tags! if (this._editedTag !== undefined && this._editedTag !== null && this._editedTag.value === "") { this.removeTag(this._editedTag.id); } this._editedTag = null; }; return JSTagsCollection; }]);
angular.module("jsTag") .factory("InputService", ['$filter', function($filter){ function InputService(options){ this.input = ""; this.isWaitingForInput = options.autoFocus || false; this.options = options; } //TODO: Events InputService.prototype.onkeydown = function(inputService, tagsCollection, options){ var e = options.$event; var $element = angular.element(e.currentTarget); var keycode = e.which; var value = ($element.typeahead !== undefined) ? $element.typeahead('val') : this.input; var valueIsEmpty = (value === null || value === undefined || value === ""); if($filter("inArray")(keycode, this.options.breakCodes) !== false){ inputService.breakCodeHit(tagsCollection, this.options); $element.triggerHandler('jsTag:breakcodeHit'); if(!valueIsEmpty){ e.preventDefault(); } }else{ switch (keycode){ case 9: //TAB break; case 37:// Left arrow case 8://Backapace if(valueIsEmpty){ tagsCollection.setLastTagActive(); } break; } } }; InputService.prototype.tagInputKeydown = function(tagsCollection, options){ var e = options.$event; var keycode = e.which; if($filter("inArray")(keycode, this.options.breakCodes) !== false){ this.breakCodeHitOnEdit(tagsCollection, options); } }; InputService.prototype.onBlur = function(tagsCollection) { this.breakCodeHit(tagsCollection, this.options); }; //TODO: Method InputService.prototype.resetInput = function() { var value = this.input; this.input = ""; return value; }; InputService.prototype.focusInput = function() { this.isWaitingForInput = true; }; InputService.prototype.breakCodeHit = function(tagsCollection, options) { if( this.input !== ''){ var originalValue = this.resetInput(); if(originalValue instanceof Object){ originalValue = originalValue[options.tagDisplayKey || Object.keys(originalValue)[0]]; } var values = originalValue.split(options.splitter); for (var i = 0; i < values.length; i++) { if (!values[i]) { values.splice(i, 1); i--; } } for (var key in values) { if ( !values.hasOwnProperty(key) ) continue; // for IE 8 var value = values[key]; tagsCollection.addTag(value); } } }; InputService.prototype.breakCodeHitOnEdit = function(tagsCollection, options) { var editedTag = tagsCollection.getEditedTag(); if (editedTag.value instanceof Object) { editedTag.value = editedTag.value[options.tagDisplayKey || Object.keys(editedTag.value)[0]]; } tagsCollection.unsetEditedTag(); this.isWaitingForInput = true; }; return InputService; }]) .factory("TagsInputService", ['JSTag', 'JSTagsCollection', function(JSTag, JSTagsCollection){ function TagsHandler(options){ this.options = options; var tags = options.tags; if (tags && Object.getPrototypeOf(tags) === JSTagsCollection.prototype) { this.tagsCollection = tags; }else{ var defaultTags = options.defaultTags; this.tagsCollection = new JSTagsCollection(defaultTags); } this.shouldBlurActiveTag = true; } TagsHandler.prototype.tagClicked = function(tag) { this.tagsCollection.setActiveTag(tag); }; TagsHandler.prototype.tagDblClicked = function(tag) { var editAllowed = this.options.edit; if (editAllowed) { this.tagsCollection.setEditedTag(tag); } }; TagsHandler.prototype.onActiveTagKeydown = function(inputService, options) { var activeTag = this.tagsCollection.getActiveTag(); // Do nothing in unexpected situations if (activeTag !== null) { var e = options.$event; // Mimics blur of the active tag though the focus is on the input. // This will cause expected features like unseting active tag var blurActiveTag = function() { // Expose the option not to blur the active tag if (this.shouldBlurActiveTag) { this.onActiveTagBlur(options); } }; switch (e.which) { case 13: // Return var editAllowed = this.options.edit; if (editAllowed) { blurActiveTag.apply(this); this.tagsCollection.setEditedTag(activeTag); } break; case 8: // Backspace this.tagsCollection.removeTag(activeTag.id); inputService.isWaitingForInput = true; break; case 37: // Left arrow blurActiveTag.apply(this); var previousTag = this.tagsCollection.getPreviousTag(activeTag); this.tagsCollection.setActiveTag(previousTag); break; case 39: // Right arrow blurActiveTag.apply(this); var nextTag = this.tagsCollection.getNextTag(activeTag); if (nextTag !== activeTag) { this.tagsCollection.setActiveTag(nextTag); } else { inputService.isWaitingForInput = true; } break; } } }; TagsHandler.prototype.onActiveTagBlur = function(options) { var activeTag = this.tagsCollection.getActiveTag(); // Do nothing in unexpected situations if (activeTag !== null) { this.tagsCollection.unsetActiveTag(activeTag); } }; TagsHandler.prototype.onEditTagBlur = function(tagsCollection, inputService) { tagsCollection.unsetEditedTag(); this.isWaitingForInput = true; } return TagsHandler; }]);
angular.module("jsTag") .controller("JSTagMainCtrl", ['$attrs', '$scope', 'InputService', 'TagsInputService', 'jsTagDefaults', function($attrs, $scope, InputService, TagsInputService, jsTagDefaults){ var userOptions = {}; try { userOptions = $scope.$eval($attrs.jsTagOptions); } catch(e) { console.log("jsTag Error: Invalid user options, using defaults only"); } var options = angular.copy(jsTagDefaults); if (userOptions !== undefined) { userOptions.texts = angular.extend(options.texts, userOptions.texts || {}); angular.extend(options, userOptions); } $scope.options = options; $scope.tagsInputService = new TagsInputService($scope.options); $scope.inputService = new InputService($scope.options); var tagsCollection = $scope.tagsInputService.tagsCollection; $scope.tagsCollection = tagsCollection; $scope.$watch('tagsCollection._editedTag', function(newValue, oldValue) { $scope.isThereAnEditedTag = newValue !== null; }); $scope.$watchCollection('tagsCollection._activeTags', function(newValue, oldValue) { $scope.isThereAnActiveTag = newValue.length > 0; }); } ]);
angular.module("jsTag") .directive("jsTag", ['$templateCache', function($templateCache){ return { restrict: 'E', scope: true, controller: 'JSTagMainCtrl', templateUrl: function($element, $attrs){ var mode = $attrs.jsTagMode || "default"; return 'jsTag/source/templates/' + mode + '/js-tag.html'; } }; }]) .directive("ngBlur", ['$parse', function($parse){ return { restrict: 'A', link: function(scope, elem, attrs){ var functionToCall = $parse(attrs.ngBlur); elem.bind('blur', function(event){ scope.$apply(function(){ functionToCall(scope, {$event: event}); }); }) } }; }]) .directive("focusMe", ['$parse', '$timeout', function($parse, $timeout){ return { restrict: 'A', link: function(scope, element, attrs){ var model = $parse(attrs.focusMe); scope.$watch(model, function(value){ if(value === true){ $timeout(function(){ element[0].focus(); }); } }); element.bind('blur', function(){ scope.$apply(model.assign(scope, false)); }); } }; }]) .directive("focusOnce", ['$timeout', function($timeout){ return { restrict: 'A', link: function(scope, element, attrs){ $timeout(function(){ element[0].select(); }); } }; }]) .directive("autoGrow", ['$timeout', function($timeout){ return { link: function(scope, element, attrs){ var paddingLeft = element.css('paddingLeft'), paddingRight = element.css('paddingRight'); var minWidth = 60; var $shadow = angular.element('<span></span>').css({ 'position': 'absolute', 'top': '-10000px', 'left': '-10000px', 'fontSize': element.css('fontSize'), 'fontFamily': element.css('fontFamily'), 'white-space': 'pre' }); element.after($shadow); function update(){ var val = element.val() .replace(/</g, '<') .replace(/>/g, '>') .replace(/&/g, '&'); if(val !== ""){ $shadow.html(val); }else{ $shadow.html(element[0].placeholder); } var newWidth = ($shadow[0].offsetWidth + 10) + "px"; element.css('width', newWidth); } var ngModel = element.attr('ng-model'); if(ngModel){ scope.$watch(ngModel, update); }else{ element.bind('keyup keydown', update); } $timeout(update); } } }]) .directive("jsTagTypeahead", function(){//预先输入 return { restrict: 'A', require: '?ngModel', link: function(scope, element, attrs, ngModel){ element.bind("jsTag:breakcodeHit", function(event){ if(scope.$eval(attrs.options).contentEditable === false){return;} $(event.currentTarget).typeahead('val', ''); }) } }; });
function getNumberOfProperties(obj) { return Object.keys(obj).length;
}
function getFirstProperty(obj) { var keys = Object.keys(obj); return obj[keys[0]];
}
function getLastProperty(obj) { var keys = Object.keys(obj); return obj[keys[keys.length - 1]];
}
function getNextProperty(obj, propertyId) { var keys = Object.keys(obj); var indexOfProperty = keys.indexOf(propertyId.toString()); var keyOfNextProperty = keys[indexOfProperty + 1]; return obj[keyOfNextProperty];
}
function getPreviousProperty(obj, propertyId) { var keys = Object.keys(obj); var indexOfProperty = keys.indexOf(propertyId.toString()); var keyOfPreviousProperty = keys[indexOfProperty - 1]; return obj[keyOfPreviousProperty];
}
//----------------------------------------------------
//--- 测试代码
//----------------------------------------------------
angular.module("demoJSTag", ['siyfion.sfTypeahead', 'jsTag']);
angular.module("demoJSTag").controller("MoreControlController", MoreControlController);
function MoreControlController($scope, JSTagsCollection){ $scope.tags = new JSTagsCollection(["jsTag", "angularJS"]); $scope.jsTagOptions = { "tags": $scope.tags };
}
MoreControlController.$jection = ['$scope', 'JSTagsCollection'];
/**
angular.module("demoJSTag").controller("CustomizedController", CustomizedController);
angular.module("demoJSTag").controller("NoneditableController", NoneditableController);
angular.module("demoJSTag").controller("TypeaheadController", TypeaheadController);
**/
angular.bootstrap(document, ['demoJSTag']);
Developer | Kevin |
Username | chenming142 |
Uploaded | August 28, 2022 |
Rating | 3 |
Size | 7,050 Kb |
Views | 30,360 |
Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!
Name | Size |
Submitability | 2,105 Kb |
NgTouchSpin | 3,084 Kb |
NgModel validation Unit | 2,421 Kb |
A Pen by Kevin | 10,365 Kb |
Vue Transition | 4,561 Kb |
Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!
Name | Username | Size |
Filter inputs | Rowinf | 1,721 Kb |
JQuery AJAX reddit Exercise | Btholt | 1,777 Kb |
Ocean | Gordonnl | 2,817 Kb |
Price table | Serluk | 5,928 Kb |
Personal Logo Animation | Lloydwheeler | 3,795 Kb |
Clock with full screen background | Owebboy | 2,415 Kb |
Count checked checkboxes with jQuery | Mestika | 2,343 Kb |
LDE old privacy page | Jasonangle | 2,339 Kb |
Simple checkbox style | Vncnz | 2,628 Kb |
Multiple jCarousel | Pafnuty | 2,461 Kb |
Surf anonymously, prevent hackers from acquiring your IP address, send anonymous email, and encrypt your Internet connection. High speed, ultra secure, and easy to use. Instant setup. Hide Your IP Now!