Created
April 30, 2014 23:16
-
-
Save vgdub/44d167acd178464247e7 to your computer and use it in GitHub Desktop.
textcomplete angular
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict";angular.module("ngTextcomplete",[]).factory("utils",[function(){function lock(func){var free,locked;free=function(){locked=false};return function(){var args;if(locked)return;locked=true;args=toArray(arguments);args.unshift(free);func.apply(this,args)}}function toArray(args){return Array.prototype.slice.call(args)}function bind(func,context){return func.bind?func.bind(context):function(){func.apply(context,arguments)}}var getStyles=function(){var color;color=$("<div></div>").css(["color"]).color;if(typeof color!=="undefined"){return function($el,properties){return $el.css(properties)}}else{return function($el,properties){var styles;styles={};angular.forEach(properties,function(property,i){styles[property]=$el.css(property)});return styles}}}();function memoize(func){var memo={};return function(term,callback){if(memo[term]){callback(memo[term])}else{func.call(this,term,function(data){memo[term]=(memo[term]||[]).concat(data);callback.apply(null,arguments)})}}}function include(array,value){var i,l;if(array.indexOf)return array.indexOf(value)!=-1;for(i=0,l=array.length;i<l;i++){if(array[i]===value)return true}return false}return{lock:lock,toArray:toArray,bind:bind,getStyles:getStyles,memoize:memoize,include:include}}]).factory("Completer",["ListView","utils","$rootScope",function(ListView,utils,$rootScope){var html,css,$baseWrapper,$baseList;html={wrapper:'<div class="textcomplete-wrapper"></div>',list:'<ul class="dropdown-menu"></ul>'};css={wrapper:{position:"relative"},list:{position:"absolute",top:0,left:0,zIndex:"100",display:"none"}};$baseWrapper=$(html.wrapper).css(css.wrapper);$baseList=$(html.list).css(css.list);function Completer($el,strategies){var $wrapper,$list,focused;$list=$baseList.clone();this.el=$el.get(0);this.$el=$el;$wrapper=_prepareWrapper(this.$el);focused=this.el===document.activeElement;this.$el.wrap($wrapper).before($list);if(focused){this.el.focus()}this.listView=new ListView($list,this);this.strategies=strategies;this.$el.on("keyup",utils.bind(this.onKeyup,this));this.$el.on("keydown",utils.bind(this.listView.onKeydown,this.listView));$(document).on("click",utils.bind(function(e){if(e.originalEvent&&!e.originalEvent.keepTextCompleteDropdown){this.listView.deactivate()}},this))}angular.extend(Completer.prototype,{renderList:function(data){if(this.clearAtNext){this.listView.clear();this.clearAtNext=false}if(data.length){if(!this.listView.shown){this.listView.setPosition(this.getCaretPosition()).clear().activate();this.listView.strategy=this.strategy}data=data.slice(0,this.strategy.maxCount);this.listView.render(data)}if(!this.listView.data.length&&this.listView.shown){this.listView.deactivate()}},searchCallbackFactory:function(free){var self=this;return function(data,keep){self.renderList(data);if(!keep){free();self.clearAtNext=true}}},onKeyup:function(e){var searchQuery,term;searchQuery=this.extractSearchQuery(this.getTextFromHeadToCaret());if(searchQuery.length){term=searchQuery[1];if(this.term===term)return;this.term=term;this.search(searchQuery)}else{this.term=null;this.listView.deactivate()}},onSelect:function(value,cb){var pre,post,newSubStr;pre=this.getTextFromHeadToCaret();post=this.el.value.substring(this.el.selectionEnd);newSubStr=this.strategy.replace(value);if(angular.isArray(newSubStr)){post=newSubStr[1]+post;newSubStr=newSubStr[0]}pre=pre.replace(this.strategy.match,newSubStr);this.$el.val(pre+post);$rootScope.$broadcast("onSelect",this.$el.val());$rootScope.$apply();this.el.focus();this.el.selectionStart=this.el.selectionEnd=pre.length},getCaretPosition:function(){if(this.el.selectionEnd===0)return;var properties,css,$div,$span,position;properties=["border-width","font-family","font-size","font-style","font-variant","font-weight","height","letter-spacing","word-spacing","line-height","text-decoration","width","padding-top","padding-right","padding-bottom","padding-left","margin-top","margin-right","margin-bottom","margin-left"];css=angular.extend({position:"absolute",overflow:"auto","white-space":"pre-wrap",top:0,left:-9999},utils.getStyles(this.$el,properties));$div=$("<div></div>").css(css).text(this.getTextFromHeadToCaret());$span=$("<span></span>").text(" ").appendTo($div);this.$el.before($div);position=$span.position();position.top+=$span.height()-this.$el.scrollTop();$div.remove();return position},getTextFromHeadToCaret:function(){var text,selectionEnd,range;selectionEnd=this.el.selectionEnd;if(typeof selectionEnd==="number"){text=this.el.value.substring(0,selectionEnd)}else if(document.selection){range=this.el.createTextRange();range.moveStart("character",0);range.moveEnd("textedit");text=range.text}return text},extractSearchQuery:function(text){var name,strategy,match;for(name in this.strategies)if(this.strategies.hasOwnProperty(name)){strategy=this.strategies[name];match=text.match(strategy.match);if(match){return[strategy,match[strategy.index]]}}return[]},search:utils.lock(function(free,searchQuery){var term,strategy;this.strategy=searchQuery[0];term=searchQuery[1];this.strategy.search(term,this.searchCallbackFactory(free))})});function _prepareWrapper($el){return $baseWrapper.css("display",$el.css("display"))}return Completer}]).factory("ListView",["utils",function(utils){function ListView($el,completer){this.$el=$el;this.index=0;this.completer=completer;this.$el.on("click","li.textcomplete-item",utils.bind(this.onClick,this))}angular.extend(ListView.prototype,{shown:false,render:function(data){var html,i,l,index,val;html="";for(i=0,l=data.length;i<l;i++){val=data[i];if(utils.include(this.data,val))continue;index=this.data.length;this.data.push(val);html+='<li class="textcomplete-item" data-index="'+index+'"><a>';html+=this.strategy.template(val);html+="</a></li>";if(this.data.length===this.strategy.maxCount)break}this.$el.append(html);if(!this.data.length){this.deactivate()}else{this.activateIndexedItem()}},clear:function(){this.data=[];this.$el.html("");this.index=0;return this},activateIndexedItem:function(){var $item;this.$el.find(".active").removeClass("active");this.getActiveItem().addClass("active")},getActiveItem:function(){return $(this.$el.children().get(this.index))},activate:function(){if(!this.shown){this.$el.show();this.shown=true}return this},deactivate:function(){if(this.shown){this.$el.hide();this.shown=false;this.data=this.index=null}return this},setPosition:function(position){this.$el.css(position);return this},select:function(index){this.completer.onSelect(this.data[index]);this.deactivate()},onKeydown:function(e){var $item;if(!this.shown)return;if(e.keyCode===27){this.deactivate()}else if(e.keyCode===38){e.preventDefault();if(this.index===0){this.index=this.data.length-1}else{this.index-=1}this.activateIndexedItem()}else if(e.keyCode===40){e.preventDefault();if(this.index===this.data.length-1){this.index=0}else{this.index+=1}this.activateIndexedItem()}else if(e.keyCode===13||e.keyCode===9){e.preventDefault();this.select(parseInt(this.getActiveItem().data("index")))}},onClick:function(e){var $e=$(e.target);e.originalEvent.keepTextCompleteDropdown=true;if(!$e.hasClass("textcomplete-item")){$e=$e.parents("li.textcomplete-item")}this.select(parseInt($e.data("index")))}});return ListView}]).factory("Textcomplete",["utils","Completer",function(utils,Completer){function identity(obj){return obj}function Textcomplete(ta,strategies){var name,strategy;for(name in strategies){if(strategies.hasOwnProperty(name)){strategy=strategies[name];if(!strategy.template){strategy.template=identity}if(strategy.index==null){strategy.index=2}if(strategy.cache){strategy.search=utils.memoize(strategy.search)}strategy.maxCount=strategy.maxCount||10}}return new Completer(ta,strategies)}return Textcomplete}]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment