diff --git a/.gitignore b/.gitignore index 75aa3c26..9eded787 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,14 @@ # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile ~/.gitignore_global +#assety stuff realtime/node_modules +public/assets + +#secrets config/database.yml +config/secrets.yml .env -#public/assets # Ignore bundler config .bundle @@ -20,5 +24,4 @@ log/*.log tmp .DS_Store - .vagrant diff --git a/Gemfile b/Gemfile index 4732f0a8..80ac4f64 100644 --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,12 @@ source 'https://rubygems.org' ruby '2.1.3' -gem 'rails', '3.2.17' - -# Bundle edge Rails instead: -# gem 'rails', :git => 'git://github.com/rails/rails.git' +gem 'rails', '4.2.4' gem 'devise' gem 'redis' gem 'pg' -gem 'cancan' +gem 'cancancan' gem 'formula' gem 'formtastic' gem 'json' @@ -22,6 +19,10 @@ gem 'dotenv' gem 'paperclip' gem 'aws-sdk' +gem 'jquery-rails' +gem 'jquery-ui-rails' +gem 'jbuilder' + #gem 'therubyracer' #optional #gem 'rb-readline' @@ -29,12 +30,12 @@ gem 'aws-sdk' # in production environments by default. group :assets do gem 'sass-rails' - gem 'coffee-rails', '~> 3.2.1' + gem 'coffee-rails' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer' - gem 'uglifier', '>= 1.0.3' + gem 'uglifier' end group :test do @@ -44,22 +45,13 @@ end group :production do #this is used on heroku #gem 'rmagick' + gem 'rails_12factor' end -gem 'jquery-rails', '2.1.2' - -# To use ActiveModel has_secure_password -# gem 'bcrypt-ruby', '~> 3.0.0' - -# To use Jbuilder templates for JSON - gem 'jbuilder', '0.8.2' - -# Use unicorn as the web server -# gem 'unicorn' - -# Deploy with Capistrano -# gem 'capistrano' - -# To use debugger -# gem 'ruby-debug19', :require => 'ruby-debug' - +group :development, :test do + gem 'pry-rails' + gem 'pry-byebug' + gem 'better_errors' + gem 'binding_of_caller' + gem 'quiet_assets' +end diff --git a/app/assets/javascripts/lib/jquery-ui-1.8.23.custom.min.js b/app/assets/javascripts/lib/jquery-ui-1.8.23.custom.min.js deleted file mode 100644 index 564759cd..00000000 --- a/app/assets/javascripts/lib/jquery-ui-1.8.23.custom.min.js +++ /dev/null @@ -1,25 +0,0 @@ -/*! jQuery UI - v1.8.23 - 2012-08-15 -* https://github.com/jquery/jquery-ui -* Includes: jquery.ui.core.js -* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;return!b.href||!g||f.nodeName.toLowerCase()!=="map"?!1:(h=a("img[usemap=#"+g+"]")[0],!!h&&d(h))}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.ui=a.ui||{};if(a.ui.version)return;a.extend(a.ui,{version:"1.8.23",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;return a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0),/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a("").outerWidth(1).jquery||a.each(["Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?g["inner"+d].call(this):this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return typeof b!="number"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:a.expr.createPseudo?a.expr.createPseudo(function(b){return function(c){return!!a.data(c,b)}}):function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.curCSS||(a.curCSS=a.css),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!d||!a.element[0].parentNode)return;for(var e=0;e0?!0:(b[d]=1,e=b[d]>0,b[d]=0,e)},isOverAxis:function(a,b,c){return a>b&&a=9||!!b.button?this._mouseStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted):this._mouseUp(b)},_mouseUp:function(b){return a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b)),!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 -* https://github.com/jquery/jquery-ui -* Includes: jquery.ui.position.js -* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;return i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1],this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]===e)return;var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0},top:function(b,c){if(c.at[1]===e)return;var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];return!c||!c.ownerDocument?null:b?a.isFunction(b)?this.each(function(c){a(this).offset(b.call(this,c,a(this).offset()))}):this.each(function(){a.offset.setOffset(this,b)}):h.call(this)}),a.curCSS||(a.curCSS=a.css),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 -* https://github.com/jquery/jquery-ui -* Includes: jquery.ui.draggable.js -* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!this.element.data("draggable"))return;return this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy(),this},_mouseCapture:function(b){var c=this.options;return this.helper||c.disabled||a(b.target).is(".ui-resizable-handle")?!1:(this.handle=this._getHandle(b),this.handle?(c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(b){var c=this.options;return this.helper=this._createHelper(b),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment(),this._trigger("start",b)===!1?(this._clear(),!1):(this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b),!0)},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1)return this._mouseUp({}),!1;this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";return a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);var d=this.element[0],e=!1;while(d&&(d=d.parentNode))d==document&&(e=!0);if(!e&&this.options.helper==="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var f=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){f._trigger("stop",b)!==!1&&f._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){return this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b),a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;return a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)}),c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;return d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute"),d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.lefth[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.toph[3]?j-this.offset.click.toph[2]?k-this.offset.click.left=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;try{e.id}catch(f){e=document.body}return b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;return b.parent().is(".ui-effects-wrapper")?(c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus(),c):b},setTransition:function(b,c,d,e){return e=e||{},a.each(c,function(a,c){var f=b.cssUnit(c);f[0]>0&&(e[c]=f[0]*d+f[1])}),e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];return a.fx.off||!i?h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)}):i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);return b[1].mode="show",this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);return b[1].mode="hide",this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);return c[1].mode="toggle",this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];return a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])}),d}});var m={};a.each(["Quad","Cubic","Quart","Quint","Expo"],function(a,b){m[b]=function(b){return Math.pow(b,a+2)}}),a.extend(m,{Sine:function(a){return 1-Math.cos(a*Math.PI/2)},Circ:function(a){return 1-Math.sqrt(1-a*a)},Elastic:function(a){return a===0||a===1?a:-Math.pow(2,8*(a-1))*Math.sin(((a-1)*80-7.5)*Math.PI/15)},Back:function(a){return a*a*(3*a-2)},Bounce:function(a){var b,c=4;while(a<((b=Math.pow(2,--c))-1)/11);return 1/Math.pow(4,3-c)-7.5625*Math.pow((b*3-2)/22-a,2)}}),a.each(m,function(b,c){a.easing["easeIn"+b]=c,a.easing["easeOut"+b]=function(a){return 1-c(1-a)},a.easing["easeInOut"+b]=function(a){return a<.5?c(a*2)/2:c(a*-2+2)/-2+1}})}(jQuery);; \ No newline at end of file diff --git a/app/assets/javascripts/lib/typeahead.bundle.js b/app/assets/javascripts/lib/typeahead.bundle.js new file mode 100644 index 00000000..bb0c8aed --- /dev/null +++ b/app/assets/javascripts/lib/typeahead.bundle.js @@ -0,0 +1,2451 @@ +/*! + * typeahead.js 0.11.1 + * https://github.com/twitter/typeahead.js + * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT + */ + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define("bloodhound", [ "jquery" ], function(a0) { + return root["Bloodhound"] = factory(a0); + }); + } else if (typeof exports === "object") { + module.exports = factory(require("jquery")); + } else { + root["Bloodhound"] = factory(jQuery); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + noop: function() {} + }; + }(); + var VERSION = "0.11.1"; + var tokenizers = function() { + "use strict"; + return { + nonword: nonword, + whitespace: whitespace, + obj: { + nonword: getObjTokenizer(nonword), + whitespace: getObjTokenizer(whitespace) + } + }; + function whitespace(str) { + str = _.toStr(str); + return str ? str.split(/\s+/) : []; + } + function nonword(str) { + str = _.toStr(str); + return str ? str.split(/\W+/) : []; + } + function getObjTokenizer(tokenizer) { + return function setKey(keys) { + keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0); + return function tokenize(o) { + var tokens = []; + _.each(keys, function(k) { + tokens = tokens.concat(tokenizer(_.toStr(o[k]))); + }); + return tokens; + }; + }; + } + }(); + var LruCache = function() { + "use strict"; + function LruCache(maxSize) { + this.maxSize = _.isNumber(maxSize) ? maxSize : 100; + this.reset(); + if (this.maxSize <= 0) { + this.set = this.get = $.noop; + } + } + _.mixin(LruCache.prototype, { + set: function set(key, val) { + var tailItem = this.list.tail, node; + if (this.size >= this.maxSize) { + this.list.remove(tailItem); + delete this.hash[tailItem.key]; + this.size--; + } + if (node = this.hash[key]) { + node.val = val; + this.list.moveToFront(node); + } else { + node = new Node(key, val); + this.list.add(node); + this.hash[key] = node; + this.size++; + } + }, + get: function get(key) { + var node = this.hash[key]; + if (node) { + this.list.moveToFront(node); + return node.val; + } + }, + reset: function reset() { + this.size = 0; + this.hash = {}; + this.list = new List(); + } + }); + function List() { + this.head = this.tail = null; + } + _.mixin(List.prototype, { + add: function add(node) { + if (this.head) { + node.next = this.head; + this.head.prev = node; + } + this.head = node; + this.tail = this.tail || node; + }, + remove: function remove(node) { + node.prev ? node.prev.next = node.next : this.head = node.next; + node.next ? node.next.prev = node.prev : this.tail = node.prev; + }, + moveToFront: function(node) { + this.remove(node); + this.add(node); + } + }); + function Node(key, val) { + this.key = key; + this.val = val; + this.prev = this.next = null; + } + return LruCache; + }(); + var PersistentStorage = function() { + "use strict"; + var LOCAL_STORAGE; + try { + LOCAL_STORAGE = window.localStorage; + LOCAL_STORAGE.setItem("~~~", "!"); + LOCAL_STORAGE.removeItem("~~~"); + } catch (err) { + LOCAL_STORAGE = null; + } + function PersistentStorage(namespace, override) { + this.prefix = [ "__", namespace, "__" ].join(""); + this.ttlKey = "__ttl__"; + this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix)); + this.ls = override || LOCAL_STORAGE; + !this.ls && this._noop(); + } + _.mixin(PersistentStorage.prototype, { + _prefix: function(key) { + return this.prefix + key; + }, + _ttlKey: function(key) { + return this._prefix(key) + this.ttlKey; + }, + _noop: function() { + this.get = this.set = this.remove = this.clear = this.isExpired = _.noop; + }, + _safeSet: function(key, val) { + try { + this.ls.setItem(key, val); + } catch (err) { + if (err.name === "QuotaExceededError") { + this.clear(); + this._noop(); + } + } + }, + get: function(key) { + if (this.isExpired(key)) { + this.remove(key); + } + return decode(this.ls.getItem(this._prefix(key))); + }, + set: function(key, val, ttl) { + if (_.isNumber(ttl)) { + this._safeSet(this._ttlKey(key), encode(now() + ttl)); + } else { + this.ls.removeItem(this._ttlKey(key)); + } + return this._safeSet(this._prefix(key), encode(val)); + }, + remove: function(key) { + this.ls.removeItem(this._ttlKey(key)); + this.ls.removeItem(this._prefix(key)); + return this; + }, + clear: function() { + var i, keys = gatherMatchingKeys(this.keyMatcher); + for (i = keys.length; i--; ) { + this.remove(keys[i]); + } + return this; + }, + isExpired: function(key) { + var ttl = decode(this.ls.getItem(this._ttlKey(key))); + return _.isNumber(ttl) && now() > ttl ? true : false; + } + }); + return PersistentStorage; + function now() { + return new Date().getTime(); + } + function encode(val) { + return JSON.stringify(_.isUndefined(val) ? null : val); + } + function decode(val) { + return $.parseJSON(val); + } + function gatherMatchingKeys(keyMatcher) { + var i, key, keys = [], len = LOCAL_STORAGE.length; + for (i = 0; i < len; i++) { + if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) { + keys.push(key.replace(keyMatcher, "")); + } + } + return keys; + } + }(); + var Transport = function() { + "use strict"; + var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10); + function Transport(o) { + o = o || {}; + this.cancelled = false; + this.lastReq = null; + this._send = o.transport; + this._get = o.limiter ? o.limiter(this._get) : this._get; + this._cache = o.cache === false ? new LruCache(0) : sharedCache; + } + Transport.setMaxPendingRequests = function setMaxPendingRequests(num) { + maxPendingRequests = num; + }; + Transport.resetCache = function resetCache() { + sharedCache.reset(); + }; + _.mixin(Transport.prototype, { + _fingerprint: function fingerprint(o) { + o = o || {}; + return o.url + o.type + $.param(o.data || {}); + }, + _get: function(o, cb) { + var that = this, fingerprint, jqXhr; + fingerprint = this._fingerprint(o); + if (this.cancelled || fingerprint !== this.lastReq) { + return; + } + if (jqXhr = pendingRequests[fingerprint]) { + jqXhr.done(done).fail(fail); + } else if (pendingRequestsCount < maxPendingRequests) { + pendingRequestsCount++; + pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always); + } else { + this.onDeckRequestArgs = [].slice.call(arguments, 0); + } + function done(resp) { + cb(null, resp); + that._cache.set(fingerprint, resp); + } + function fail() { + cb(true); + } + function always() { + pendingRequestsCount--; + delete pendingRequests[fingerprint]; + if (that.onDeckRequestArgs) { + that._get.apply(that, that.onDeckRequestArgs); + that.onDeckRequestArgs = null; + } + } + }, + get: function(o, cb) { + var resp, fingerprint; + cb = cb || $.noop; + o = _.isString(o) ? { + url: o + } : o || {}; + fingerprint = this._fingerprint(o); + this.cancelled = false; + this.lastReq = fingerprint; + if (resp = this._cache.get(fingerprint)) { + cb(null, resp); + } else { + this._get(o, cb); + } + }, + cancel: function() { + this.cancelled = true; + } + }); + return Transport; + }(); + var SearchIndex = window.SearchIndex = function() { + "use strict"; + var CHILDREN = "c", IDS = "i"; + function SearchIndex(o) { + o = o || {}; + if (!o.datumTokenizer || !o.queryTokenizer) { + $.error("datumTokenizer and queryTokenizer are both required"); + } + this.identify = o.identify || _.stringify; + this.datumTokenizer = o.datumTokenizer; + this.queryTokenizer = o.queryTokenizer; + this.reset(); + } + _.mixin(SearchIndex.prototype, { + bootstrap: function bootstrap(o) { + this.datums = o.datums; + this.trie = o.trie; + }, + add: function(data) { + var that = this; + data = _.isArray(data) ? data : [ data ]; + _.each(data, function(datum) { + var id, tokens; + that.datums[id = that.identify(datum)] = datum; + tokens = normalizeTokens(that.datumTokenizer(datum)); + _.each(tokens, function(token) { + var node, chars, ch; + node = that.trie; + chars = token.split(""); + while (ch = chars.shift()) { + node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode()); + node[IDS].push(id); + } + }); + }); + }, + get: function get(ids) { + var that = this; + return _.map(ids, function(id) { + return that.datums[id]; + }); + }, + search: function search(query) { + var that = this, tokens, matches; + tokens = normalizeTokens(this.queryTokenizer(query)); + _.each(tokens, function(token) { + var node, chars, ch, ids; + if (matches && matches.length === 0) { + return false; + } + node = that.trie; + chars = token.split(""); + while (node && (ch = chars.shift())) { + node = node[CHILDREN][ch]; + } + if (node && chars.length === 0) { + ids = node[IDS].slice(0); + matches = matches ? getIntersection(matches, ids) : ids; + } else { + matches = []; + return false; + } + }); + return matches ? _.map(unique(matches), function(id) { + return that.datums[id]; + }) : []; + }, + all: function all() { + var values = []; + for (var key in this.datums) { + values.push(this.datums[key]); + } + return values; + }, + reset: function reset() { + this.datums = {}; + this.trie = newNode(); + }, + serialize: function serialize() { + return { + datums: this.datums, + trie: this.trie + }; + } + }); + return SearchIndex; + function normalizeTokens(tokens) { + tokens = _.filter(tokens, function(token) { + return !!token; + }); + tokens = _.map(tokens, function(token) { + return token.toLowerCase(); + }); + return tokens; + } + function newNode() { + var node = {}; + node[IDS] = []; + node[CHILDREN] = {}; + return node; + } + function unique(array) { + var seen = {}, uniques = []; + for (var i = 0, len = array.length; i < len; i++) { + if (!seen[array[i]]) { + seen[array[i]] = true; + uniques.push(array[i]); + } + } + return uniques; + } + function getIntersection(arrayA, arrayB) { + var ai = 0, bi = 0, intersection = []; + arrayA = arrayA.sort(); + arrayB = arrayB.sort(); + var lenArrayA = arrayA.length, lenArrayB = arrayB.length; + while (ai < lenArrayA && bi < lenArrayB) { + if (arrayA[ai] < arrayB[bi]) { + ai++; + } else if (arrayA[ai] > arrayB[bi]) { + bi++; + } else { + intersection.push(arrayA[ai]); + ai++; + bi++; + } + } + return intersection; + } + }(); + var Prefetch = function() { + "use strict"; + var keys; + keys = { + data: "data", + protocol: "protocol", + thumbprint: "thumbprint" + }; + function Prefetch(o) { + this.url = o.url; + this.ttl = o.ttl; + this.cache = o.cache; + this.prepare = o.prepare; + this.transform = o.transform; + this.transport = o.transport; + this.thumbprint = o.thumbprint; + this.storage = new PersistentStorage(o.cacheKey); + } + _.mixin(Prefetch.prototype, { + _settings: function settings() { + return { + url: this.url, + type: "GET", + dataType: "json" + }; + }, + store: function store(data) { + if (!this.cache) { + return; + } + this.storage.set(keys.data, data, this.ttl); + this.storage.set(keys.protocol, location.protocol, this.ttl); + this.storage.set(keys.thumbprint, this.thumbprint, this.ttl); + }, + fromCache: function fromCache() { + var stored = {}, isExpired; + if (!this.cache) { + return null; + } + stored.data = this.storage.get(keys.data); + stored.protocol = this.storage.get(keys.protocol); + stored.thumbprint = this.storage.get(keys.thumbprint); + isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol; + return stored.data && !isExpired ? stored.data : null; + }, + fromNetwork: function(cb) { + var that = this, settings; + if (!cb) { + return; + } + settings = this.prepare(this._settings()); + this.transport(settings).fail(onError).done(onResponse); + function onError() { + cb(true); + } + function onResponse(resp) { + cb(null, that.transform(resp)); + } + }, + clear: function clear() { + this.storage.clear(); + return this; + } + }); + return Prefetch; + }(); + var Remote = function() { + "use strict"; + function Remote(o) { + this.url = o.url; + this.prepare = o.prepare; + this.transform = o.transform; + this.transport = new Transport({ + cache: o.cache, + limiter: o.limiter, + transport: o.transport + }); + } + _.mixin(Remote.prototype, { + _settings: function settings() { + return { + url: this.url, + type: "GET", + dataType: "json" + }; + }, + get: function get(query, cb) { + var that = this, settings; + if (!cb) { + return; + } + query = query || ""; + settings = this.prepare(query, this._settings()); + return this.transport.get(settings, onResponse); + function onResponse(err, resp) { + err ? cb([]) : cb(that.transform(resp)); + } + }, + cancelLastRequest: function cancelLastRequest() { + this.transport.cancel(); + } + }); + return Remote; + }(); + var oParser = function() { + "use strict"; + return function parse(o) { + var defaults, sorter; + defaults = { + initialize: true, + identify: _.stringify, + datumTokenizer: null, + queryTokenizer: null, + sufficient: 5, + sorter: null, + local: [], + prefetch: null, + remote: null + }; + o = _.mixin(defaults, o || {}); + !o.datumTokenizer && $.error("datumTokenizer is required"); + !o.queryTokenizer && $.error("queryTokenizer is required"); + sorter = o.sorter; + o.sorter = sorter ? function(x) { + return x.sort(sorter); + } : _.identity; + o.local = _.isFunction(o.local) ? o.local() : o.local; + o.prefetch = parsePrefetch(o.prefetch); + o.remote = parseRemote(o.remote); + return o; + }; + function parsePrefetch(o) { + var defaults; + if (!o) { + return null; + } + defaults = { + url: null, + ttl: 24 * 60 * 60 * 1e3, + cache: true, + cacheKey: null, + thumbprint: "", + prepare: _.identity, + transform: _.identity, + transport: null + }; + o = _.isString(o) ? { + url: o + } : o; + o = _.mixin(defaults, o); + !o.url && $.error("prefetch requires url to be set"); + o.transform = o.filter || o.transform; + o.cacheKey = o.cacheKey || o.url; + o.thumbprint = VERSION + o.thumbprint; + o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax; + return o; + } + function parseRemote(o) { + var defaults; + if (!o) { + return; + } + defaults = { + url: null, + cache: true, + prepare: null, + replace: null, + wildcard: null, + limiter: null, + rateLimitBy: "debounce", + rateLimitWait: 300, + transform: _.identity, + transport: null + }; + o = _.isString(o) ? { + url: o + } : o; + o = _.mixin(defaults, o); + !o.url && $.error("remote requires url to be set"); + o.transform = o.filter || o.transform; + o.prepare = toRemotePrepare(o); + o.limiter = toLimiter(o); + o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax; + delete o.replace; + delete o.wildcard; + delete o.rateLimitBy; + delete o.rateLimitWait; + return o; + } + function toRemotePrepare(o) { + var prepare, replace, wildcard; + prepare = o.prepare; + replace = o.replace; + wildcard = o.wildcard; + if (prepare) { + return prepare; + } + if (replace) { + prepare = prepareByReplace; + } else if (o.wildcard) { + prepare = prepareByWildcard; + } else { + prepare = idenityPrepare; + } + return prepare; + function prepareByReplace(query, settings) { + settings.url = replace(settings.url, query); + return settings; + } + function prepareByWildcard(query, settings) { + settings.url = settings.url.replace(wildcard, encodeURIComponent(query)); + return settings; + } + function idenityPrepare(query, settings) { + return settings; + } + } + function toLimiter(o) { + var limiter, method, wait; + limiter = o.limiter; + method = o.rateLimitBy; + wait = o.rateLimitWait; + if (!limiter) { + limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait); + } + return limiter; + function debounce(wait) { + return function debounce(fn) { + return _.debounce(fn, wait); + }; + } + function throttle(wait) { + return function throttle(fn) { + return _.throttle(fn, wait); + }; + } + } + function callbackToDeferred(fn) { + return function wrapper(o) { + var deferred = $.Deferred(); + fn(o, onSuccess, onError); + return deferred; + function onSuccess(resp) { + _.defer(function() { + deferred.resolve(resp); + }); + } + function onError(err) { + _.defer(function() { + deferred.reject(err); + }); + } + }; + } + }(); + var Bloodhound = function() { + "use strict"; + var old; + old = window && window.Bloodhound; + function Bloodhound(o) { + o = oParser(o); + this.sorter = o.sorter; + this.identify = o.identify; + this.sufficient = o.sufficient; + this.local = o.local; + this.remote = o.remote ? new Remote(o.remote) : null; + this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null; + this.index = new SearchIndex({ + identify: this.identify, + datumTokenizer: o.datumTokenizer, + queryTokenizer: o.queryTokenizer + }); + o.initialize !== false && this.initialize(); + } + Bloodhound.noConflict = function noConflict() { + window && (window.Bloodhound = old); + return Bloodhound; + }; + Bloodhound.tokenizers = tokenizers; + _.mixin(Bloodhound.prototype, { + __ttAdapter: function ttAdapter() { + var that = this; + return this.remote ? withAsync : withoutAsync; + function withAsync(query, sync, async) { + return that.search(query, sync, async); + } + function withoutAsync(query, sync) { + return that.search(query, sync); + } + }, + _loadPrefetch: function loadPrefetch() { + var that = this, deferred, serialized; + deferred = $.Deferred(); + if (!this.prefetch) { + deferred.resolve(); + } else if (serialized = this.prefetch.fromCache()) { + this.index.bootstrap(serialized); + deferred.resolve(); + } else { + this.prefetch.fromNetwork(done); + } + return deferred.promise(); + function done(err, data) { + if (err) { + return deferred.reject(); + } + that.add(data); + that.prefetch.store(that.index.serialize()); + deferred.resolve(); + } + }, + _initialize: function initialize() { + var that = this, deferred; + this.clear(); + (this.initPromise = this._loadPrefetch()).done(addLocalToIndex); + return this.initPromise; + function addLocalToIndex() { + that.add(that.local); + } + }, + initialize: function initialize(force) { + return !this.initPromise || force ? this._initialize() : this.initPromise; + }, + add: function add(data) { + this.index.add(data); + return this; + }, + get: function get(ids) { + ids = _.isArray(ids) ? ids : [].slice.call(arguments); + return this.index.get(ids); + }, + search: function search(query, sync, async) { + var that = this, local; + local = this.sorter(this.index.search(query)); + sync(this.remote ? local.slice() : local); + if (this.remote && local.length < this.sufficient) { + this.remote.get(query, processRemote); + } else if (this.remote) { + this.remote.cancelLastRequest(); + } + return this; + function processRemote(remote) { + var nonDuplicates = []; + _.each(remote, function(r) { + !_.some(local, function(l) { + return that.identify(r) === that.identify(l); + }) && nonDuplicates.push(r); + }); + async && async(nonDuplicates); + } + }, + all: function all() { + return this.index.all(); + }, + clear: function clear() { + this.index.reset(); + return this; + }, + clearPrefetchCache: function clearPrefetchCache() { + this.prefetch && this.prefetch.clear(); + return this; + }, + clearRemoteCache: function clearRemoteCache() { + Transport.resetCache(); + return this; + }, + ttAdapter: function ttAdapter() { + return this.__ttAdapter(); + } + }); + return Bloodhound; + }(); + return Bloodhound; +}); + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define("typeahead.js", [ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof exports === "object") { + module.exports = factory(require("jquery")); + } else { + factory(jQuery); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e; + $e = $.Event(namespace + type); + (args = args || []).unshift($e); + this.$el.trigger.apply(this.$el, args); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function getRegex(patterns, caseSensitive, wordsOnly) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + escapedPatterns.push(_.escapeRegExChars(patterns[i])); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = o.name || nameGenerator(); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", this.name, suggestions, false); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", this.name, suggestions, true); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + rendered += suggestions.length; + that._append(query, suggestions.slice(0, that.limit - rendered)); + that.async && that.trigger("asyncReceived", query); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return $("
").text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, isDatasetEmpty); + function isDatasetEmpty(dataset) { + return dataset.isEmpty(); + } + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { + this._updateHint(); + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, payload, cancelMove; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + payload = data ? data.obj : null; + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { + this.menu.setCursor($candidate); + if (data) { + this.input.setInputValue(data.val); + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", payload); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(newVal); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ + autocomplete: "off", + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + autocomplete: "off", + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/app/assets/javascripts/lib/typeahead.js b/app/assets/javascripts/lib/typeahead.js deleted file mode 100644 index 3a413d68..00000000 --- a/app/assets/javascripts/lib/typeahead.js +++ /dev/null @@ -1,1165 +0,0 @@ -/*! - * typeahead.js 0.9.3 - * https://github.com/twitter/typeahead - * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT - */ - -(function($) { - var VERSION = "0.9.3"; - var utils = { - isMsie: function() { - var match = /(msie) ([\w.]+)/i.exec(navigator.userAgent); - return match ? parseInt(match[2], 10) : false; - }, - isBlankString: function(str) { - return !str || /^\s*$/.test(str); - }, - escapeRegExChars: function(str) { - return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - }, - isString: function(obj) { - return typeof obj === "string"; - }, - isNumber: function(obj) { - return typeof obj === "number"; - }, - isArray: $.isArray, - isFunction: $.isFunction, - isObject: $.isPlainObject, - isUndefined: function(obj) { - return typeof obj === "undefined"; - }, - bind: $.proxy, - bindAll: function(obj) { - var val; - for (var key in obj) { - $.isFunction(val = obj[key]) && (obj[key] = $.proxy(val, obj)); - } - }, - indexOf: function(haystack, needle) { - for (var i = 0; i < haystack.length; i++) { - if (haystack[i] === needle) { - return i; - } - } - return -1; - }, - each: $.each, - map: $.map, - filter: $.grep, - every: function(obj, test) { - var result = true; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (!(result = test.call(null, val, key, obj))) { - return false; - } - }); - return !!result; - }, - some: function(obj, test) { - var result = false; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (result = test.call(null, val, key, obj)) { - return false; - } - }); - return !!result; - }, - mixin: $.extend, - getUniqueId: function() { - var counter = 0; - return function() { - return counter++; - }; - }(), - defer: function(fn) { - setTimeout(fn, 0); - }, - debounce: function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments, later, callNow; - later = function() { - timeout = null; - if (!immediate) { - result = func.apply(context, args); - } - }; - callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) { - result = func.apply(context, args); - } - return result; - }; - }, - throttle: function(func, wait) { - var context, args, timeout, result, previous, later; - previous = 0; - later = function() { - previous = new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date(), remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout) { - timeout = setTimeout(later, remaining); - } - return result; - }; - }, - tokenizeQuery: function(str) { - return $.trim(str).toLowerCase().split(/[\s]+/); - }, - tokenizeText: function(str) { - return $.trim(str).toLowerCase().split(/[\s\-_]+/); - }, - getProtocol: function() { - return location.protocol; - }, - noop: function() {} - }; - var EventTarget = function() { - var eventSplitter = /\s+/; - return { - on: function(events, callback) { - var event; - if (!callback) { - return this; - } - this._callbacks = this._callbacks || {}; - events = events.split(eventSplitter); - while (event = events.shift()) { - this._callbacks[event] = this._callbacks[event] || []; - this._callbacks[event].push(callback); - } - return this; - }, - trigger: function(events, data) { - var event, callbacks; - if (!this._callbacks) { - return this; - } - events = events.split(eventSplitter); - while (event = events.shift()) { - if (callbacks = this._callbacks[event]) { - for (var i = 0; i < callbacks.length; i += 1) { - callbacks[i].call(this, { - type: event, - data: data - }); - } - } - } - return this; - } - }; - }(); - var EventBus = function() { - var namespace = "typeahead:"; - function EventBus(o) { - if (!o || !o.el) { - $.error("EventBus initialized without el"); - } - this.$el = $(o.el); - } - utils.mixin(EventBus.prototype, { - trigger: function(type) { - var args = [].slice.call(arguments, 1); - this.$el.trigger(namespace + type, args); - } - }); - return EventBus; - }(); - var PersistentStorage = function() { - var ls, methods; - try { - ls = window.localStorage; - ls.setItem("~~~", "!"); - ls.removeItem("~~~"); - } catch (err) { - ls = null; - } - function PersistentStorage(namespace) { - this.prefix = [ "__", namespace, "__" ].join(""); - this.ttlKey = "__ttl__"; - this.keyMatcher = new RegExp("^" + this.prefix); - } - if (ls && window.JSON) { - methods = { - _prefix: function(key) { - return this.prefix + key; - }, - _ttlKey: function(key) { - return this._prefix(key) + this.ttlKey; - }, - get: function(key) { - if (this.isExpired(key)) { - this.remove(key); - } - return decode(ls.getItem(this._prefix(key))); - }, - set: function(key, val, ttl) { - if (utils.isNumber(ttl)) { - ls.setItem(this._ttlKey(key), encode(now() + ttl)); - } else { - ls.removeItem(this._ttlKey(key)); - } - return ls.setItem(this._prefix(key), encode(val)); - }, - remove: function(key) { - ls.removeItem(this._ttlKey(key)); - ls.removeItem(this._prefix(key)); - return this; - }, - clear: function() { - var i, key, keys = [], len = ls.length; - for (i = 0; i < len; i++) { - if ((key = ls.key(i)).match(this.keyMatcher)) { - keys.push(key.replace(this.keyMatcher, "")); - } - } - for (i = keys.length; i--; ) { - this.remove(keys[i]); - } - return this; - }, - isExpired: function(key) { - var ttl = decode(ls.getItem(this._ttlKey(key))); - return utils.isNumber(ttl) && now() > ttl ? true : false; - } - }; - } else { - methods = { - get: utils.noop, - set: utils.noop, - remove: utils.noop, - clear: utils.noop, - isExpired: utils.noop - }; - } - utils.mixin(PersistentStorage.prototype, methods); - return PersistentStorage; - function now() { - return new Date().getTime(); - } - function encode(val) { - return JSON.stringify(utils.isUndefined(val) ? null : val); - } - function decode(val) { - return JSON.parse(val); - } - }(); - var RequestCache = function() { - function RequestCache(o) { - utils.bindAll(this); - o = o || {}; - this.sizeLimit = o.sizeLimit || 10; - this.cache = {}; - this.cachedKeysByAge = []; - } - utils.mixin(RequestCache.prototype, { - get: function(url) { - return this.cache[url]; - }, - set: function(url, resp) { - var requestToEvict; - if (this.cachedKeysByAge.length === this.sizeLimit) { - requestToEvict = this.cachedKeysByAge.shift(); - delete this.cache[requestToEvict]; - } - this.cache[url] = resp; - this.cachedKeysByAge.push(url); - } - }); - return RequestCache; - }(); - var Transport = function() { - var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests, requestCache; - function Transport(o) { - utils.bindAll(this); - o = utils.isString(o) ? { - url: o - } : o; - requestCache = requestCache || new RequestCache(); - maxPendingRequests = utils.isNumber(o.maxParallelRequests) ? o.maxParallelRequests : maxPendingRequests || 6; - this.url = o.url; - this.wildcard = o.wildcard || "%QUERY"; - this.filter = o.filter; - this.replace = o.replace; - this.ajaxSettings = { - type: "get", - cache: o.cache, - timeout: o.timeout, - dataType: o.dataType || "json", - beforeSend: o.beforeSend - }; - this._get = (/^throttle$/i.test(o.rateLimitFn) ? utils.throttle : utils.debounce)(this._get, o.rateLimitWait || 300); - } - utils.mixin(Transport.prototype, { - _get: function(url, cb) { - var that = this; - if (belowPendingRequestsThreshold()) { - this._sendRequest(url).done(done); - } else { - this.onDeckRequestArgs = [].slice.call(arguments, 0); - } - function done(resp) { - var data = that.filter ? that.filter(resp) : resp; - cb && cb(data); - requestCache.set(url, resp); - } - }, - _sendRequest: function(url) { - var that = this, jqXhr = pendingRequests[url]; - if (!jqXhr) { - incrementPendingRequests(); - jqXhr = pendingRequests[url] = $.ajax(url, this.ajaxSettings).always(always); - } - return jqXhr; - function always() { - decrementPendingRequests(); - pendingRequests[url] = null; - if (that.onDeckRequestArgs) { - that._get.apply(that, that.onDeckRequestArgs); - that.onDeckRequestArgs = null; - } - } - }, - get: function(query, cb) { - var that = this, encodedQuery = encodeURIComponent(query || ""), url, resp; - cb = cb || utils.noop; - url = this.replace ? this.replace(this.url, encodedQuery) : this.url.replace(this.wildcard, encodedQuery); - if (resp = requestCache.get(url)) { - utils.defer(function() { - cb(that.filter ? that.filter(resp) : resp); - }); - } else { - this._get(url, cb); - } - return !!resp; - } - }); - return Transport; - function incrementPendingRequests() { - pendingRequestsCount++; - } - function decrementPendingRequests() { - pendingRequestsCount--; - } - function belowPendingRequestsThreshold() { - return pendingRequestsCount < maxPendingRequests; - } - }(); - var Dataset = function() { - var keys = { - thumbprint: "thumbprint", - protocol: "protocol", - itemHash: "itemHash", - adjacencyList: "adjacencyList" - }; - function Dataset(o) { - utils.bindAll(this); - if (utils.isString(o.template) && !o.engine) { - $.error("no template engine specified"); - } - if (!o.local && !o.prefetch && !o.remote) { - $.error("one of local, prefetch, or remote is required"); - } - this.name = o.name || utils.getUniqueId(); - this.limit = o.limit || 5; - this.minLength = o.minLength || 1; - this.header = o.header; - this.footer = o.footer; - this.valueKey = o.valueKey || "value"; - this.template = compileTemplate(o.template, o.engine, this.valueKey); - this.local = o.local; - this.prefetch = o.prefetch; - this.remote = o.remote; - this.itemHash = {}; - this.adjacencyList = {}; - this.storage = o.name ? new PersistentStorage(o.name) : null; - } - utils.mixin(Dataset.prototype, { - _processLocalData: function(data) { - this._mergeProcessedData(this._processData(data)); - }, - _loadPrefetchData: function(o) { - var that = this, thumbprint = VERSION + (o.thumbprint || ""), storedThumbprint, storedProtocol, storedItemHash, storedAdjacencyList, isExpired, deferred; - if (this.storage) { - storedThumbprint = this.storage.get(keys.thumbprint); - storedProtocol = this.storage.get(keys.protocol); - storedItemHash = this.storage.get(keys.itemHash); - storedAdjacencyList = this.storage.get(keys.adjacencyList); - } - isExpired = storedThumbprint !== thumbprint || storedProtocol !== utils.getProtocol(); - o = utils.isString(o) ? { - url: o - } : o; - o.ttl = utils.isNumber(o.ttl) ? o.ttl : 24 * 60 * 60 * 1e3; - if (storedItemHash && storedAdjacencyList && !isExpired) { - this._mergeProcessedData({ - itemHash: storedItemHash, - adjacencyList: storedAdjacencyList - }); - deferred = $.Deferred().resolve(); - } else { - deferred = $.getJSON(o.url).done(processPrefetchData); - } - return deferred; - function processPrefetchData(data) { - var filteredData = o.filter ? o.filter(data) : data, processedData = that._processData(filteredData), itemHash = processedData.itemHash, adjacencyList = processedData.adjacencyList; - if (that.storage) { - that.storage.set(keys.itemHash, itemHash, o.ttl); - that.storage.set(keys.adjacencyList, adjacencyList, o.ttl); - that.storage.set(keys.thumbprint, thumbprint, o.ttl); - that.storage.set(keys.protocol, utils.getProtocol(), o.ttl); - } - that._mergeProcessedData(processedData); - } - }, - _transformDatum: function(datum) { - var value = utils.isString(datum) ? datum : datum[this.valueKey], tokens = datum.tokens || utils.tokenizeText(value), item = { - value: value, - tokens: tokens - }; - if (utils.isString(datum)) { - item.datum = {}; - item.datum[this.valueKey] = datum; - } else { - item.datum = datum; - } - item.tokens = utils.filter(item.tokens, function(token) { - return !utils.isBlankString(token); - }); - item.tokens = utils.map(item.tokens, function(token) { - return token.toLowerCase(); - }); - return item; - }, - _processData: function(data) { - var that = this, itemHash = {}, adjacencyList = {}; - utils.each(data, function(i, datum) { - var item = that._transformDatum(datum), id = utils.getUniqueId(item.value); - itemHash[id] = item; - utils.each(item.tokens, function(i, token) { - var character = token.charAt(0), adjacency = adjacencyList[character] || (adjacencyList[character] = [ id ]); - !~utils.indexOf(adjacency, id) && adjacency.push(id); - }); - }); - return { - itemHash: itemHash, - adjacencyList: adjacencyList - }; - }, - _mergeProcessedData: function(processedData) { - var that = this; - utils.mixin(this.itemHash, processedData.itemHash); - utils.each(processedData.adjacencyList, function(character, adjacency) { - var masterAdjacency = that.adjacencyList[character]; - that.adjacencyList[character] = masterAdjacency ? masterAdjacency.concat(adjacency) : adjacency; - }); - }, - _getLocalSuggestions: function(terms) { - var that = this, firstChars = [], lists = [], shortestList, suggestions = []; - utils.each(terms, function(i, term) { - var firstChar = term.charAt(0); - !~utils.indexOf(firstChars, firstChar) && firstChars.push(firstChar); - }); - utils.each(firstChars, function(i, firstChar) { - var list = that.adjacencyList[firstChar]; - if (!list) { - return false; - } - lists.push(list); - if (!shortestList || list.length < shortestList.length) { - shortestList = list; - } - }); - if (lists.length < firstChars.length) { - return []; - } - utils.each(shortestList, function(i, id) { - var item = that.itemHash[id], isCandidate, isMatch; - isCandidate = utils.every(lists, function(list) { - return ~utils.indexOf(list, id); - }); - isMatch = isCandidate && utils.every(terms, function(term) { - return utils.some(item.tokens, function(token) { - return token.indexOf(term) === 0; - }); - }); - isMatch && suggestions.push(item); - }); - return suggestions; - }, - initialize: function() { - var deferred; - this.local && this._processLocalData(this.local); - this.transport = this.remote ? new Transport(this.remote) : null; - deferred = this.prefetch ? this._loadPrefetchData(this.prefetch) : $.Deferred().resolve(); - this.local = this.prefetch = this.remote = null; - this.initialize = function() { - return deferred; - }; - return deferred; - }, - getSuggestions: function(query, cb) { - var that = this, terms, suggestions, cacheHit = false; - if (query.length < this.minLength) { - return; - } - terms = utils.tokenizeQuery(query); - suggestions = this._getLocalSuggestions(terms).slice(0, this.limit); - if (suggestions.length < this.limit && this.transport) { - cacheHit = this.transport.get(query, processRemoteData); - } - !cacheHit && cb && cb(suggestions); - function processRemoteData(data) { - suggestions = suggestions.slice(0); - utils.each(data, function(i, datum) { - var item = that._transformDatum(datum), isDuplicate; - isDuplicate = utils.some(suggestions, function(suggestion) { - //return item.value === suggestion.value; - return false; - }); - !isDuplicate && suggestions.push(item); - return suggestions.length < that.limit; - }); - cb && cb(suggestions); - } - } - }); - return Dataset; - function compileTemplate(template, engine, valueKey) { - var renderFn, compiledTemplate; - if (utils.isFunction(template)) { - renderFn = template; - } else if (utils.isString(template)) { - compiledTemplate = engine.compile(template); - renderFn = utils.bind(compiledTemplate.render, compiledTemplate); - } else { - renderFn = function(context) { - return "

" + context[valueKey] + "

"; - }; - } - return renderFn; - } - }(); - var InputView = function() { - function InputView(o) { - var that = this; - utils.bindAll(this); - this.specialKeyCodeMap = { - // START METAMAPS CODE - //9: "tab", - // END METAMAPS CODE - 27: "esc", - 37: "left", - 39: "right", - 13: "enter", - 38: "up", - 40: "down" - }; - this.$hint = $(o.hint); - this.$input = $(o.input).on("blur.tt", this._handleBlur).on("focus.tt", this._handleFocus).on("keydown.tt", this._handleSpecialKeyEvent); - if (!utils.isMsie()) { - this.$input.on("input.tt", this._compareQueryToInputValue); - } else { - this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { - if (that.specialKeyCodeMap[$e.which || $e.keyCode]) { - return; - } - utils.defer(that._compareQueryToInputValue); - }); - } - this.query = this.$input.val(); - this.$overflowHelper = buildOverflowHelper(this.$input); - } - utils.mixin(InputView.prototype, EventTarget, { - _handleFocus: function() { - this.trigger("focused"); - }, - _handleBlur: function() { - this.trigger("blured"); - }, - _handleSpecialKeyEvent: function($e) { - var keyName = this.specialKeyCodeMap[$e.which || $e.keyCode]; - keyName && this.trigger(keyName + "Keyed", $e); - }, - _compareQueryToInputValue: function() { - var inputValue = this.getInputValue(), isSameQuery = compareQueries(this.query, inputValue), isSameQueryExceptWhitespace = isSameQuery ? this.query.length !== inputValue.length : false; - if (isSameQueryExceptWhitespace) { - this.trigger("whitespaceChanged", { - value: this.query - }); - } else if (!isSameQuery) { - this.trigger("queryChanged", { - value: this.query = inputValue - }); - } - }, - destroy: function() { - this.$hint.off(".tt"); - this.$input.off(".tt"); - this.$hint = this.$input = this.$overflowHelper = null; - }, - focus: function() { - this.$input.focus(); - }, - blur: function() { - this.$input.blur(); - }, - getQuery: function() { - return this.query; - }, - setQuery: function(query) { - this.query = query; - }, - getInputValue: function() { - return this.$input.val(); - }, - setInputValue: function(value, silent) { - this.$input.val(value); - !silent && this._compareQueryToInputValue(); - }, - getHintValue: function() { - return this.$hint.val(); - }, - setHintValue: function(value) { - this.$hint.val(value); - }, - getLanguageDirection: function() { - return (this.$input.css("direction") || "ltr").toLowerCase(); - }, - isOverflow: function() { - this.$overflowHelper.text(this.getInputValue()); - return this.$overflowHelper.width() > this.$input.width(); - }, - isCursorAtEnd: function() { - var valueLength = this.$input.val().length, selectionStart = this.$input[0].selectionStart, range; - if (utils.isNumber(selectionStart)) { - return selectionStart === valueLength; - } else if (document.selection) { - range = document.selection.createRange(); - range.moveStart("character", -valueLength); - return valueLength === range.text.length; - } - return true; - } - }); - return InputView; - function buildOverflowHelper($input) { - return $("").css({ - position: "absolute", - left: "-9999px", - visibility: "hidden", - whiteSpace: "nowrap", - fontFamily: $input.css("font-family"), - fontSize: $input.css("font-size"), - fontStyle: $input.css("font-style"), - fontVariant: $input.css("font-variant"), - fontWeight: $input.css("font-weight"), - wordSpacing: $input.css("word-spacing"), - letterSpacing: $input.css("letter-spacing"), - textIndent: $input.css("text-indent"), - textRendering: $input.css("text-rendering"), - textTransform: $input.css("text-transform") - }).insertAfter($input); - } - function compareQueries(a, b) { - a = (a || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " "); - b = (b || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " "); - return a === b; - } - }(); - var DropdownView = function() { - var html = { - suggestionsList: '' - }, css = { - suggestionsList: { - display: "block" - }, - suggestion: { - whiteSpace: "nowrap", - cursor: "pointer" - }, - suggestionChild: { - whiteSpace: "normal" - } - }; - function DropdownView(o) { - utils.bindAll(this); - this.isOpen = false; - this.isEmpty = true; - this.isMouseOverDropdown = false; - this.$menu = $(o.menu).on("mouseenter.tt", this._handleMouseenter).on("mouseleave.tt", this._handleMouseleave).on("click.tt", ".tt-suggestion", this._handleSelection).on("mouseover.tt", ".tt-suggestion", this._handleMouseover); - } - utils.mixin(DropdownView.prototype, EventTarget, { - _handleMouseenter: function() { - this.isMouseOverDropdown = true; - }, - _handleMouseleave: function() { - this.isMouseOverDropdown = false; - - // START METAMAPS CODE - this._getSuggestions().removeClass("tt-is-under-cursor"); - this._getSuggestions().removeClass("tt-is-under-mouse-cursor"); - // END METAMAPS CODE - }, - _handleMouseover: function($e) { - var $suggestion = $($e.currentTarget); - this._getSuggestions().removeClass("tt-is-under-cursor"); - // START METAMAPS CODE - this._getSuggestions().removeClass("tt-is-under-mouse-cursor"); - $suggestion.addClass("tt-is-under-mouse-cursor"); - // ORIGINAL CODE $suggestion.addClass("tt-is-under-cursor"); - }, - _handleSelection: function($e) { - var $suggestion = $($e.currentTarget); - this.trigger("suggestionSelected", extractSuggestion($suggestion)); - }, - _show: function() { - this.$menu.css("display", "block"); - }, - _hide: function() { - this.$menu.hide(); - }, - _moveCursor: function(increment) { - var $suggestions, $cur, nextIndex, $underCursor; - if (!this.isVisible()) { - return; - } - $suggestions = this._getSuggestions(); - $cur = $suggestions.filter(".tt-is-under-cursor"); - $cur.removeClass("tt-is-under-cursor"); - nextIndex = $suggestions.index($cur) + increment; - nextIndex = (nextIndex + 1) % ($suggestions.length + 1) - 1; - if (nextIndex === -1) { - this.trigger("cursorRemoved"); - return; - } else if (nextIndex < -1) { - nextIndex = $suggestions.length - 1; - } - $underCursor = $suggestions.eq(nextIndex).addClass("tt-is-under-cursor"); - this._ensureVisibility($underCursor); - this.trigger("cursorMoved", extractSuggestion($underCursor)); - }, - _getSuggestions: function() { - return this.$menu.find(".tt-suggestions > .tt-suggestion"); - }, - _ensureVisibility: function($el) { - var menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10), menuScrollTop = this.$menu.scrollTop(), elTop = $el.position().top, elBottom = elTop + $el.outerHeight(true); - if (elTop < 0) { - this.$menu.scrollTop(menuScrollTop + elTop); - } else if (menuHeight < elBottom) { - this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight)); - } - }, - destroy: function() { - this.$menu.off(".tt"); - this.$menu = null; - }, - isVisible: function() { - return this.isOpen && !this.isEmpty; - }, - closeUnlessMouseIsOverDropdown: function() { - if (!this.isMouseOverDropdown) { - this.close(); - } - }, - close: function() { - if (this.isOpen) { - this.isOpen = false; - this.isMouseOverDropdown = false; - this._hide(); - this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor"); - this.trigger("closed"); - } - }, - open: function() { - if (!this.isOpen) { - this.isOpen = true; - !this.isEmpty && this._show(); - this.trigger("opened"); - } - }, - setLanguageDirection: function(dir) { - var ltrCss = { - left: "0", - right: "auto" - }, rtlCss = { - left: "auto", - right: " 0" - }; - dir === "ltr" ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss); - }, - moveCursorUp: function() { - this._moveCursor(-1); - }, - moveCursorDown: function() { - this._moveCursor(+1); - }, - getSuggestionUnderCursor: function() { - var $suggestion = this._getSuggestions().filter(".tt-is-under-cursor").first(); - return $suggestion.length > 0 ? extractSuggestion($suggestion) : null; - }, - getFirstSuggestion: function() { - var $suggestion = this._getSuggestions().first(); - return $suggestion.length > 0 ? extractSuggestion($suggestion) : null; - }, - renderSuggestions: function(dataset, suggestions) { - var datasetClassName = "tt-dataset-" + dataset.name, wrapper = '
%body
', compiledHtml, $suggestionsList, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, $el; - if ($dataset.length === 0) { - $suggestionsList = $(html.suggestionsList).css(css.suggestionsList); - $dataset = $("
").addClass(datasetClassName).append(dataset.header).append($suggestionsList).append(dataset.footer).appendTo(this.$menu); - } - if (suggestions.length > 0) { - this.isEmpty = false; - this.isOpen && this._show(); - elBuilder = document.createElement("div"); - fragment = document.createDocumentFragment(); - utils.each(suggestions, function(i, suggestion) { - suggestion.dataset = dataset.name; - compiledHtml = dataset.template(suggestion.datum); - elBuilder.innerHTML = wrapper.replace("%body", compiledHtml); - $el = $(elBuilder.firstChild).css(css.suggestion).data("suggestion", suggestion); - $el.children().each(function() { - $(this).css(css.suggestionChild); - }); - fragment.appendChild($el[0]); - }); - $dataset.show().find(".tt-suggestions").html(fragment); - } else { - this.clearSuggestions(dataset.name); - } - this.trigger("suggestionsRendered"); - }, - clearSuggestions: function(datasetName) { - var $datasets = datasetName ? this.$menu.find(".tt-dataset-" + datasetName) : this.$menu.find('[class^="tt-dataset-"]'), $suggestions = $datasets.find(".tt-suggestions"); - $datasets.hide(); - $suggestions.empty(); - if (this._getSuggestions().length === 0) { - this.isEmpty = true; - this._hide(); - } - } - }); - return DropdownView; - function extractSuggestion($el) { - return $el.data("suggestion"); - } - }(); - var TypeaheadView = function() { - var html = { - wrapper: '', - hint: '', - dropdown: '' - }, css = { - wrapper: { - position: "relative", - display: "inline-block" - }, - hint: { - position: "absolute", - top: "0", - left: "0", - borderColor: "transparent", - boxShadow: "none" - }, - query: { - position: "relative", - verticalAlign: "top", - backgroundColor: "transparent" - }, - dropdown: { - position: "absolute", - top: "100%", - left: "0", - zIndex: "100", - display: "none" - } - }; - if (utils.isMsie()) { - utils.mixin(css.query, { - backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" - }); - } - if (utils.isMsie() && utils.isMsie() <= 7) { - utils.mixin(css.wrapper, { - display: "inline", - zoom: "1" - }); - utils.mixin(css.query, { - marginTop: "-1px" - }); - } - function TypeaheadView(o) { - var $menu, $input, $hint; - utils.bindAll(this); - this.$node = buildDomStructure(o.input); - this.datasets = o.datasets; - this.dir = null; - this.eventBus = o.eventBus; - $menu = this.$node.find(".tt-dropdown-menu"); - $input = this.$node.find(".tt-query"); - $hint = this.$node.find(".tt-hint"); - this.dropdownView = new DropdownView({ - menu: $menu - }).on("suggestionSelected", this._handleSelection).on("cursorMoved", this._clearHint).on("cursorMoved", this._setInputValueToSuggestionUnderCursor).on("cursorRemoved", this._setInputValueToQuery).on("cursorRemoved", this._updateHint).on("suggestionsRendered", this._updateHint).on("opened", this._updateHint).on("closed", this._clearHint).on("opened closed", this._propagateEvent); - // START METAMAPS CODE - this.dropdownView.on('suggestionsRendered', this._suggestionsRendered); - // END METAMAPS CODE - - this.inputView = new InputView({ - input: $input, - hint: $hint - }).on("focused", this._openDropdown).on("blured", this._closeDropdown).on("blured", this._setInputValueToQuery).on("enterKeyed tabKeyed", this._handleSelection).on("queryChanged", this._clearHint).on("queryChanged", this._clearSuggestions).on("queryChanged", this._getSuggestions).on("whitespaceChanged", this._updateHint).on("queryChanged whitespaceChanged", this._openDropdown).on("queryChanged whitespaceChanged", this._setLanguageDirection).on("escKeyed", this._closeDropdown).on("escKeyed", this._setInputValueToQuery).on("tabKeyed upKeyed downKeyed", this._managePreventDefault).on("upKeyed downKeyed", this._moveDropdownCursor).on("upKeyed downKeyed", this._openDropdown).on("tabKeyed leftKeyed rightKeyed", this._autocomplete); - // START METAMAPS CODE - this.inputView.on('queryChanged', this._queryChanged); - // END METAMAPS CODE - } - utils.mixin(TypeaheadView.prototype, EventTarget, { - _managePreventDefault: function(e) { - var $e = e.data, hint, inputValue, preventDefault = false; - switch (e.type) { - case "tabKeyed": - hint = this.inputView.getHintValue(); - inputValue = this.inputView.getInputValue(); - preventDefault = hint && hint !== inputValue; - break; - - case "upKeyed": - case "downKeyed": - preventDefault = !$e.shiftKey && !$e.ctrlKey && !$e.metaKey; - break; - } - preventDefault && $e.preventDefault(); - }, - _setLanguageDirection: function() { - var dir = this.inputView.getLanguageDirection(); - if (dir !== this.dir) { - this.dir = dir; - this.$node.css("direction", dir); - this.dropdownView.setLanguageDirection(dir); - } - }, - // START METAMAPS CODE - _suggestionsRendered: function() { - this.eventBus.trigger('suggestionsRendered'); - }, - _queryChanged: function() { - this.eventBus.trigger('queryChanged'); - }, - // END METAMAPS CODE - _updateHint: function() { - var suggestion = this.dropdownView.getFirstSuggestion(), hint = suggestion ? suggestion.value : null, dropdownIsVisible = this.dropdownView.isVisible(), inputHasOverflow = this.inputView.isOverflow(), inputValue, query, escapedQuery, beginsWithQuery, match; - if (hint && dropdownIsVisible && !inputHasOverflow) { - inputValue = this.inputView.getInputValue(); - query = inputValue.replace(/\s{2,}/g, " ").replace(/^\s+/g, ""); - escapedQuery = utils.escapeRegExChars(query); - beginsWithQuery = new RegExp("^(?:" + escapedQuery + ")(.*$)", "i"); - match = beginsWithQuery.exec(hint); - this.inputView.setHintValue(inputValue + (match ? match[1] : "")); - } - }, - _clearHint: function() { - this.inputView.setHintValue(""); - }, - _clearSuggestions: function() { - this.dropdownView.clearSuggestions(); - }, - _setInputValueToQuery: function() { - this.inputView.setInputValue(this.inputView.getQuery()); - }, - _setInputValueToSuggestionUnderCursor: function(e) { - var suggestion = e.data; - this.inputView.setInputValue(suggestion.value, true); - }, - _openDropdown: function() { - this.dropdownView.open(); - }, - _closeDropdown: function(e) { - this.dropdownView[e.type === "blured" ? "closeUnlessMouseIsOverDropdown" : "close"](); - }, - _moveDropdownCursor: function(e) { - var $e = e.data; - if (!$e.shiftKey && !$e.ctrlKey && !$e.metaKey) { - this.dropdownView[e.type === "upKeyed" ? "moveCursorUp" : "moveCursorDown"](); - } - }, - _handleSelection: function(e) { - var byClick = e.type === "suggestionSelected", suggestion = byClick ? e.data : this.dropdownView.getSuggestionUnderCursor(); - if (suggestion) { - this.inputView.setInputValue(suggestion.value); - byClick ? this.inputView.focus() : e.data.preventDefault(); - byClick && utils.isMsie() ? utils.defer(this.dropdownView.close) : this.dropdownView.close(); - this.eventBus.trigger("selected", suggestion.datum, suggestion.dataset); - } - }, - _getSuggestions: function() { - var that = this, query = this.inputView.getQuery(); - if (utils.isBlankString(query)) { - return; - } - utils.each(this.datasets, function(i, dataset) { - dataset.getSuggestions(query, function(suggestions) { - if (query === that.inputView.getQuery()) { - that.dropdownView.renderSuggestions(dataset, suggestions); - } - }); - }); - }, - _autocomplete: function(e) { - var isCursorAtEnd, ignoreEvent, query, hint, suggestion; - if (e.type === "rightKeyed" || e.type === "leftKeyed") { - isCursorAtEnd = this.inputView.isCursorAtEnd(); - ignoreEvent = this.inputView.getLanguageDirection() === "ltr" ? e.type === "leftKeyed" : e.type === "rightKeyed"; - if (!isCursorAtEnd || ignoreEvent) { - return; - } - } - query = this.inputView.getQuery(); - hint = this.inputView.getHintValue(); - if (hint !== "" && query !== hint) { - suggestion = this.dropdownView.getFirstSuggestion(); - this.inputView.setInputValue(suggestion.value); - this.eventBus.trigger("autocompleted", suggestion.datum, suggestion.dataset); - } - }, - _propagateEvent: function(e) { - this.eventBus.trigger(e.type); - }, - destroy: function() { - this.inputView.destroy(); - this.dropdownView.destroy(); - destroyDomStructure(this.$node); - this.$node = null; - }, - setQuery: function(query) { - this.inputView.setQuery(query); - this.inputView.setInputValue(query); - this._clearHint(); - this._clearSuggestions(); - this._getSuggestions(); - } - }); - return TypeaheadView; - function buildDomStructure(input) { - var $wrapper = $(html.wrapper), $dropdown = $(html.dropdown), $input = $(input), $hint = $(html.hint); - $wrapper = $wrapper.css(css.wrapper); - $dropdown = $dropdown.css(css.dropdown); - $hint.css(css.hint).css({ - backgroundAttachment: $input.css("background-attachment"), - backgroundClip: $input.css("background-clip"), - backgroundColor: $input.css("background-color"), - backgroundImage: $input.css("background-image"), - backgroundOrigin: $input.css("background-origin"), - backgroundPosition: $input.css("background-position"), - backgroundRepeat: $input.css("background-repeat"), - backgroundSize: $input.css("background-size") - }); - $input.data("ttAttrs", { - dir: $input.attr("dir"), - autocomplete: $input.attr("autocomplete"), - spellcheck: $input.attr("spellcheck"), - style: $input.attr("style") - }); - $input.addClass("tt-query").attr({ - autocomplete: "off", - spellcheck: false - }).css(css.query); - try { - !$input.attr("dir") && $input.attr("dir", "auto"); - } catch (e) {} - return $input.wrap($wrapper).parent().prepend($hint).append($dropdown); - } - function destroyDomStructure($node) { - var $input = $node.find(".tt-query"); - utils.each($input.data("ttAttrs"), function(key, val) { - utils.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); - }); - $input.detach().removeData("ttAttrs").removeClass("tt-query").insertAfter($node); - $node.remove(); - } - }(); - (function() { - var cache = {}, viewKey = "ttView", methods; - methods = { - initialize: function(datasetDefs) { - var datasets; - datasetDefs = utils.isArray(datasetDefs) ? datasetDefs : [ datasetDefs ]; - if (datasetDefs.length === 0) { - $.error("no datasets provided"); - } - datasets = utils.map(datasetDefs, function(o) { - var dataset = cache[o.name] ? cache[o.name] : new Dataset(o); - if (o.name) { - cache[o.name] = dataset; - } - return dataset; - }); - return this.each(initialize); - function initialize() { - var $input = $(this), deferreds, eventBus = new EventBus({ - el: $input - }); - deferreds = utils.map(datasets, function(dataset) { - return dataset.initialize(); - }); - $input.data(viewKey, new TypeaheadView({ - input: $input, - eventBus: eventBus = new EventBus({ - el: $input - }), - datasets: datasets - })); - $.when.apply($, deferreds).always(function() { - utils.defer(function() { - eventBus.trigger("initialized"); - }); - }); - } - }, - destroy: function() { - return this.each(destroy); - function destroy() { - var $this = $(this), view = $this.data(viewKey); - if (view) { - view.destroy(); - $this.removeData(viewKey); - } - } - }, - setQuery: function(query) { - return this.each(setQuery); - function setQuery() { - var view = $(this).data(viewKey); - view && view.setQuery(query); - } - } - }; - jQuery.fn.typeahead = function(method) { - if (methods[method]) { - return methods[method].apply(this, [].slice.call(arguments, 1)); - } else { - return methods.initialize.apply(this, arguments); - } - }; - })(); -})(window.jQuery); diff --git a/app/assets/javascripts/src/JIT.js b/app/assets/javascripts/src/JIT.js.erb similarity index 96% rename from app/assets/javascripts/src/JIT.js rename to app/assets/javascripts/src/JIT.js.erb index 79954271..81cc687d 100644 --- a/app/assets/javascripts/src/JIT.js +++ b/app/assets/javascripts/src/JIT.js.erb @@ -3127,7 +3127,7 @@ var Canvas; ctx = base.getCtx(), scale = base.scaleOffsetX; //var pattern = new Image(); - //pattern.src = "/assets/cubes.png"; + //pattern.src = "<%= asset_path('cubes.png') %>"; //var ptrn = ctx.createPattern(pattern, 'repeat'); //ctx.fillStyle = ptrn; ctx.fillStyle = Metamaps.Settings.colors.background; diff --git a/app/assets/javascripts/src/Metamaps.Backbone.js b/app/assets/javascripts/src/Metamaps.Backbone.js.erb similarity index 100% rename from app/assets/javascripts/src/Metamaps.Backbone.js rename to app/assets/javascripts/src/Metamaps.Backbone.js.erb diff --git a/app/assets/javascripts/src/Metamaps.GlobalUI.js b/app/assets/javascripts/src/Metamaps.GlobalUI.js.erb similarity index 96% rename from app/assets/javascripts/src/Metamaps.GlobalUI.js rename to app/assets/javascripts/src/Metamaps.GlobalUI.js.erb index 46a34b5c..5c575e88 100644 --- a/app/assets/javascripts/src/Metamaps.GlobalUI.js +++ b/app/assets/javascripts/src/Metamaps.GlobalUI.js.erb @@ -495,7 +495,7 @@ Metamaps.GlobalUI.Search = { dataset.push({ value: "No results", label: "No results", - typeImageURL: "/assets/icons/wildcard.png", + typeImageURL: "<%= asset_path('icons/wildcard.png') %>", rtype: "noresult" }); } @@ -549,7 +549,7 @@ Metamaps.GlobalUI.Search = { filter: function (dataset) { if (dataset.length == 0) { dataset.push({ - profile: "/assets/user.png", + profile: "<%= asset_path('user.png') %>", value: "No results", label: "No results", @@ -616,7 +616,7 @@ Metamaps.GlobalUI.Search = { var self = Metamaps.GlobalUI.Search; function toggleResultSet(set) { - var s = $('.tt-dataset-' + set + ' .tt-suggestions'); + var s = $('.tt-dataset-' + set + ' .tt-dataset'); if (s.css('height') == '0px') { s.css({ 'height': 'auto', @@ -658,4 +658,4 @@ Metamaps.GlobalUI.Search = { showLoader: function () { $('#searchLoading').show(); } -}; \ No newline at end of file +}; diff --git a/app/assets/javascripts/src/Metamaps.JIT.js b/app/assets/javascripts/src/Metamaps.JIT.js.erb similarity index 97% rename from app/assets/javascripts/src/Metamaps.JIT.js rename to app/assets/javascripts/src/Metamaps.JIT.js.erb index bef4ad59..38a2de5b 100644 --- a/app/assets/javascripts/src/Metamaps.JIT.js +++ b/app/assets/javascripts/src/Metamaps.JIT.js.erb @@ -29,10 +29,10 @@ Metamaps.JIT = { $(".takeScreenshot").click(Metamaps.Map.exportImage); self.topicDescImage = new Image(); - self.topicDescImage.src = '/assets/topic_description_signifier.png'; + self.topicDescImage.src = '<%= asset_path('topic_description_signifier.png') %>'; self.topicLinkImage = new Image(); - self.topicLinkImage.src = '/assets/topic_link_signifier.png'; + self.topicLinkImage.src = '<%= asset_path('topic_link_signifier.png') %>'; }, /** * convert our topic JSON into something JIT can use diff --git a/app/assets/javascripts/src/Metamaps.Router.js b/app/assets/javascripts/src/Metamaps.Router.js.erb similarity index 100% rename from app/assets/javascripts/src/Metamaps.Router.js rename to app/assets/javascripts/src/Metamaps.Router.js.erb diff --git a/app/assets/javascripts/src/Metamaps.Views.js b/app/assets/javascripts/src/Metamaps.Views.js.erb similarity index 100% rename from app/assets/javascripts/src/Metamaps.Views.js rename to app/assets/javascripts/src/Metamaps.Views.js.erb diff --git a/app/assets/javascripts/src/Metamaps.js b/app/assets/javascripts/src/Metamaps.js.erb similarity index 94% rename from app/assets/javascripts/src/Metamaps.js rename to app/assets/javascripts/src/Metamaps.js.erb index af0518d0..84fd38bc 100644 --- a/app/assets/javascripts/src/Metamaps.js +++ b/app/assets/javascripts/src/Metamaps.js.erb @@ -37,7 +37,7 @@ Metamaps.Settings = { background: '#18202E', text: '#DDD' } - } + }, }; Metamaps.Touch = { @@ -157,7 +157,7 @@ Metamaps.Backbone.init = function () { this.on('saved', this.savedEvent); this.on('nowPrivate', function(){ var removeTopicData = { - topicid: this.id + mappableid: this.id }; $(document).trigger(Metamaps.JIT.events.removeTopic, [removeTopicData]); @@ -165,7 +165,7 @@ Metamaps.Backbone.init = function () { this.on('noLongerPrivate', function(){ var newTopicData = { mappingid: this.getMapping().id, - topicid: this.id + mappableid: this.id }; $(document).trigger(Metamaps.JIT.events.newTopic, [newTopicData]); @@ -194,7 +194,8 @@ Metamaps.Backbone.init = function () { return Metamaps.Mappings.findWhere({ map_id: Metamaps.Active.Map.id, - topic_id: this.isNew() ? this.cid : this.id + mappable_type: "Topic", + mappable_id: this.isNew() ? this.cid : this.id }); }, createNode: function () { @@ -320,14 +321,14 @@ Metamaps.Backbone.init = function () { this.on('noLongerPrivate', function(){ var newSynapseData = { mappingid: this.getMapping().id, - synapseid: this.id + mappableid: this.id }; $(document).trigger(Metamaps.JIT.events.newSynapse, [newSynapseData]); }); this.on('nowPrivate', function(){ $(document).trigger(Metamaps.JIT.events.removeSynapse, [{ - synapseid: this.id + mappableid: this.id }]); }); @@ -336,7 +337,7 @@ Metamaps.Backbone.init = function () { prepareLiForFilter: function () { var li = ''; li += '
  • ';       - li += '"'; li += ' alt="synapse icon" />';       li += '

    ' + this.get('desc') + '

  • '; return li; @@ -370,7 +371,8 @@ Metamaps.Backbone.init = function () { return Metamaps.Mappings.findWhere({ map_id: Metamaps.Active.Map.id, - synapse_id: this.isNew() ? this.cid : this.id + mappable_type: "Synapse", + mappable_id: this.isNew() ? this.cid : this.id }); }, createEdge: function () { @@ -457,11 +459,11 @@ Metamaps.Backbone.init = function () { return Metamaps.Map.get(this.get('map_id')); }, getTopic: function () { - if (this.get('category') === 'Topic') return Metamaps.Topic.get(this.get('topic_id')); + if (this.get('mappable_type') === 'Topic') return Metamaps.Topic.get(this.get('mappable_id')); else return false; }, getSynapse: function () { - if (this.get('category') === 'Synapse') return Metamaps.Synapse.get(this.get('synapse_id')); + if (this.get('mappable_type') === 'Synapse') return Metamaps.Synapse.get(this.get('mappable_id')); else return false; } }); @@ -664,24 +666,36 @@ Metamaps.Create = { Metamaps.Create.newTopic.name = $(this).val(); }); + var topicBloodhound = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: '/topics/autocomplete_topic?term=%QUERY', + wildcard: '%QUERY', + }, + }); + // initialize the autocomplete results for the metacode spinner $('#topic_name').typeahead( { + highlight: true, minLength: 2, }, [{ name: 'topic_autocomplete', limit: 8, - template: $('#topicAutocompleteTemplate').html(), - remote: { - url: '/topics/autocomplete_topic?term=%QUERY' + display: function (s) { return s.label; }, + templates: { + suggestion: function(s) { + return Hogan.compile($('#topicAutocompleteTemplate').html()).render(s); + }, }, - engine: Hogan + source: topicBloodhound, }] ); // tell the autocomplete to submit the form with the topic you clicked on if you pick from the autocomplete - $('#topic_name').bind('typeahead:selected', function (event, datum, dataset) { + $('#topic_name').bind('typeahead:select', function (event, datum, dataset) { Metamaps.Topic.getTopicFromAutocomplete(datum.id); }); @@ -714,7 +728,7 @@ Metamaps.Create = { }, hide: function () { $('#new_topic').fadeOut('fast'); - $("#topic_name").typeahead('setQuery', ''); + $("#topic_name").typeahead('val', ''); Metamaps.Create.newTopic.beingCreated = false; } }, @@ -726,35 +740,62 @@ Metamaps.Create = { Metamaps.Create.newSynapse.description = $(this).val(); }); + var synapseBloodhound = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: '/search/synapses?term=%QUERY', + wildcard: '%QUERY', + }, + }); + var existingSynapseBloodhound = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: '/search/synapses?topic1id=%TOPIC1&topic2id=%TOPIC2', + prepare: function(query, settings) { + var self = Metamaps.Create.newSynapse; + if (Metamaps.Selected.Nodes.length < 2) { + settings.url = settings.url.replace("%TOPIC1", self.topic1id).replace("%TOPIC2", self.topic2id); + return settings; + } else { + return null; + } + }, + }, + }); + // initialize the autocomplete results for synapse creation $('#synapse_desc').typeahead( { + highlight: true, minLength: 2, }, [{ name: 'synapse_autocomplete', - template: "
    {{label}}
    ", - remote: { - url: '/search/synapses?term=%QUERY' + display: function(s) { return s.label; }, + templates: { + suggestion: function(s) { + return Hogan.compile("
    {{label}}
    ").render(s); + }, }, - engine: Hogan + source: synapseBloodhound, }, { name: 'existing_synapses', limit: 50, - template: $('#synapseAutocompleteTemplate').html(), - remote: { - url: '/search/synapses', - replace: function () { - return self.getSearchQuery(); - } + display: function(s) { return s.label; }, + templates: { + suggestion: function(s) { + return Hogan.compile($('#synapseAutocompleteTemplate').html()).render(s); + }, + header: "

    Existing synapses

    " }, - engine: Hogan, - header: "

    Existing synapses

    " + source: existingSynapseBloodhound, }] ); - $('#synapse_desc').bind('typeahead:selected', function (event, datum, dataset) { + $('#synapse_desc').bind('typeahead:select', function (event, datum, dataset) { if (datum.id) { // if they clicked on an existing synapse get it Metamaps.Synapse.getSynapseFromAutocomplete(datum.id); } @@ -777,7 +818,7 @@ Metamaps.Create = { }, hide: function () { $('#new_synapse').fadeOut('fast'); - $("#synapse_desc").typeahead('setQuery', ''); + $("#synapse_desc").typeahead('val', ''); Metamaps.Create.newSynapse.beingCreated = false; Metamaps.Create.newTopic.addSynapse = false; Metamaps.Create.newSynapse.topic1id = 0; @@ -785,13 +826,6 @@ Metamaps.Create = { Metamaps.Mouse.synapseStartCoordinates = []; Metamaps.Visualize.mGraph.plot(); }, - getSearchQuery: function () { - var self = Metamaps.Create.newSynapse; - - if (Metamaps.Selected.Nodes.length < 2) { - return '/search/synapses?topic1id=' + self.topic1id + '&topic2id=' + self.topic2id; - } else return ''; - } } }; // end Metamaps.Create @@ -1534,8 +1568,6 @@ Metamaps.SynapseCard = { ////////////////////// END TOPIC AND SYNAPSE CARDS ////////////////////////////////// - - /* * * VISUALIZE @@ -2511,7 +2543,7 @@ Metamaps.Realtime = { if (!self.status) return; - function test() { + function waitThenRenderTopic() { if (topic && mapping && mapper) { Metamaps.Topic.renderTopic(mapping, topic, false, false); } @@ -2529,7 +2561,7 @@ Metamaps.Realtime = { Metamaps.Mapper.get(data.mapperid, mapperCallback); } $.ajax({ - url: "/topics/" + data.topicid + ".json", + url: "/topics/" + data.mappableid + ".json", success: function (response) { Metamaps.Topics.add(response); topic = Metamaps.Topics.get(response.id); @@ -2549,7 +2581,7 @@ Metamaps.Realtime = { } }); - test(); + waitThenRenderTopic(); }, // removeTopic sendDeleteTopic: function (data) { @@ -2576,7 +2608,7 @@ Metamaps.Realtime = { if (!self.status) return; - var topic = Metamaps.Topics.get(data.topicid); + var topic = Metamaps.Topics.get(data.mappableid); if (topic) { var node = topic.get('node'); var mapping = topic.getMapping(); @@ -2604,7 +2636,7 @@ Metamaps.Realtime = { if (!self.status) return; - function test() { + function waitThenRenderSynapse() { if (synapse && mapping && mapper) { topic1 = synapse.getTopic1(); node1 = topic1.get('node'); @@ -2627,7 +2659,7 @@ Metamaps.Realtime = { Metamaps.Mapper.get(data.mapperid, mapperCallback); } $.ajax({ - url: "/synapses/" + data.synapseid + ".json", + url: "/synapses/" + data.mappableid + ".json", success: function (response) { Metamaps.Synapses.add(response); synapse = Metamaps.Synapses.get(response.id); @@ -2646,7 +2678,7 @@ Metamaps.Realtime = { cancel = true; } }); - test(); + waitThenRenderSynapse(); }, // deleteSynapse sendDeleteSynapse: function (data) { @@ -2674,7 +2706,7 @@ Metamaps.Realtime = { if (!self.status) return; - var synapse = Metamaps.Synapses.get(data.synapseid); + var synapse = Metamaps.Synapses.get(data.mappableid); if (synapse) { var edge = synapse.get('edge'); var mapping = synapse.getMapping(); @@ -2784,12 +2816,12 @@ Metamaps.Control = { var permToDelete = Metamaps.Active.Mapper.id === topic.get('user_id') || Metamaps.Active.Mapper.get('admin'); if (permToDelete) { - var topicid = topic.id; + var mappableid = topic.id; var mapping = node.getData('mapping'); topic.destroy(); Metamaps.Mappings.remove(mapping); $(document).trigger(Metamaps.JIT.events.deleteTopic, [{ - topicid: topicid + mappableid: mappableid }]); Metamaps.Control.hideNode(nodeid); } else { @@ -2828,12 +2860,12 @@ Metamaps.Control = { } var topic = node.getData('topic'); - var topicid = topic.id; + var mappableid = topic.id; var mapping = node.getData('mapping'); mapping.destroy(); Metamaps.Topics.remove(topic); $(document).trigger(Metamaps.JIT.events.removeTopic, [{ - topicid: topicid + mappableid: mappableid }]); Metamaps.Control.hideNode(nodeid); }, @@ -2957,7 +2989,7 @@ Metamaps.Control = { Metamaps.Control.hideEdge(edge); } - var synapseid = synapse.id; + var mappableid = synapse.id; synapse.destroy(); // the server will destroy the mapping, we just need to remove it here @@ -2968,7 +3000,7 @@ Metamaps.Control = { delete edge.data.$displayIndex; } $(document).trigger(Metamaps.JIT.events.deleteSynapse, [{ - synapseid: synapseid + mappableid: mappableid }]); } else { Metamaps.GlobalUI.notifyUser('Only synapses you created can be deleted'); @@ -3013,7 +3045,7 @@ Metamaps.Control = { var synapse = edge.getData("synapses")[index]; var mapping = edge.getData("mappings")[index]; - var synapseid = synapse.id; + var mappableid = synapse.id; mapping.destroy(); Metamaps.Synapses.remove(synapse); @@ -3024,7 +3056,7 @@ Metamaps.Control = { delete edge.data.$displayIndex; } $(document).trigger(Metamaps.JIT.events.removeSynapse, [{ - synapseid: synapseid + mappableid: mappableid }]); }, hideSelectedEdges: function () { @@ -4024,14 +4056,14 @@ Metamaps.Topic = { var mappingSuccessCallback = function (mappingModel, response) { var newTopicData = { mappingid: mappingModel.id, - topicid: mappingModel.get('topic_id') + mappableid: mappingModel.get('mappable_id') }; $(document).trigger(Metamaps.JIT.events.newTopic, [newTopicData]); }; var topicSuccessCallback = function (topicModel, response) { if (Metamaps.Active.Map) { - mapping.save({ topic_id: topicModel.id }, { + mapping.save({ mappable_id: topicModel.id }, { success: mappingSuccessCallback, error: function (model, response) { console.log('error saving mapping to database'); @@ -4081,10 +4113,10 @@ Metamaps.Topic = { Metamaps.Topics.add(topic); var mapping = new Metamaps.Backbone.Mapping({ - category: "Topic", xloc: Metamaps.Create.newTopic.x, yloc: Metamaps.Create.newTopic.y, - topic_id: topic.cid + mappable_id: topic.cid, + mappable_type: "Topic", }); Metamaps.Mappings.add(mapping); @@ -4103,10 +4135,10 @@ Metamaps.Topic = { var topic = self.get(id); var mapping = new Metamaps.Backbone.Mapping({ - category: "Topic", xloc: Metamaps.Create.newTopic.x, yloc: Metamaps.Create.newTopic.y, - topic_id: topic.id + mappable_type: "Topic", + mappable_id: topic.id, }); Metamaps.Mappings.add(mapping); @@ -4121,10 +4153,10 @@ Metamaps.Topic = { var nextCoords = Metamaps.Map.getNextCoord(); var mapping = new Metamaps.Backbone.Mapping({ - category: "Topic", xloc: nextCoords.x, yloc: nextCoords.y, - topic_id: topic.id + mappable_type: "Topic", + mappable_id: topic.id, }); Metamaps.Mappings.add(mapping); @@ -4195,14 +4227,14 @@ Metamaps.Synapse = { var mappingSuccessCallback = function (mappingModel, response) { var newSynapseData = { mappingid: mappingModel.id, - synapseid: mappingModel.get('synapse_id') + mappableid: mappingModel.get('mappable_id') }; $(document).trigger(Metamaps.JIT.events.newSynapse, [newSynapseData]); }; var synapseSuccessCallback = function (synapseModel, response) { if (Metamaps.Active.Map) { - mapping.save({ synapse_id: synapseModel.id }, { + mapping.save({ mappable_id: synapseModel.id }, { success: mappingSuccessCallback }); } @@ -4252,15 +4284,15 @@ Metamaps.Synapse = { node1 = synapsesToCreate[i]; topic1 = node1.getData('topic'); synapse = new Metamaps.Backbone.Synapse({ - desc: Metamaps.Create.newSynapse.description, + desc: Metamaps.Create.newSynapse.description,// || "", node1_id: topic1.isNew() ? topic1.cid : topic1.id, node2_id: topic2.isNew() ? topic2.cid : topic2.id, }); Metamaps.Synapses.add(synapse); mapping = new Metamaps.Backbone.Mapping({ - category: "Synapse", - synapse_id: synapse.cid + mappable_type: "Synapse", + mappable_id: synapse.cid, }); Metamaps.Mappings.add(mapping); @@ -4280,8 +4312,8 @@ Metamaps.Synapse = { var synapse = self.get(id); var mapping = new Metamaps.Backbone.Mapping({ - category: "Synapse", - synapse_id: synapse.id + mappable_type: "Synapse", + mappable_id: synapse.id, }); Metamaps.Mappings.add(mapping); @@ -4761,7 +4793,7 @@ Metamaps.Map.InfoBox = { obj["map_creator_tip"] = isCreator ? self.changePermissionText : ""; obj["contributors_class"] = Metamaps.Mappers.length > 1 ? "multiple" : ""; obj["contributors_class"] += Metamaps.Mappers.length === 2 ? " mTwo" : ""; - obj["contributor_image"] = Metamaps.Mappers.length > 0 ? Metamaps.Mappers.models[0].get("image") : "/assets/user.png"; + obj["contributor_image"] = Metamaps.Mappers.length > 0 ? Metamaps.Mappers.models[0].get("image") : "<%= asset_path('user.png') %>"; obj["contributor_list"] = self.createContributorList(); obj["user_name"] = isCreator ? "You" : map.get("user_name"); obj["created_at"] = map.get("created_at_clean"); @@ -4858,7 +4890,7 @@ Metamaps.Map.InfoBox = { if (Metamaps.Mappers.length === 2) contributors_class = "multiple mTwo"; else if (Metamaps.Mappers.length > 2) contributors_class = "multiple"; - var contributors_image = "/assets/user.png"; + var contributors_image = "<%= asset_path('user.png') %>"; if (Metamaps.Mappers.length > 0) { // get the first contributor and use their image contributors_image = Metamaps.Mappers.models[0].get("image"); @@ -5039,7 +5071,7 @@ Metamaps.Account = { var self = Metamaps.Account; $('.userImageDiv canvas').remove(); - $('.userImageDiv img').attr('src', '/assets/user.png').show(); + $('.userImageDiv img').attr('src', '<%= asset_path('user.png') %>').show(); $('.userImageMenu').hide(); var input = $('#user_image'); diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css.erb similarity index 90% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.css.erb index 392b2d2c..b671dd80 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css.erb @@ -78,7 +78,7 @@ html { } body { - background: #d8d9da url(shattered_@2X.png); + background: #d8d9da url(<%= asset_data_uri('shattered_@2X.png') %>); font-family: 'din-medium', helvetica, sans-serif; color: #424242; -moz-osx-font-smoothing: grayscale; @@ -300,7 +300,7 @@ input[type="submit"]:active { display: none; width: 40px; height: 40px; - background-image: url(photo.png); + background-image: url(<%= asset_data_uri('photo.png') %>); position: absolute; top: 22px; left: 22px; @@ -413,7 +413,7 @@ input[type="submit"]:active { } .accountName:hover .nameEdit:after { - background:url(edit.png)no-repeat; + background: url(<%= asset_data_uri('edit.png') %>) no-repeat; content:" "; position:absolute; width:25px; @@ -547,7 +547,7 @@ input[type="submit"]:active { display: block; height: 16px; width: 16px; - background-image: url(metacodesettings_sprite.png); + background-image: url(<%= asset_data_uri('metacodesettings_sprite.png') %>); position: absolute; z-index: 2; top: 20px; @@ -652,7 +652,7 @@ label { box-shadow: 6px 6px 8px rgba(0, 0, 0, 0.4); } .headertop .tab { - background: url(tab.png) no-repeat 0 0; + background: url('tab.png') no-repeat 0 0; position: absolute; top: -11px; right: -2px; @@ -719,7 +719,7 @@ label { .accountInnerArrow { width:16px; height:16px; - background-image: url(arrowdown_sprite.png); + background-image: url(<%= asset_data_uri('arrowdown_sprite.png') %>); background-repeat: no-repeat; position:absolute; top: 8px; @@ -783,7 +783,7 @@ label { position:absolute; pointer-events:none; background-repeat:no-repeat; - background-image: url(user_sprite.png); + background-image: url(<%= asset_data_uri('user_sprite.png') %>); } .accountSettings .accountIcon { background-position: 0 0; @@ -813,7 +813,7 @@ li.accountInvite span { padding: 9px 0 9px 62px; } .accountImage { - background-image: url(user.png); + background-image: url(<%= asset_data_uri 'user.png' %>); background-size: 84px 84px; background-repeat: no-repeat; height:84px; @@ -1145,7 +1145,7 @@ h3.realtimeBoxTitle { position: absolute; top: 4px; right: 0; - background-image: url('junto24_sprite.png'); + background-image: url(<%= asset_data_uri('junto24_sprite.png') %>); } .realtimeMapperList .littleRtOff .littleJuntoIcon { background-position: 0 0; @@ -1222,7 +1222,7 @@ h3.realtimeBoxTitle { position: absolute; top: 0; left: 4px; - background-image: url(context_sprite.png); + background-image: url(<%= asset_data_uri('context_sprite.png') %>); background-repeat: no-repeat; width: 24px; height: 24px; @@ -1238,7 +1238,7 @@ h3.realtimeBoxTitle { } .rightclickmenu .rc-center .rc-icon { - background-image: url(context_topicview_sprite.png); + background-image: url(<%= asset_data_uri('context_topicview_sprite.png') %>); } .rightclickmenu .rc-popout .rc-icon { background-position: 0 -72px; @@ -1251,7 +1251,7 @@ h3.realtimeBoxTitle { } .rightclickmenu .rc-siblings .rc-icon { background-position: 0 -24px; - background-image: url(context_topicview_sprite.png); + background-image: url(<%= asset_data_uri('context_topicview_sprite.png') %>); } .rightclickmenu .expandLi { position: absolute; @@ -1259,7 +1259,7 @@ h3.realtimeBoxTitle { right: 8px; width: 16px; height: 16px; - background-image: url(arrowright_sprite.png); + background-image: url(<%= asset_data_uri('arrowright_sprite.png') %>); background-repeat: no-repeat; background-position: 0 -32px; } @@ -1294,7 +1294,7 @@ h3.realtimeBoxTitle { position: absolute; top: 0; left: 4px; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); background-size: 72px 48px; background-repeat: no-repeat; width: 24px; @@ -1339,7 +1339,7 @@ h3.realtimeBoxTitle { right: 4px; width: 16px; height: 16px; - background-image: url(arrowright_sprite.png); + background-image: url(<%= asset_data_uri('arrowright_sprite.png') %>); background-repeat: no-repeat; background-position: 0 -32px; } @@ -1373,13 +1373,14 @@ h3.realtimeBoxTitle { /* topic and synapse autocomplete */ -#new_topic .tt-suggestion.tt-is-under-cursor, -#new_topic .tt-suggestion.tt-is-under-mouse-cursor, -#new_synapse .tt-suggestion.tt-is-under-cursor, -#new_synapse .tt-suggestion.tt-is-under-mouse-cursor { +#new_topic .tt-suggestion:hover, +#new_topic .tt-suggestion.tt-cursor, +#new_synapse .tt-suggestion:hover, +#new_synapse .tt-suggestion.tt-cursor { background: #E0E0E0; } #new_topic .tt-suggestion, +#new_synapse .tt-dataset h3, #new_synapse .tt-suggestion { background: #F5F5F5; position: relative; @@ -1418,15 +1419,15 @@ h3.realtimeBoxTitle { top: 8px; right: 8px; background-repeat: no-repeat; - background-image: url(arrowright_sprite.png); + background-image: url(<%= asset_data_uri('arrowright_sprite.png') %>); background-position: 0 -32px; } -#new_topic .tt-suggestion.tt-is-under-cursor .expandTopicMetadata, -#new_topic .tt-suggestion.tt-is-under-mouse-cursor .expandTopicMetadata { +#new_topic .tt-suggestion:hover .expandTopicMetadata, +#new_topic .tt-suggestion.tt-cursor .expandTopicMetadata { display: block; } -#new_topic .tt-suggestion.tt-is-under-cursor .topicMetadata, -#new_topic .tt-suggestion.tt-is-under-mouse-cursor .topicMetadata { +#new_topic .tt-suggestion:hover .topicMetadata, +#new_topic .tt-suggestion.tt-cursor .topicMetadata { display: block; } #new_topic .topicMetadata { @@ -1444,7 +1445,7 @@ h3.realtimeBoxTitle { width: 32px; height: 32px; background-repeat: no-repeat; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); position: absolute; bottom: 4px; right: 4px; @@ -1464,7 +1465,7 @@ h3.realtimeBoxTitle { #new_topic .topicNumMaps { height: 14px; padding: 1px 0 1px 32px; - background-image: url(metamap16.png); + background-image: url(<%= asset_data_uri('metamap16.png') %>); background-repeat: no-repeat; background-position: 8px 0; position: absolute; @@ -1473,7 +1474,7 @@ h3.realtimeBoxTitle { #new_topic .topicNumSynapses { height: 14px; padding: 1px 0 1px 32px; - background-image: url(synapse16.png); + background-image: url(<%= asset_data_uri('synapse16.png') %>); background-repeat: no-repeat; background-position: 8px 0; position: absolute; @@ -1563,7 +1564,7 @@ h3.realtimeBoxTitle { width: 32px; height: 32px; background-repeat: no-repeat; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); } /* map info box */ /* map info box */ @@ -1603,7 +1604,7 @@ h3.realtimeBoxTitle { resize: none; } .mapInfoBox.canEdit #mapInfoName:hover { - background-image: url(edit.png); + background-image: url(<%= asset_data_uri('edit.png') %>); background-repeat: no-repeat; background-position: bottom right; cursor: text; @@ -1757,11 +1758,11 @@ h3.realtimeBoxTitle { } .mapTopics { - background-image: url(topic32.png); + background-image: url(<%= asset_data_uri('topic32.png') %>); background-position: 13px center; } .mapSynapses { - background-image: url(synapse32padded.png); + background-image: url(<%= asset_data_uri('synapse32padded.png') %>); background-position: 13px center; } .mapInfoBox .mapPermission { @@ -1771,7 +1772,7 @@ h3.realtimeBoxTitle { padding: 0; margin: 8px 30px 8px 10px; position: relative; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); } .mapInfoBox .mapPermission.commons { background-position: 0 0; @@ -1783,12 +1784,12 @@ h3.realtimeBoxTitle { background-position: -32px 0; } .yourMap .mapPermission:hover { - background-image: url(arrowperms_sprite.png); + background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>); cursor: pointer; background-position: -32px 0; } .yourMap .mapPermission.minimize { - background-image: url(arrowperms_sprite.png) !important; + background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>) !important; cursor: pointer; background-position: 0 0; } @@ -1803,7 +1804,7 @@ h3.realtimeBoxTitle { width: 32px; height: 32px; background-repeat: no-repeat; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); } .mapInfoBox .mapPermission .permissionSelect .commons { background-position: 0 0; @@ -1850,7 +1851,7 @@ h3.realtimeBoxTitle { width: 340px; } .mapInfoBox.canEdit .mapInfoDesc:hover { - background-image: url(edit.png); + background-image: url(<%= asset_data_uri('edit.png') %>); background-position: top right; background-repeat: no-repeat; cursor: text; @@ -1895,7 +1896,7 @@ h3.realtimeBoxTitle { .mapInfoShareIcon { width: 24px; height: 24px; - background-image: url(share_sprite_mapinfo.png); + background-image: url(<%= asset_data_uri('share_sprite_mapinfo.png') %>); background-repeat: no-repeat; margin: 4px auto 0; background-position: 0 -24px; @@ -1916,7 +1917,7 @@ and it won't be important on password protected instances */ width: 16px; height: 16px; margin: 8px auto 0; - background-image: url(remove_mapinfo_sprite.png); + background-image: url(<%= asset_data_uri('remove_mapinfo_sprite.png') %>); background-repeat: no-repeat; background-position: -16px 0; } @@ -1977,7 +1978,7 @@ and it won't be important on password protected instances */ border: solid 2px #000; } #lightbox_overlay #lightbox_main a#lightbox_close { - background-image: url(xlightbox.png); + background-image: url(<%= asset_data_uri('xlightbox.png') %>); cursor: pointer; height: 32px; outline-style: none; @@ -2076,7 +2077,7 @@ and it won't be important on password protected instances */ color: #00bcd4; } .lightbox_links .lightboxAboutIcon { - background-image: url(about_sprite.png); + background-image: url(<%= asset_data_uri('about_sprite.png') %>); background-repeat: no-repeat; width:32px; height:32px; @@ -2351,15 +2352,15 @@ and it won't be important on password protected instances */ opacity: 0.7; } #chromeIcon { - background: url(/assets/browser_icons.png) no-repeat; + background: url(<%= asset_data_uri 'browser_icons.png' %>) no-repeat; } #fireFoxIcon { - background: url(/assets/browser_icons.png) no-repeat -105px 0; + background: url(<%= asset_data_uri 'browser_icons.png' %>) no-repeat -105px 0; } #safariIcon { - background: url(/assets/browser_icons.png) no-repeat -220px 0; + background: url(<%= asset_data_uri 'browser_icons.png' %>) no-repeat -220px 0; } #chromeIcon:hover{ @@ -2496,7 +2497,7 @@ and it won't be important on password protected instances */ position: relative; } .new_map .mapPermIcon { - background-image: url(permissions64sprite.png); + background-image: url(<%= asset_data_uri('permissions64sprite.png') %>); background-repeat: no-repeat; width:64px; height:64px; @@ -2611,7 +2612,7 @@ and it won't be important on password protected instances */ } #zoomIn { - background: #424242 url(zoom_sprite.png) no-repeat; + background: #424242 url(<%= asset_data_uri('zoom_sprite.png') %>) no-repeat; width: 22px; height: 22px; display: inline-block; @@ -2623,7 +2624,7 @@ and it won't be important on password protected instances */ } #zoomOut { - background: #424242 url(zoom_sprite.png) no-repeat; + background: #424242 url(<%= asset_data_uri('zoom_sprite.png') %>) no-repeat; width: 22px; height: 22px; display: inline-block; @@ -2635,7 +2636,7 @@ and it won't be important on password protected instances */ } #centerMap { - background: #424242 url(extents_sprite.png) no-repeat; + background: #424242 url(<%= asset_data_uri('extents_sprite.png') %>) no-repeat; width: 22px; height: 22px; display: inline-block; @@ -2940,7 +2941,7 @@ and it won't be important on password protected instances */ .compassArrow { display: none; background-repeat: no-repeat; - background-image: url(compass_arrow.png); + background-image: url(<%= asset_data_uri('compass_arrow.png') %>); width: 48px; height: 32px; position: absolute; diff --git a/app/assets/stylesheets/base.css b/app/assets/stylesheets/base.css.erb similarity index 88% rename from app/assets/stylesheets/base.css rename to app/assets/stylesheets/base.css.erb index 2e0073a0..216fa04d 100644 --- a/app/assets/stylesheets/base.css +++ b/app/assets/stylesheets/base.css.erb @@ -85,7 +85,7 @@ padding: 0 16px; } .canEdit #titleActivator:hover { - background-image: url(edit.png); + background-image: url(<%= asset_data_uri('edit.png') %>); background-repeat: no-repeat; background-position: bottom right; cursor: text; @@ -148,7 +148,7 @@ margin-right: 8px; } .canEdit .CardOnGraph .best_in_place_desc:hover { - background-image: url(edit.png); + background-image: url(<%= asset_data_uri('edit.png') %>); background-position: top right; background-repeat: no-repeat; cursor: text; @@ -244,7 +244,7 @@ left: 0; width: 32px; height: 32px; - background-image: url(map32_sprite.png); + background-image: url(<%= asset_data_uri('map32_sprite.png') %>); background-repeat: no-repeat; background-position: 0 0; cursor: pointer; @@ -335,7 +335,7 @@ left: 0; width: 32px; height: 32px; - background-image: url(synapse32_sprite.png); + background-image: url(<%= asset_data_uri('synapse32_sprite.png') %>); background-repeat: no-repeat; background-position: 0 0; } @@ -382,7 +382,7 @@ min-width: 32px; margin-top: 8px; margin-left: 8px; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); background-position: 0 0; } .mapPerm.co { @@ -396,12 +396,12 @@ } .yourTopic .mapPerm:hover, .yourEdge .mapPerm:hover { - background-image: url(arrowperms_sprite.png); + background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>); background-position: -32px 0; cursor:pointer; } .yourTopic .mapPerm.minimize, .yourEdge .mapPerm.minimize { - background-image: url(arrowperms_sprite.png) !important; + background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>) !important; background-position: 0 0; cursor: pointer; } @@ -417,7 +417,7 @@ cursor: pointer; height: 32px; background-repeat: no-repeat; background-position: 0 0; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); } .mapPerm .permissionSelect .commons { background-position: 0 0; @@ -462,7 +462,7 @@ cursor: pointer; right: 16px; width: 16px; height: 16px; - background-image: url(arrowright_sprite.png); + background-image: url(<%= asset_data_uri('arrowright_sprite.png') %>); background-repeat: no-repeat; background-position: 0 -32px; } @@ -542,7 +542,7 @@ background-color: #E0E0E0; right: 8px; width: 16px; height: 16px; - background-image: url(arrowright_sprite.png); + background-image: url(<%= asset_data_uri('arrowright_sprite.png') %>); background-repeat: no-repeat; background-position: 0 -32px; } @@ -648,10 +648,10 @@ background-color: #E0E0E0; left: 12px; } #linkIcon { - background-image: url(link_sprite.png); + background-image: url(<%= asset_data_uri('link_sprite.png') %>); } #uploadIcon { - background-image: url(upload_sprite.png); + background-image: url(<%= asset_data_uri('upload_sprite.png') %>); } #addlink:hover #linkIcon, #addupload:hover #uploadIcon { background-position: 0 -24px; @@ -690,7 +690,7 @@ font-family: 'din-regular', helvetica, sans-serif; height: 24px; background-repeat: no-repeat; background-position: 0 0; - background-image: url(link_sprite.png); + background-image: url(<%= asset_data_uri('link_sprite.png') %>); pointer-events: none; z-index: 1; } @@ -703,7 +703,7 @@ font-family: 'din-regular', helvetica, sans-serif; height: 32px; cursor: pointer; float:none; - background-image: url(remove.png); + background-image: url(<%= asset_data_uri('remove.png') %>); background-repeat: no-repeat; background-position: center center; } @@ -740,7 +740,7 @@ font-family: 'din-regular', helvetica, sans-serif; } #linkremove { - background-image: url(/assets/remove.png); + background-image: url(<%= asset_data_uri 'remove.png' %>); background-repeat: no-repeat; background-position: center center; width: 24px; @@ -781,7 +781,7 @@ font-family: 'din-regular', helvetica, sans-serif; } #editSynUpperBar { - background: #FFFFFF url(synapse32.png) no-repeat 8px center; + background: #FFFFFF url(<%= asset_data_uri('synapse32.png') %>) no-repeat 8px center; min-height: 48px; height: 48px; border-bottom: 1px solid #222222; @@ -814,7 +814,7 @@ font-family: 'din-regular', helvetica, sans-serif; } .canEdit #edit_synapse_desc:hover { - background-image: url(edit.png); + background-image: url(<%= asset_data_uri('edit.png') %>); background-repeat: no-repeat; background-position: 164px center; cursor: text; @@ -841,7 +841,7 @@ font-family: 'din-regular', helvetica, sans-serif; height: 24px; top: 12px; right: 8px; - background-image: url(arrowdown_sprite.png); + background-image: url(<%= asset_data_uri('arrowdown_sprite.png') %>); background-repeat: no-repeat; background-position: 4px -12px; } @@ -923,11 +923,11 @@ font-family: 'din-regular', helvetica, sans-serif; background-repeat: no-repeat; } #edit_synapse_right { - background-image: url(synapsedirectionright_sprite.png); + background-image: url(<%= asset_data_uri('synapsedirectionright_sprite.png') %>); right: 16px; } #edit_synapse_left { - background-image: url(synapsedirectionleft_sprite.png); + background-image: url(<%= asset_data_uri('synapsedirectionleft_sprite.png') %>); right: 56px; } #edit_synapse_left.checked, #edit_synapse_right.checked { @@ -1103,7 +1103,7 @@ font-family: 'din-regular', helvetica, sans-serif; } .mapperMetadata { - background: url(profile_card_sprite.png) no-repeat center 0; + background: url(<%= asset_data_uri('profile_card_sprite.png') %>) no-repeat center 0; height: 64px; width: 160px; margin: 16px auto 0; @@ -1126,4 +1126,4 @@ font-family: 'din-regular', helvetica, sans-serif; } .mapperMetadata .metadataSynapses { color: #DAB539; -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/clean.css b/app/assets/stylesheets/clean.css.erb similarity index 90% rename from app/assets/stylesheets/clean.css rename to app/assets/stylesheets/clean.css.erb index 4ecd3120..94164b6b 100644 --- a/app/assets/stylesheets/clean.css +++ b/app/assets/stylesheets/clean.css.erb @@ -1,20 +1,20 @@ @font-face { font-family: 'din-medium'; - src: url('/assets/Fonts/din.eot'); - src: url('/assets/Fonts/din.eot?#iefix') format('embedded-opentype'), - url('/assets/Fonts/din.woff') format('woff'), - url('/assets/Fonts/din.ttf') format('truetype'), - url('/assets/Fonts/din.svg#din-medium') format('svg'); + src: url(<%= asset_path 'Fonts/din.eot' %>); + src: url(<%= asset_path 'Fonts/din.eot?#iefix' %>) format('embedded-opentype'), + url(<%= asset_path 'Fonts/din.woff' %>) format('woff'), + url(<%= asset_path 'Fonts/din.ttf' %>) format('truetype'), + url(<%= asset_path 'Fonts/din.svg#din-medium' %>) format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'din-regular'; - src: url('/assets/Fonts/din-reg.eot'); - src: url('/assets/Fonts/din-reg.eot?#iefix') format('embedded-opentype'), - url('/assets/Fonts/din-reg.woff') format('woff'), - url('/assets/Fonts/din-reg.ttf') format('truetype'), - url('/assets/Fonts/din-reg.svg#din-reg') format('svg'); + src: url(<%= asset_path 'Fonts/din-reg.eot' %>); + src: url(<%= asset_path 'Fonts/din-reg.eot?#iefix' %>) format('embedded-opentype'), + url(<%= asset_path 'Fonts/din-reg.woff' %>) format('woff'), + url(<%= asset_path 'Fonts/din-reg.ttf' %>) format('truetype'), + url(<%= asset_path 'Fonts/din-reg.svg#din-reg' %>) format('svg'); font-weight: normal; font-style: normal; } @@ -134,7 +134,7 @@ width: 40px; height: 32px; background-color: #757575; - background-image: url(home_light.png); + background-image: url(<%= asset_data_uri('home_light.png') %>); background-repeat: no-repeat; background-position: center center; border-top-left-radius: 2px; @@ -142,7 +142,7 @@ float:left; } .homeButton:hover { - background-image: url(home_light.png); + background-image: url(<%= asset_data_uri('home_light.png') %>); } .homeButton a { display:block; @@ -176,7 +176,7 @@ border-top-right-radius: 2px; border-bottom-right-radius: 2px; height: 32px; - background: #4fb5c0 url('search.png') no-repeat center center; + background: #4fb5c0 url(<%= asset_data_uri('search.png') %>) no-repeat center center; background-size: 32px 32px; cursor: pointer; } @@ -288,7 +288,7 @@ .sidebarSearch .tt-dropdown-menu .minimizeResults, .sidebarSearch .tt-dropdown-menu .maximizeResults { width: 32px; height: 32px; - background-image: url(arrowpermswhite_sprite.png); + background-image: url(<%= asset_data_uri('arrowpermswhite_sprite.png') %>); background-repeat: no-repeat; cursor: pointer; position: absolute; @@ -301,7 +301,7 @@ .sidebarSearch .tt-dropdown-menu .maximizeResults { background-position: -32px 0; } -.sidebarSearch .tt-suggestions { +.sidebarSearch .tt-dataset { overflow: visible; } .sidebarSearch .tt-suggestion { @@ -310,7 +310,7 @@ padding: 8px 0; } .sidebarSearch .tt-is-under-cursor, -.sidebarSearch .tt-is-under-mouse-cursor { +.sidebarSearch .tt-suggestion:hover { background: #E0E0E0; } @@ -425,7 +425,7 @@ display:none; width: 24px; height: 24px; - background: url(addtopic_sprite.png); + background: url(<%= asset_data_uri('addtopic_sprite.png') %>); background-repeat: no-repeat; background-size: 48px 24px; top: 12px; @@ -442,7 +442,7 @@ .sidebarSearch div.topicCount { width: 24px; height: 24px; - background: url(topic16.png); + background: url(<%= asset_data_uri('topic16.png') %>); background-repeat: no-repeat; background-position: 0 center; top: 0; @@ -455,7 +455,7 @@ .sidebarSearch div.mapCount { width: 24px; height: 24px; - background: url(metamap16.png); + background: url(<%= asset_data_uri('metamap16.png') %>); background-repeat: no-repeat; background-position: 0 center; left: 0; @@ -466,7 +466,7 @@ .sidebarSearch div.synapseCount { width: 24px; height: 24px; - background: url(synapse16.png); + background: url(<%= asset_data_uri('synapse16.png') %>); background-repeat: no-repeat; background-position: 0 center; top: 24px; @@ -573,7 +573,7 @@ .sidebarSearch div.mapPermission { width: 24px; height: 24px; - background-image: url(permissions32_sprite.png); + background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>); background-repeat: no-repeat; background-size: 72px 48px !important; top: 24px; @@ -665,7 +665,7 @@ .upperRightIcon { width: 32px; height: 32px; - background-image: url('topright_sprite.png'); + background-image: url(<%= asset_data_uri('topright_sprite.png') %>); background-repeat: no-repeat; cursor: pointer; } @@ -816,12 +816,12 @@ } .fullWidthWrapper.withPartners { - background: url(homepage_bg_fade.png) no-repeat center -300px; + background: url(<%= asset_data_uri('homepage_bg_fade.png') %>) no-repeat center -300px; } .homeWrapper.homePartners { padding: 64px 0 280px; height: 96px; - background: url(partner_logos.png) no-repeat 0 64px; + background: url(<%= asset_data_uri('partner_logos.png') %>) no-repeat 0 64px; } .github-fork-ribbon-wrapper { @@ -863,7 +863,7 @@ cursor: pointer; } .openCheatsheet { - background-image: url('help_sprite.png'); + background-image: url(<%= asset_data_uri('help_sprite.png') %>); background-repeat:no-repeat; } .openCheatsheet:hover { @@ -872,7 +872,7 @@ .mapInfoIcon { position: relative; top: 56px; /* puts it just offscreen */ - background-image: url('mapinfo_sprite.png'); + background-image: url(<%= asset_data_uri('mapinfo_sprite.png') %>); background-repeat:no-repeat; } .mapInfoIcon:hover { @@ -918,7 +918,7 @@ .takeScreenshot { margin-bottom: 5px; border-radius: 2px; - background-image: url(screenshot_sprite.png); + background-image: url(<%= asset_data_uri 'screenshot_sprite.png' %>); display: none; } .takeScreenshot:hover { @@ -931,7 +931,7 @@ .zoomExtents { margin-bottom:5px; border-radius: 2px; - background-image: url(extents_sprite.png); + background-image: url(<%= asset_data_uri('extents_sprite.png') %>); } .zoomExtents:hover { @@ -1076,7 +1076,7 @@ } .zoomIn { - background-image: url(zoom_sprite.png); + background-image: url(<%= asset_data_uri('zoom_sprite.png') %>); background-position: 0 /…0; border-top-left-radius: 2px; border-top-right-radius: 2px; @@ -1085,7 +1085,7 @@ background-position: -32px 0; } .zoomOut { - background-image: url(zoom_sprite.png); + background-image: url(<%= asset_data_uri('zoom_sprite.png') %>); background-position:0 -32px; border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; @@ -1163,15 +1163,15 @@ left:5px; } .exploreMapsCenter .myMaps .exploreMapsIcon { - background-image: url(exploremaps_sprite.png); + background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>); background-position: 0 0; } .exploreMapsCenter .activeMaps .exploreMapsIcon { - background-image: url(exploremaps_sprite.png); + background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>); background-position: -32px 0; } .exploreMapsCenter .featuredMaps .exploreMapsIcon { - background-image: url(exploremaps_sprite.png); + background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>); background-position: -64px 0; } .myMaps:hover .exploreMapsIcon, .myMaps.active .exploreMapsIcon { @@ -1212,7 +1212,7 @@ /* feedback */ body a#barometer_tab { -background-image: url(feedback_sprite.png); +background-image: url(<%= asset_path 'feedback_sprite.png' %>); background-position: 0 0; background-color: transparent; height: 110px; @@ -1227,4 +1227,4 @@ box-shadow: 0px 1px 1.5px rgba(0,0,0,0.12), 0 1px 1px rgba(0,0,0,0.24); body a#barometer_tab:hover { background-position: 0 -110px; -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/jquery-ui.css b/app/assets/stylesheets/jquery-ui.css.erb similarity index 88% rename from app/assets/stylesheets/jquery-ui.css rename to app/assets/stylesheets/jquery-ui.css.erb index c89a95ff..88113a6e 100644 --- a/app/assets/stylesheets/jquery-ui.css +++ b/app/assets/stylesheets/jquery-ui.css.erb @@ -243,25 +243,25 @@ body .ui-tooltip { border-width: 2px; } .ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; } .ui-widget .ui-widget { font-size: 1em; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; } -.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; } +.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(<%= asset_data_uri('ui-bg_flat_75_ffffff_40x100.png') %>)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; } .ui-widget-content a { color: #222222/*{fcContent}*/; } -.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; } +.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(<%= asset_data_uri('images/ui-bg_highlight-soft_75_cccccc_1x100.png') %>)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; } .ui-widget-header a { color: #222222/*{fcHeader}*/; } /* Interaction states ----------------------------------*/ -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; } +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(<%= asset_data_uri('images/ui-bg_glass_75_e6e6e6_1x400.png') %>)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; } .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; } -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 0px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 0px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(<%= asset_data_uri('images/ui-bg_glass_75_dadada_1x400.png') %>)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; } .ui-state-hover a, .ui-state-hover a:hover, .ui-state-hover a:link, .ui-state-hover a:visited { color: #212121/*{fcHover}*/; text-decoration: none; } -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(<%= asset_data_uri('images/ui-bg_glass_65_ffffff_1x400.png') %>)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; } .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; } /* Interaction Cues ----------------------------------*/ -.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; } +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(<%= asset_data_uri('images/ui-bg_glass_55_fbf9ee_1x400.png') %>)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; } .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; } -.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(<%= asset_data_uri('images/ui-bg_glass_95_fef1ec_1x400.png') %>)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; } .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; } .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; } .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } @@ -273,14 +273,14 @@ body .ui-tooltip { border-width: 2px; } ----------------------------------*/ /* states and images */ -.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } -.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; } -.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; } -.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; } -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; } -.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; } -.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; } -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; } +.ui-icon { width: 16px; height: 16px; background-image: url(<%= asset_data_uri('images/ui-icons_222222_256x240.png') %>)/*{iconsContent}*/; } +.ui-widget-content .ui-icon {background-image: url(<%= asset_data_uri('images/ui-icons_222222_256x240.png') %>)/*{iconsContent}*/; } +.ui-widget-header .ui-icon {background-image: url(<%= asset_data_uri('images/ui-icons_222222_256x240.png') %>)/*{iconsHeader}*/; } +.ui-state-default .ui-icon { background-image: url(<%= asset_data_uri('images/ui-icons_888888_256x240.png') %>)/*{iconsDefault}*/; } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(<%= asset_data_uri('images/ui-icons_454545_256x240.png') %>)/*{iconsHover}*/; } +.ui-state-active .ui-icon {background-image: url(<%= asset_data_uri('images/ui-icons_454545_256x240.png') %>)/*{iconsActive}*/; } +.ui-state-highlight .ui-icon {background-image: url(<%= asset_data_uri('images/ui-icons_2e83ff_256x240.png') %>)/*{iconsHighlight}*/; } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(<%= asset_data_uri('images/ui-icons_cd0a0a_256x240.png') %>)/*{iconsError}*/; } /* positioning */ .ui-icon-carat-1-n { background-position: 0 0; } @@ -470,5 +470,5 @@ body .ui-tooltip { border-width: 2px; } .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; -khtml-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } /* Overlays */ -.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; } -.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -khtml-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } +.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(<%= asset_data_uri('images/ui-bg_flat_0_aaaaaa_40x100.png') %>)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; } +.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/<%= asset_data_uri('ui-bg_flat_0_aaaaaa_40x100.png') %>)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -khtml-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; } diff --git a/app/assets/stylesheets/jquery.mCustomScrollbar.css b/app/assets/stylesheets/jquery.mCustomScrollbar.css index e318b29c..f799cdc4 100644 --- a/app/assets/stylesheets/jquery.mCustomScrollbar.css +++ b/app/assets/stylesheets/jquery.mCustomScrollbar.css @@ -162,7 +162,7 @@ .mCSB_scrollTools .mCSB_buttonDown, .mCSB_scrollTools .mCSB_buttonLeft, .mCSB_scrollTools .mCSB_buttonRight{ - background-image:url(mCSB_buttons.png); + background-image:url(<%= asset_data_uri('mCSB_buttons.png') %>); background-repeat:no-repeat; opacity:0.4; filter:"alpha(opacity=40)"; -ms-filter:"alpha(opacity=40)"; /* old ie */ @@ -471,4 +471,4 @@ } .mCS-dark-thin>.mCSB_scrollTools .mCSB_buttonRight{ background-position:-80px -56px; -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/uservoice.css b/app/assets/stylesheets/uservoice.css.erb similarity index 85% rename from app/assets/stylesheets/uservoice.css rename to app/assets/stylesheets/uservoice.css.erb index 02b92b9e..df15d675 100644 --- a/app/assets/stylesheets/uservoice.css +++ b/app/assets/stylesheets/uservoice.css.erb @@ -6,7 +6,7 @@ } div.uv-icon.uv-bottom-left { - background-image:url(feedback_sprite.png); + background-image:url(<%= asset_data_uri 'feedback_sprite.png' %>); background-repeat: no-repeat; color:#FFFFFF; cursor:pointer; @@ -22,4 +22,4 @@ div.uv-icon.uv-bottom-left { div.uv-icon.uv-bottom-left:hover { background-position: 0 -110px; -} \ No newline at end of file +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 511ab723..0a3d12ab 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -37,7 +37,7 @@ private end def require_admin - unless authenticated? && user.admin + unless authenticated? && admin? redirect_to root_url, notice: "You need to be an admin for that." return false end diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb index 46747f71..c5535276 100644 --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -68,25 +68,20 @@ class MainController < ApplicationController else search = term.downcase + '%' - if !user - @topics = Topic.where('LOWER("name") like ?', search).where('metacode_id = ?', filterByMetacode.id).order('"name"') - @topics2 = Topic.where('LOWER("name") like ?', '%' + search).where('metacode_id = ?', filterByMetacode.id).order('"name"') - @topics3 = Topic.where('LOWER("desc") like ?', '%' + search).where('metacode_id = ?', filterByMetacode.id).order('"name"') - @topics4 = Topic.where('LOWER("link") like ?', '%' + search).where('metacode_id = ?', filterByMetacode.id).order('"name"') - @topics = @topics + (@topics2 - @topics) - @topics = @topics + (@topics3 - @topics) - @topics = @topics + (@topics4 - @topics) - - elsif user - @topics = Topic.where('LOWER("name") like ?', search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"') - @topics2 = Topic.where('LOWER("name") like ?', '%' + search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"') - @topics3 = Topic.where('LOWER("desc") like ?', '%' + search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"') - @topics4 = Topic.where('LOWER("link") like ?', '%' + search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"') - @topics = @topics + (@topics2 - @topics) - @topics = @topics + (@topics3 - @topics) - @topics = @topics + (@topics4 - @topics) - + if user + @topics = Set.new(Topic.where('LOWER("name") like ?', search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"')) + @topics2 = Set.new(Topic.where('LOWER("name") like ?', '%' + search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"')) + @topics3 = Set.new(Topic.where('LOWER("desc") like ?', '%' + search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"')) + @topics4 = Set.new(Topic.where('LOWER("link") like ?', '%' + search).where('metacode_id = ? AND user_id = ?', filterByMetacode.id, user).order('"name"')) + else + @topics = Set.new(Topic.where('LOWER("name") like ?', search).where('metacode_id = ?', filterByMetacode.id).order('"name"')) + @topics2 = Set.new(Topic.where('LOWER("name") like ?', '%' + search).where('metacode_id = ?', filterByMetacode.id).order('"name"')) + @topics3 = Set.new(Topic.where('LOWER("desc") like ?', '%' + search).where('metacode_id = ?', filterByMetacode.id).order('"name"')) + @topics4 = Set.new(Topic.where('LOWER("link") like ?', '%' + search).where('metacode_id = ?', filterByMetacode.id).order('"name"')) end + + #get unique elements only through the magic of Sets + @topics = (@topics + @topics2 + @topics3 + @topics4).to_a end elsif desc search = '%' + term.downcase + '%' @@ -127,7 +122,7 @@ class MainController < ApplicationController end #read this next line as 'delete a topic if its private and you're either 1. logged out or 2. logged in but not the topic creator - @topics.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } + @topics.to_a.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } render json: autocomplete_array_json(@topics) end @@ -163,7 +158,7 @@ class MainController < ApplicationController end #read this next line as 'delete a map if its private and you're either 1. logged out or 2. logged in but not the map creator - @maps.delete_if {|m| m.permission == "private" && (!authenticated? || (authenticated? && @current.id != m.user_id)) } + @maps.to_a.delete_if {|m| m.permission == "private" && (!authenticated? || (authenticated? && @current.id != m.user_id)) } render json: autocomplete_map_array_json(@maps) end @@ -192,14 +187,14 @@ class MainController < ApplicationController term = params[:term] topic1id = params[:topic1id] topic2id = params[:topic2id] - + if term && !term.empty? - @synapses = Synapse.select('DISTINCT "desc"').where('LOWER("desc") like ?', '%' + term.downcase + '%').order('"desc"') + @synapses = Synapse.where('LOWER("desc") like ?', '%' + term.downcase + '%').order('"desc"') # remove any duplicate synapse types that just differ by # leading or trailing whitespaces collectedDesc = [] - @synapses.delete_if {|s| + @synapses.to_a.uniq(&:desc).delete_if {|s| desc = s.desc == nil || s.desc == "" ? "" : s.desc.strip if collectedDesc.index(desc) == nil collectedDesc.push(desc) @@ -211,23 +206,20 @@ class MainController < ApplicationController #limit to 5 results @synapses = @synapses.slice(0,5) - - render json: autocomplete_synapse_generic_json(@synapses) - elsif topic1id && !topic1id.empty? @one = Synapse.where('node1_id = ? AND node2_id = ?', topic1id, topic2id) @two = Synapse.where('node2_id = ? AND node1_id = ?', topic1id, topic2id) @synapses = @one + @two - @synapses.sort! {|s1,s2| s1.desc <=> s2.desc } + @synapses.sort! {|s1,s2| s1.desc <=> s2.desc }.to_a - #read this next line as 'delete a synapse if its private and you're either 1. logged out or 2. logged in but not the synapse creator - @synapses.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } - - render json: autocomplete_synapse_array_json(@synapses) + #permissions + @synapses.delete_if {|s| s.permission == "private" && !authenticated? } + @synapses.delete_if {|s| s.permission == "private" && authenticated? && @current.id != s.user_id } else @synapses = [] - render json: autocomplete_synapse_array_json(@synapses) end + + render json: autocomplete_synapse_array_json(@synapses) end end diff --git a/app/controllers/mappings_controller.rb b/app/controllers/mappings_controller.rb index b28c7638..27567eb4 100644 --- a/app/controllers/mappings_controller.rb +++ b/app/controllers/mappings_controller.rb @@ -13,7 +13,7 @@ class MappingsController < ApplicationController # POST /mappings.json def create - @mapping = Mapping.new(params[:mapping]) + @mapping = Mapping.new(mapping_params) @mapping.map.touch(:updated_at) @@ -30,7 +30,7 @@ class MappingsController < ApplicationController @mapping.map.touch(:updated_at) - if @mapping.update_attributes(params[:mapping]) + if @mapping.update_attributes(mapping_params) head :no_content else render json: @mapping.errors, status: :unprocessable_entity @@ -48,4 +48,10 @@ class MappingsController < ApplicationController head :no_content end + + private + # Never trust parameters from the scary internet, only allow the white list through. + def mapping_params + params.require(:mapping).permit(:id, :xloc, :yloc, :mappable_id, :mappable_type, :map_id, :user_id) + end end diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index 8f0ced9b..add8a0c1 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -72,14 +72,10 @@ class MapsController < ApplicationController respond_to do |format| format.html { @allmappers = @map.contributors - @alltopics = @map.topics.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } - @allsynapses = @map.synapses.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } - @allmappings = @map.mappings.delete_if {|m| - if m.category == "Synapse" - object = m.synapse - elsif m.category == "Topic" - object = m.topic - end + @alltopics = @map.topics.to_a.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } + @allsynapses = @map.synapses.to_a.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } + @allmappings = @map.mappings.to_a.delete_if {|m| + object = m.mappable !object || (object.permission == "private" && (!authenticated? || (authenticated? && @current.id != object.user_id))) } @@ -100,14 +96,10 @@ class MapsController < ApplicationController end @allmappers = @map.contributors - @alltopics = @map.topics.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } - @allsynapses = @map.synapses.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } - @allmappings = @map.mappings.delete_if {|m| - if m.category == "Synapse" - object = m.synapse - elsif m.category == "Topic" - object = m.topic - end + @alltopics = @map.topics.to_a.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } + @allsynapses = @map.synapses.to_a.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } + @allmappings = @map.mappings.to_a.delete_if {|m| + object = m.mappable !object || (object.permission == "private" && (!authenticated? || (authenticated? && @current.id != object.user_id))) } @@ -141,10 +133,9 @@ class MapsController < ApplicationController @all.each do |topic| topic = topic.split('/') @mapping = Mapping.new() - @mapping.category = "Topic" @mapping.user = @user @mapping.map = @map - @mapping.topic = Topic.find(topic[0]) + @mapping.mappable = Topic.find(topic[0]) @mapping.xloc = topic[1] @mapping.yloc = topic[2] @mapping.save @@ -155,10 +146,9 @@ class MapsController < ApplicationController @synAll = @synAll.split(',') @synAll.each do |synapse_id| @mapping = Mapping.new() - @mapping.category = "Synapse" @mapping.user = @user @mapping.map = @map - @mapping.synapse = Synapse.find(synapse_id) + @mapping.mappable = Synapse.find(synapse_id) @mapping.save end end @@ -180,7 +170,7 @@ class MapsController < ApplicationController respond_to do |format| if !@map format.json { render json: "unauthorized" } - elsif @map.update_attributes(params[:map]) + elsif @map.update_attributes(map_params) format.json { head :no_content } else format.json { render json: @map.errors, status: :unprocessable_entity } @@ -218,15 +208,7 @@ class MapsController < ApplicationController @map = Map.find(params[:id]).authorize_to_delete(@current) - if @map - @mappings = @map.mappings - - @mappings.each do |mapping| - mapping.delete - end - - @map.delete - end + @map.delete if @map respond_to do |format| format.json { @@ -238,4 +220,11 @@ class MapsController < ApplicationController } end end + + private + + # Never trust parameters from the scary internet, only allow the white list through. + def map_params + params.require(:map).permit(:id, :name, :arranged, :desc, :permission, :user_id) + end end diff --git a/app/controllers/metacode_sets_controller.rb b/app/controllers/metacode_sets_controller.rb index 4542f6dd..720076c1 100644 --- a/app/controllers/metacode_sets_controller.rb +++ b/app/controllers/metacode_sets_controller.rb @@ -1,7 +1,7 @@ class MetacodeSetsController < ApplicationController before_filter :require_admin - + # GET /metacode_sets # GET /metacode_sets.json def index @@ -45,7 +45,7 @@ class MetacodeSetsController < ApplicationController # POST /metacode_sets.json def create @user = current_user - @metacode_set = MetacodeSet.new(params[:metacode_set]) + @metacode_set = MetacodeSet.new(metacode_set_params) @metacode_set.user_id = @user.id respond_to do |format| @@ -70,7 +70,7 @@ class MetacodeSetsController < ApplicationController @metacode_set = MetacodeSet.find(params[:id]) respond_to do |format| - if @metacode_set.update_attributes(params[:metacode_set]) + if @metacode_set.update_attributes(metacode_set_params) # build an array of the IDs of the metacodes currently in the set @currentMetacodes = @metacode_set.metacodes.map{ |m| m.id.to_s } @@ -116,4 +116,11 @@ class MetacodeSetsController < ApplicationController format.json { head :no_content } end end + + private + + def metacode_set_params + params.require(:metacode_set).permit(:desc, :mapperContributed, :name) + end + end diff --git a/app/controllers/metacodes_controller.rb b/app/controllers/metacodes_controller.rb index 810981dc..7a14f2a8 100644 --- a/app/controllers/metacodes_controller.rb +++ b/app/controllers/metacodes_controller.rb @@ -5,8 +5,10 @@ class MetacodesController < ApplicationController # GET /metacodes # GET /metacodes.json def index - @metacodes = Metacode.order("name").all + @metacodes.map do |metacode| + metacode.icon = ActionController::Base.helpers.asset_path(metacode.icon) + end respond_to do |format| format.html { @@ -51,7 +53,7 @@ class MetacodesController < ApplicationController # POST /metacodes # POST /metacodes.json def create - @metacode = Metacode.new(params[:metacode]) + @metacode = Metacode.new(metacode_params) respond_to do |format| if @metacode.save @@ -70,7 +72,7 @@ class MetacodesController < ApplicationController @metacode = Metacode.find(params[:id]) respond_to do |format| - if @metacode.update_attributes(params[:metacode]) + if @metacode.update_attributes(metacode_params) format.html { redirect_to metacodes_url, notice: 'Metacode was successfully updated.' } format.json { head :no_content } else @@ -93,4 +95,11 @@ class MetacodesController < ApplicationController # format.json { head :no_content } # end # end + + private + + # Never trust parameters from the scary internet, only allow the white list through. + def metacode_params + params.require(:metacode).permit(:id, :name, :icon, :color) + end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 0f0be9ba..5fff2f1c 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -7,4 +7,4 @@ class Users::RegistrationsController < Devise::RegistrationsController def after_update_path_for(resource) signed_in_root_path(resource) end -end \ No newline at end of file +end diff --git a/app/controllers/synapses_controller.rb b/app/controllers/synapses_controller.rb index 6ff1537b..dc36ff28 100644 --- a/app/controllers/synapses_controller.rb +++ b/app/controllers/synapses_controller.rb @@ -21,7 +21,8 @@ class SynapsesController < ApplicationController # POST /synapses # POST /synapses.json def create - @synapse = Synapse.new(params[:synapse]) + @synapse = Synapse.new(synapse_params) + @synapse.update_attribute :desc, "" if @synapse.desc.nil? respond_to do |format| if @synapse.save @@ -36,9 +37,10 @@ class SynapsesController < ApplicationController # PUT /synapses/1.json def update @synapse = Synapse.find(params[:id]) + @synapse.update_attribute :desc, "" if @synapse.desc.nil? respond_to do |format| - if @synapse.update_attributes(params[:synapse]) + if @synapse.update_attributes(synapse_params) format.json { head :no_content } else format.json { render json: @synapse.errors, status: :unprocessable_entity } @@ -50,18 +52,16 @@ class SynapsesController < ApplicationController def destroy @current = current_user @synapse = Synapse.find(params[:id]).authorize_to_delete(@current) - - if @synapse - @synapse.mappings.each do |m| - m.map.touch(:updated_at) - m.delete - end - - @synapse.delete - end + @synapse.delete if @synapse respond_to do |format| format.json { head :no_content } end end + + private + + def synapse_params + params.require(:synapse).permit(:id, :desc, :category, :weight, :permission, :node1_id, :node2_id, :user_id) + end end diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index ca24d1fb..d73be190 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -10,12 +10,11 @@ class TopicsController < ApplicationController @current = current_user term = params[:term] if term && !term.empty? - # !connor term here needs to have .downcase @topics = Topic.where('LOWER("name") like ?', term.downcase + '%').order('"name"') #read this next line as 'delete a topic if its private and you're either #1. logged out or 2. logged in but not the topic creator - @topics.delete_if {|t| t.permission == "private" && + @topics.to_a.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } else @topics = [] @@ -35,7 +34,7 @@ class TopicsController < ApplicationController respond_to do |format| format.html { @alltopics = ([@topic] + @topic.relatives).delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } # should limit to topics visible to user - @allsynapses = @topic.synapses.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } + @allsynapses = @topic.synapses.to_a.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } @allcreators = [] @alltopics.each do |t| @@ -64,8 +63,8 @@ class TopicsController < ApplicationController redirect_to root_url, notice: "Access denied. That topic is private." and return end - @alltopics = @topic.relatives.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } - @allsynapses = @topic.synapses.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } + @alltopics = @topic.relatives.to_a.delete_if {|t| t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id)) } + @allsynapses = @topic.synapses.to_a.delete_if {|s| s.permission == "private" && (!authenticated? || (authenticated? && @current.id != s.user_id)) } @allcreators = [] @allcreators.push(@topic.user) @alltopics.each do |t| @@ -101,7 +100,7 @@ class TopicsController < ApplicationController @topicsAlreadyHas = params[:network] ? params[:network].split(',') : [] - @alltopics = @topic.relatives.delete_if {|t| + @alltopics = @topic.relatives.to_a.delete_if {|t| @topicsAlreadyHas.index(t.id.to_s) != nil || (t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id))) } @@ -133,7 +132,7 @@ class TopicsController < ApplicationController @topicsAlreadyHas = params[:network] ? params[:network].split(',') : [] - @alltopics = @topic.relatives.delete_if {|t| + @alltopics = @topic.relatives.to_a.delete_if {|t| @topicsAlreadyHas.index(t.id.to_s) != nil || (params[:metacode] && t.metacode_id.to_s != params[:metacode]) || (t.permission == "private" && (!authenticated? || (authenticated? && @current.id != t.user_id))) @@ -141,7 +140,7 @@ class TopicsController < ApplicationController @alltopics.uniq! - @allsynapses = @topic.synapses.delete_if {|s| + @allsynapses = @topic.synapses.to_a.delete_if {|s| (s.topic1 == @topic && @alltopics.index(s.topic2) == nil) || (s.topic2 == @topic && @alltopics.index(s.topic1) == nil) } @@ -172,7 +171,7 @@ class TopicsController < ApplicationController # POST /topics # POST /topics.json def create - @topic = Topic.new(params[:topic]) + @topic = Topic.new(topic_params) respond_to do |format| if @topic.save @@ -189,7 +188,7 @@ class TopicsController < ApplicationController @topic = Topic.find(params[:id]) respond_to do |format| - if @topic.update_attributes(params[:topic]) + if @topic.update_attributes(topic_params) format.json { head :no_content } else format.json { render json: @topic.errors, status: :unprocessable_entity } @@ -201,36 +200,16 @@ class TopicsController < ApplicationController def destroy @current = current_user @topic = Topic.find(params[:id]).authorize_to_delete(@current) - - if @topic - @synapses = @topic.synapses - @mappings = @topic.mappings - - @synapses.each do |synapse| - synapse.mappings.each do |m| - - @map = m.map - @map.touch(:updated_at) - - m.delete - end - - synapse.delete - end - - @mappings.each do |mapping| - - @map = mapping.map - @map.touch(:updated_at) - - mapping.delete - end - - @topic.delete - end + @topic.delete if @topic respond_to do |format| format.json { head :no_content } end end + + private + + def topic_params + params.require(:topic).permit(:id, :name, :desc, :link, :permission, :user_id, :metacode_id) + end end diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb index d1405f6a..ae5517e8 100644 --- a/app/controllers/users/passwords_controller.rb +++ b/app/controllers/users/passwords_controller.rb @@ -3,4 +3,4 @@ class Users::PasswordsController < Devise::PasswordsController def after_resetting_password_path_for(resource) signed_in_root_path(resource) end -end \ No newline at end of file +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 58ef2d96..52996f49 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,7 +3,7 @@ class UsersController < ApplicationController before_filter :require_user, only: [:edit, :update, :updatemetacodes] respond_to :html, :json - + # GET /users/1.json def show @user = User.find(params[:id]) @@ -98,4 +98,11 @@ class UsersController < ApplicationController end end + private + + def user_params + params.require(:user).permit(:name, :email, :image, :password, + :password_confirmation, :code, :joinedwithcode, :remember_me) + end + end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 95f6ba29..fa7876f5 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,18 @@ module ApplicationHelper + def get_metacodeset + @m = user.settings.metacodes + set = @m[0].include?("metacodeset") ? MetacodeSet.find(@m[0].sub("metacodeset-","").to_i) : false + return set + end + + def user_metacodes + @m = user.settings.metacodes + set = get_metacodeset + if set + @metacodes = set.metacodes + else + @metacodes = Metacode.where(id: @m).to_a + end + @metacodes.sort! {|m1,m2| m2.name.downcase <=> m1.name.downcase }.rotate!(-1) + end end diff --git a/app/helpers/maps_helper.rb b/app/helpers/maps_helper.rb index 0c9b3a08..6b3dd7b0 100644 --- a/app/helpers/maps_helper.rb +++ b/app/helpers/maps_helper.rb @@ -16,7 +16,7 @@ module MapsHelper map['rtype'] = "map" contributorTip = '' - firstContributorImage = '/assets/user.png' + firstContributorImage = asset_path('user.png') if m.contributors.count > 0 firstContributorImage = m.contributors[0].image.url(:thirtytwo) m.contributors.each_with_index do |c, index| diff --git a/app/helpers/topics_helper.rb b/app/helpers/topics_helper.rb index 482a663c..362a5f46 100644 --- a/app/helpers/topics_helper.rb +++ b/app/helpers/topics_helper.rb @@ -10,7 +10,7 @@ module TopicsHelper topic['value'] = t.name topic['description'] = t.desc.truncate(70) # make this return matched results topic['type'] = t.metacode.name - topic['typeImageURL'] = t.metacode.icon + topic['typeImageURL'] = t.metacode.asset_path_icon topic['permission'] = t.permission topic['mapCount'] = t.maps.count topic['synapseCount'] = t.synapses.count diff --git a/app/models/in_metacode_set.rb b/app/models/in_metacode_set.rb index 3f608587..117033d6 100644 --- a/app/models/in_metacode_set.rb +++ b/app/models/in_metacode_set.rb @@ -1,5 +1,4 @@ class InMetacodeSet < ActiveRecord::Base belongs_to :metacode, :class_name => "Metacode", :foreign_key => "metacode_id" belongs_to :metacode_set, :class_name => "MetacodeSet", :foreign_key => "metacode_set_id" - # attr_accessible :title, :body end diff --git a/app/models/map.rb b/app/models/map.rb index 3bf5a4a6..eab88814 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -2,18 +2,17 @@ class Map < ActiveRecord::Base belongs_to :user - has_many :topicmappings, :class_name => 'Mapping', :conditions => {:category => 'Topic'} - has_many :synapsemappings, :class_name => 'Mapping', :conditions => {:category => 'Synapse'} - - has_many :topics, :through => :topicmappings - has_many :synapses, :through => :synapsemappings + has_many :topicmappings, -> { Mapping.topicmapping }, class_name: :Mapping, dependent: :destroy + has_many :synapsemappings, -> { Mapping.synapsemapping }, class_name: :Mapping, dependent: :destroy + has_many :topics, through: :topicmappings, source: :mappable, source_type: "Topic" + has_many :synapses, through: :synapsemappings, source: :mappable, source_type: "Synapse" # This method associates the attribute ":image" with a file attachment has_attached_file :screenshot, :styles => { :thumb => ['188x126#', :png] #:full => ['940x630#', :png] }, - :default_url => "/assets/missing-map.png" + :default_url => ActionController::Base.helpers.asset_path('missing-map.png') # Validate the attached image is image/jpg, image/png, etc validates_attachment_content_type :screenshot, :content_type => /\Aimage\/.*\Z/ diff --git a/app/models/mapping.rb b/app/models/mapping.rb index dc50730c..318aa5cf 100644 --- a/app/models/mapping.rb +++ b/app/models/mapping.rb @@ -1,7 +1,10 @@ class Mapping < ActiveRecord::Base - belongs_to :topic, :class_name => "Topic", :foreign_key => "topic_id" - belongs_to :synapse, :class_name => "Synapse", :foreign_key => "synapse_id" + scope :topicmapping, -> { where(mappable_type: :Topic) } + scope :synapsemapping, -> { where(mappable_type: :Synapse) } + + belongs_to :mappable, polymorphic: true + belongs_to :map, :class_name => "Map", :foreign_key => "map_id" belongs_to :user diff --git a/app/models/metacode.rb b/app/models/metacode.rb index 315f7800..a1184e9c 100644 --- a/app/models/metacode.rb +++ b/app/models/metacode.rb @@ -14,4 +14,18 @@ class Metacode < ActiveRecord::Base return false end -end \ No newline at end of file + def asset_path_icon + if icon.start_with?('http') + icon + else + ActionController::Base.helpers.asset_path icon + end + end + + #output json with asset_paths merged in + def as_json(options) + json = super(options.merge!(methods: :asset_path_icon)) + json["icon"] = json["asset_path_icon"] + json.except("asset_path_icon") + end +end diff --git a/app/models/metacode_set.rb b/app/models/metacode_set.rb index 6798e568..d06de1e4 100644 --- a/app/models/metacode_set.rb +++ b/app/models/metacode_set.rb @@ -1,6 +1,5 @@ class MetacodeSet < ActiveRecord::Base belongs_to :user - attr_accessible :desc, :mapperContributed, :name has_many :in_metacode_sets has_many :metacodes, :through => :in_metacode_sets end diff --git a/app/models/synapse.rb b/app/models/synapse.rb index bf3bdab2..2745106f 100644 --- a/app/models/synapse.rb +++ b/app/models/synapse.rb @@ -5,9 +5,11 @@ class Synapse < ActiveRecord::Base belongs_to :topic1, :class_name => "Topic", :foreign_key => "node1_id" belongs_to :topic2, :class_name => "Topic", :foreign_key => "node2_id" - has_many :mappings + has_many :mappings, as: :mappable, dependent: :destroy has_many :maps, :through => :mappings + validates :desc, length: { minimum: 0, allow_nil: false } + def user_name self.user.name end diff --git a/app/models/topic.rb b/app/models/topic.rb index 078c633e..f82bc256 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -3,12 +3,12 @@ class Topic < ActiveRecord::Base belongs_to :user - has_many :synapses1, :class_name => 'Synapse', :foreign_key => 'node1_id' - has_many :synapses2, :class_name => 'Synapse', :foreign_key => 'node2_id' + has_many :synapses1, :class_name => 'Synapse', :foreign_key => 'node1_id', dependent: :destroy + has_many :synapses2, :class_name => 'Synapse', :foreign_key => 'node2_id', dependent: :destroy has_many :topics1, :through => :synapses2, :source => :topic1 has_many :topics2, :through => :synapses1, :source => :topic2 - has_many :mappings + has_many :mappings, as: :mappable, dependent: :destroy has_many :maps, :through => :mappings # This method associates the attribute ":image" with a file attachment diff --git a/app/models/user.rb b/app/models/user.rb index 2e738134..effa6ec5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,8 +11,6 @@ class User < ActiveRecord::Base devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable - attr_accessible :name, :email, :image, :password, :password_confirmation, :code, :joinedwithcode, :remember_me - serialize :settings, UserPreference validates :password, :presence => true, @@ -37,7 +35,7 @@ class User < ActiveRecord::Base :ninetysix => ['96x96#', :png], :onetwentyeight => ['128x128#', :png] }, - :default_url => "/assets/user.png" + :default_url => ActionController::Base.helpers.asset_path('user.png') # Validate the attached image is image/jpg, image/png, etc validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/ diff --git a/app/views/layouts/_templates.html.erb b/app/views/layouts/_templates.html.erb index afd9de52..4218bfda 100644 --- a/app/views/layouts/_templates.html.erb +++ b/app/views/layouts/_templates.html.erb @@ -129,7 +129,7 @@ @@ -234,7 +236,7 @@
    {{{metacode_select}}}
    - +
    {{username}}
    @@ -267,4 +269,4 @@
    -
    \ No newline at end of file +
    diff --git a/app/views/main/home.html.erb b/app/views/main/home.html.erb index 2590f2f5..b40fb2a4 100644 --- a/app/views/main/home.html.erb +++ b/app/views/main/home.html.erb @@ -50,4 +50,4 @@ Metamaps.GlobalUI.Search.isOpen = true; Metamaps.GlobalUI.Search.lock(); -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/maps/_mapinfobox.html.erb b/app/views/maps/_mapinfobox.html.erb index 4305b3a7..1fa6da02 100644 --- a/app/views/maps/_mapinfobox.html.erb +++ b/app/views/maps/_mapinfobox.html.erb @@ -8,12 +8,12 @@ <%= @map && @map.permission != 'private' ? " shareable" : "" %>"> <% if @map %> -
    <%= best_in_place @map, :name, :type => :textarea, :activator => "#mapInfoName", :classes => 'best_in_place_name' %>
    +
    <%= best_in_place @map, :name, :as => :textarea, :activator => "#mapInfoName", :class => 'best_in_place_name' %>
    <% if @map.contributors.count == 0 %> - + <% elsif @map.contributors.count == 1 %> <% elsif @map.contributors.count == 2 %> @@ -23,7 +23,7 @@ <% end %> <%= @map.contributors.count %>
    @@ -42,7 +42,7 @@
    <% if (authenticated? && @map.authorize_to_edit(user)) || (!authenticated? && @map.desc != "" && @map.desc != nil )%> - <%= best_in_place @map, :desc, :activator => "#mapInfoDesc", :type => :textarea, :nil => "Click to add description...", :classes => 'best_in_place_desc' %> + <%= best_in_place @map, :desc, :activator => "#mapInfoDesc", :as => :textarea, :placeholder => "Click to add description...", :class => 'best_in_place_desc' %> <% end %>
    @@ -61,4 +61,4 @@
    <% end %> -
    \ No newline at end of file +
    diff --git a/app/views/maps/_newtopic.html.erb b/app/views/maps/_newtopic.html.erb index f22920bb..e5263d76 100644 --- a/app/views/maps/_newtopic.html.erb +++ b/app/views/maps/_newtopic.html.erb @@ -1,19 +1,10 @@ <%= form_for Topic.new, url: topics_url, remote: true do |form| %>
    - <% @m = user.settings.metacodes %> - <% set = @m[0].include?("metacodeset") ? MetacodeSet.find(@m[0].sub("metacodeset-","").to_i) : false %> - <% if set %> - <% @metacodes = set.metacodes %> - <% else %> - <% @metacodes = [] %> - <% @m.each do |m| %> - <% @metacodes.push(Metacode.find(m.to_i)) %> - <% end %> - <% end %> - <% @metacodes.sort! {|m1,m2| m2.name.downcase <=> m1.name.downcase }.rotate!(-1) %> + <% @metacodes = user_metacodes() %> + <% set = get_metacodeset() %> <% @metacodes.each do |metacode| %> - <%= metacode.name %> + <%= metacode.name %> <% end %>
    <%= form.text_field :name, :maxlength => 140, :placeholder => "title..." %> diff --git a/app/views/metacode_sets/_form.html.erb b/app/views/metacode_sets/_form.html.erb index 3a5089b9..f7ef60c6 100644 --- a/app/views/metacode_sets/_form.html.erb +++ b/app/views/metacode_sets/_form.html.erb @@ -37,7 +37,7 @@ <% while $i < (Metacode.all.length / 4) do %>
  • class="toggledOff"<% end %> onclick="Metamaps.Admin.liClickHandler.call(this);"> - <%= @m[$i].name %> + <%= @m[$i].name %>

    <%= @m[$i].name.downcase %>

  • @@ -48,7 +48,7 @@ <% while $i < (Metacode.all.length / 4 * 2) do %>
  • class="toggledOff"<% end %> onclick="Metamaps.Admin.liClickHandler.call(this);"> - <%= @m[$i].name %> + <%= @m[$i].name %>

    <%= @m[$i].name.downcase %>

  • @@ -59,7 +59,7 @@ <% while $i < (Metacode.all.length / 4 * 3) do %>
  • class="toggledOff"<% end %> onclick="Metamaps.Admin.liClickHandler.call(this);"> - <%= @m[$i].name %> + <%= @m[$i].name %>

    <%= @m[$i].name.downcase %>

  • @@ -70,7 +70,7 @@ <% while $i < Metacode.all.length do %>
  • class="toggledOff"<% end %> onclick="Metamaps.Admin.liClickHandler.call(this);"> - <%= @m[$i].name %> + <%= @m[$i].name %>

    <%= @m[$i].name.downcase %>

  • @@ -86,4 +86,4 @@ { :class => 'button', 'data-bypass' => 'true' } %> <%= f.submit :class => 'add', :onclick => "return Metamaps.Admin.validate();" %>
    -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/metacode_sets/index.html.erb b/app/views/metacode_sets/index.html.erb index 7a93d97f..8f6aa810 100644 --- a/app/views/metacode_sets/index.html.erb +++ b/app/views/metacode_sets/index.html.erb @@ -23,7 +23,7 @@ <%= metacode_set.desc %> <% metacode_set.metacodes.each_with_index do |metacode, index| %> - + <% if (index+1)%4 == 0 %>
    <% end %> @@ -34,4 +34,4 @@ <% end %>
    - \ No newline at end of file + diff --git a/app/views/metacodes/index.html.erb b/app/views/metacodes/index.html.erb index c99634d4..4f3563f1 100644 --- a/app/views/metacodes/index.html.erb +++ b/app/views/metacodes/index.html.erb @@ -14,7 +14,7 @@ <% @metacodes.each do |metacode| %> <%= metacode.name %> - <%= metacode.icon %> + <%= asset_path metacode.icon %> <% if metacode.color %> <%= metacode.color %> @@ -22,10 +22,10 @@ <% else %> <% end %> - + <%= link_to 'Edit', edit_metacode_path(metacode), :data => { :bypass => 'true'} %> <% end %> - \ No newline at end of file + diff --git a/app/views/metacodes/new.html.erb b/app/views/metacodes/new.html.erb index 8520bb6c..e10f28d1 100644 --- a/app/views/metacodes/new.html.erb +++ b/app/views/metacodes/new.html.erb @@ -2,4 +2,4 @@
    <%= render 'form' %>
    - \ No newline at end of file + diff --git a/app/views/shared/_cheatsheet.html.erb b/app/views/shared/_cheatsheet.html.erb index f0a2ffbc..7332a1c7 100644 --- a/app/views/shared/_cheatsheet.html.erb +++ b/app/views/shared/_cheatsheet.html.erb @@ -61,7 +61,7 @@ Change Topic permission: Click on 'Permission' icon (only for topic creator)
    - Open Topic view: Click on icon within topic card bar + Open Topic view: Click on icon within topic card bar
    Close 'Topic' card: Click on canvas diff --git a/app/views/shared/_filterBox.html.erb b/app/views/shared/_filterBox.html.erb index b01f6eca..9ea4926b 100644 --- a/app/views/shared/_filterBox.html.erb +++ b/app/views/shared/_filterBox.html.erb @@ -70,13 +70,13 @@ @metacodes.each_with_index do |metacode, index| @metacodelist += '
  • ' - @metacodelist += '' + metacode.name + '' + @metacodelist += '' + metacode.name + '' @metacodelist += '

    ' + metacode.name.downcase + '

  • ' end @synapses.each_with_index do |synapse, index| d = synapse.desc || "" @synapselist += '
  • ' - @synapselist += 'synapse icon

    ' + d + @synapselist += 'synapse icon

    ' + d @synapselist += '

  • ' end @mappers.each_with_index do |mapper, index| diff --git a/app/views/shared/_metacodeoptions.html.erb b/app/views/shared/_metacodeoptions.html.erb index f45a1aa6..a6092c3e 100644 --- a/app/views/shared/_metacodeoptions.html.erb +++ b/app/views/shared/_metacodeoptions.html.erb @@ -12,7 +12,7 @@ -
    \ No newline at end of file + diff --git a/app/views/shared/_switchmetacodes.html.erb b/app/views/shared/_switchmetacodes.html.erb index fb3962ef..067d4d6b 100644 --- a/app/views/shared/_switchmetacodes.html.erb +++ b/app/views/shared/_switchmetacodes.html.erb @@ -28,7 +28,7 @@ data-metacodes="<%= m.metacodes.map(&:id).join(',') %>"> <% @list = '' %> <% m.metacodes.sort{|x,y| x.name <=> y.name }.each_with_index do |m, index| %> - <% @list += '
  • ' + m.name + '

    ' + m.name.downcase + '

  • ' %> + <% @list += '
  • ' + m.name + '

    ' + m.name.downcase + '

  • ' %> <% end %>

    <%= m.desc %>

    @@ -53,7 +53,7 @@ <% else %> <% mClass = "toggledOff" %> <% end %> - <% @list += '
  • ' + m.name + '

    ' + m.name.downcase + '

  • ' %> + <% @list += '
  • ' + m.name + '

    ' + m.name.downcase + '

  • ' %> <% end %>
    @@ -73,4 +73,4 @@ \ No newline at end of file + diff --git a/app/views/topics/_new.html.erb b/app/views/topics/_new.html.erb index 7aa13eb3..49b6a9a8 100644 --- a/app/views/topics/_new.html.erb +++ b/app/views/topics/_new.html.erb @@ -20,7 +20,7 @@ <% end %> <% @metacodes.sort! {|m1,m2| m2.name.downcase <=> m1.name.downcase }.rotate!(-1) %> <% @metacodes.each do |metacode| %> - <%= metacode.name %> + <%= metacode.name %> <% end %>
    <%= form.text_field :name, :maxlength => 140, :placeholder => "title..." %> diff --git a/config/application.rb b/config/application.rb index 72ca47ea..f1476b8e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -2,12 +2,7 @@ require File.expand_path('../boot', __FILE__) require 'rails/all' -if defined?(Bundler) - # If you precompile assets before deploying to production, use this line - Bundler.require(*Rails.groups(:assets => %w(development test))) - # If you want your assets lazily compiled in production, use this line - # Bundler.require(:default, :assets, Rails.env) -end +Bundler.require(:default, Rails.env) module Metamaps class Application < Rails::Application @@ -44,14 +39,7 @@ module Metamaps # like if you have constraints or database-specific column types # config.active_record.schema_format = :sql - # Enforce whitelist mode for mass assignment. - # This will create an empty whitelist of attributes available for mass-assignment for all models - # in your app. As such, your models will need to explicitly whitelist or blacklist accessible - # parameters by using an attr_accessible or attr_protected declaration. - # config.active_record.whitelist_attributes = true - # Enable the asset pipeline - config.assets.enabled = true config.assets.initialize_on_precompile = false # Version of your assets, change this if you want to expire all your assets @@ -60,5 +48,6 @@ module Metamaps config.generators do |g| g.test_framework :rspec end + config.active_record.raise_in_transactional_callbacks = true end end diff --git a/config/boot.rb b/config/boot.rb index 4489e586..0f0d7c60 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,12 @@ require 'rubygems' +require 'rails/commands/server' +module Rails + class Server + def default_options + super.merge(Host: '0.0.0.0', Port: 3000) + end + end +end # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) diff --git a/config/environments/development.rb b/config/environments/development.rb index 74431de7..cd440097 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,14 +1,14 @@ Metamaps::Application.configure do # Settings specified here will take precedence over those in config/application.rb + config.log_level = :info + config.eager_load = false + # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false - # Log error messages when you accidentally call methods on nil. - config.whiny_nils = true - # Show full error reports and disable caching config.consider_all_requests_local = true config.action_controller.perform_caching = false @@ -40,19 +40,6 @@ Metamaps::Application.configure do # Print deprecation notices to the Rails logger config.active_support.deprecation = :log - # Only use best-standards-support built into browsers - config.action_dispatch.best_standards_support = :builtin - - # Raise exception on mass assignment protection for Active Record models - config.active_record.mass_assignment_sanitizer = :strict - - # Log the query plan for queries taking more than this (works - # with SQLite, MySQL, and PostgreSQL) - config.active_record.auto_explain_threshold_in_seconds = 0.5 - - # Do not compress assets - config.assets.compress = false - # Expands the lines which load the assets config.assets.debug = true end diff --git a/config/environments/production.rb b/config/environments/production.rb index 2186327f..192f631d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,6 +1,10 @@ Metamaps::Application.configure do # Settings specified here will take precedence over those in config/application.rb + config.log_level = :warn + config.eager_load = true + config.assets.js_compressor = :uglifier + # Code is not reloaded between requests config.cache_classes = true @@ -9,7 +13,9 @@ Metamaps::Application.configure do config.action_controller.perform_caching = true # Disable Rails's static asset server (Apache or nginx will already do this) - config.serve_static_assets = false + config.serve_static_files = true + + config.assets.compile = false # Compress JavaScripts and CSS config.assets.compress = true diff --git a/config/environments/test.rb b/config/environments/test.rb index 8a7b408c..73003840 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,6 +1,8 @@ Metamaps::Application.configure do # Settings specified here will take precedence over those in config/application.rb + config.eager_load = false + # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -11,9 +13,6 @@ Metamaps::Application.configure do config.serve_static_assets = true config.static_cache_control = "public, max-age=3600" - # Log error messages when you accidentally call methods on nil - config.whiny_nils = true - # Show full error reports and disable caching config.consider_all_requests_local = true config.action_controller.perform_caching = false @@ -29,9 +28,12 @@ Metamaps::Application.configure do # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - # Raise exception on mass assignment protection for Active Record models - config.active_record.mass_assignment_sanitizer = :strict - # Print deprecation notices to the stderr config.active_support.deprecation = :stderr + + #assets config + config.assets.compile = true + config.assets.compress = false + config.assets.debug = false + config.assets.digest = false end diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index b9f46222..9c1fb05e 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -4,4 +4,4 @@ # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -Metamaps::Application.config.secret_token = '267c8a84f63963282f45bc3010eaddf027abfab58fc759d6e239c8005f85ee99d6d01b1ab6394cdee9ca7f8c9213a0cf91d3d8d3350f096123e2caccbcc0924f' +Metamaps::Application.config.secret_key_base = '267c8a84f63963282f45bc3010eaddf027abfab58fc759d6e239c8005f85ee99d6d01b1ab6394cdee9ca7f8c9213a0cf91d3d8d3350f096123e2caccbcc0924f' diff --git a/config/routes.rb b/config/routes.rb index 494992a6..e80b837a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,12 +2,12 @@ Metamaps::Application.routes.draw do root to: 'main#home', via: :get - match 'request', to: 'main#requestinvite', via: :get, as: :request + get 'request', to: 'main#requestinvite', as: :request - match 'search/topics', to: 'main#searchtopics', via: :get, as: :searchtopics - match 'search/maps', to: 'main#searchmaps', via: :get, as: :searchmaps - match 'search/mappers', to: 'main#searchmappers', via: :get, as: :searchmappers - match 'search/synapses', to: 'main#searchsynapses', via: :get, as: :searchsynapses + get 'search/topics', to: 'main#searchtopics', as: :searchtopics + get 'search/maps', to: 'main#searchmaps', as: :searchmaps + get 'search/mappers', to: 'main#searchmappers', as: :searchmappers + get 'search/synapses', to: 'main#searchsynapses', as: :searchsynapses resources :mappings, except: [:index, :new, :edit] resources :metacode_sets, :except => [:show] @@ -16,28 +16,28 @@ Metamaps::Application.routes.draw do resources :topics, except: [:index, :new, :edit] do get :autocomplete_topic, :on => :collection end - match 'topics/:id/network', to: 'topics#network', via: :get, as: :network - match 'topics/:id/relative_numbers', to: 'topics#relative_numbers', via: :get, as: :relative_numbers - match 'topics/:id/relatives', to: 'topics#relatives', via: :get, as: :relatives + get 'topics/:id/network', to: 'topics#network', as: :network + get 'topics/:id/relative_numbers', to: 'topics#relative_numbers', as: :relative_numbers + get 'topics/:id/relatives', to: 'topics#relatives', as: :relatives - match 'explore/active', to: 'maps#index', via: :get, as: :activemaps - match 'explore/featured', to: 'maps#index', via: :get, as: :featuredmaps - match 'explore/mine', to: 'maps#index', via: :get, as: :mymaps - match 'explore/mapper/:id', to: 'maps#index', via: :get, as: :usermaps + get 'explore/active', to: 'maps#index', as: :activemaps + get 'explore/featured', to: 'maps#index', as: :featuredmaps + get 'explore/mine', to: 'maps#index', as: :mymaps + get 'explore/mapper/:id', to: 'maps#index', as: :usermaps resources :maps, except: [:new, :edit] - match 'maps/:id/contains', to: 'maps#contains', via: :get, as: :contains - match 'maps/:id/upload_screenshot', to: 'maps#screenshot', via: :post, as: :screenshot + get 'maps/:id/contains', to: 'maps#contains', as: :contains + get 'maps/:id/upload_screenshot', to: 'maps#screenshot', as: :screenshot - devise_for :users, controllers: { registrations: 'users/registrations', passwords: 'users/passwords', sessions: 'devise/sessions' }, :skip => [:sessions] + devise_for :users, controllers: { registrations: 'users/registrations', passwords: 'users/passwords', sessions: 'devise/sessions' }, :skip => :sessions devise_scope :user do get 'login' => 'devise/sessions#new', :as => :new_user_session post 'login' => 'devise/sessions#create', :as => :user_session get 'logout' => 'devise/sessions#destroy', :as => :destroy_user_session - get 'join' => 'devise/registrations#new', :as => :new_user_registration + get 'join' => 'devise/registrations#new', :as => :new_user_registration_path end - match 'users/:id/details', to: 'users#details', via: :get, as: :details - match 'user/updatemetacodes', to: 'users#updatemetacodes', via: :post, as: :updatemetacodes + get 'users/:id/details', to: 'users#details', as: :details + post 'user/updatemetacodes', to: 'users#updatemetacodes', as: :updatemetacodes resources :users, except: [:index, :destroy] end diff --git a/config/secrets.yml.default b/config/secrets.yml.default new file mode 100644 index 00000000..6859262d --- /dev/null +++ b/config/secrets.yml.default @@ -0,0 +1,12 @@ +defaults: &defaults + missing_map_png_url: '/assets/missing-map.png' + user_png_url: '/assets/user.png' + +development: + <<: *defaults + +test: + <<: *defaults + +production: + <<: *defaults diff --git a/db/migrate/20151001024122_mapping_polymorphism.rb b/db/migrate/20151001024122_mapping_polymorphism.rb new file mode 100644 index 00000000..e41233f6 --- /dev/null +++ b/db/migrate/20151001024122_mapping_polymorphism.rb @@ -0,0 +1,32 @@ +class MappingPolymorphism < ActiveRecord::Migration + def up + add_column :mappings, :mappable_id, :integer + add_column :mappings, :mappable_type, :string + add_index :mappings, [:mappable_id, :mappable_type] + + Mapping.find_each do |mapping| + if mapping.synapse_id.nil? and mapping.topic_id.nil? + puts "Mapping id=#{mapping.id} has no valid id, skipping!" + next + end + if not mapping.synapse_id.nil? and not mapping.topic_id.nil? + puts "Mapping id=#{mapping.id} has both topic and synapse ids, skipping!" + next + end + + unless mapping.synapse_id.nil? + mapping.mappable = Synapse.find(mapping.synapse_id) + else + next if mapping.topic_id == 0 + mapping.mappable = Topic.find(mapping.topic_id) + end + mapping.save + end + end + + def down + remove_index :mappings, [:mappable_id, :mappable_type] + remove_column :mappings, :mappable_id, :integer + remove_column :mappings, :mappable_type, :string + end +end diff --git a/db/migrate/20151023143719_add_missing_indexes.rb b/db/migrate/20151023143719_add_missing_indexes.rb new file mode 100644 index 00000000..551f2319 --- /dev/null +++ b/db/migrate/20151023143719_add_missing_indexes.rb @@ -0,0 +1,16 @@ +class AddMissingIndexes < ActiveRecord::Migration + def change + add_index :topics, :user_id + add_index :topics, :metacode_id + add_index :synapses, [:node2_id, :node2_id] + add_index :synapses, [:node1_id, :node1_id] + add_index :synapses, :user_id + add_index :synapses, :node1_id + add_index :synapses, :node2_id + add_index :mappings, [:map_id, :topic_id] + add_index :mappings, [:map_id, :synapse_id] + add_index :mappings, :map_id + add_index :mappings, :user_id + add_index :maps, :user_id + end +end diff --git a/db/migrate/20151025083043_remove_asset_paths_from_metacodes.rb b/db/migrate/20151025083043_remove_asset_paths_from_metacodes.rb new file mode 100644 index 00000000..c5a5e842 --- /dev/null +++ b/db/migrate/20151025083043_remove_asset_paths_from_metacodes.rb @@ -0,0 +1,8 @@ +class RemoveAssetPathsFromMetacodes < ActiveRecord::Migration + def change + Metacode.all.each do |metacode| + metacode.icon = metacode.icon.gsub(/^\/assets\//, '') + metacode.save + end + end +end diff --git a/db/schema.rb b/db/schema.rb index fc6b7335..45fbcf67 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -9,21 +9,24 @@ # from scratch. The latter is a flawed and unsustainable approach (the more migrations # you'll amass, the slower it'll run and the greater likelihood for issues). # -# It's strongly recommended to check this file into your version control system. +# It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(:version => 20141121204712) do +ActiveRecord::Schema.define(version: 20151025083043) do - create_table "in_metacode_sets", :force => true do |t| + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "in_metacode_sets", force: :cascade do |t| t.integer "metacode_id" t.integer "metacode_set_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - add_index "in_metacode_sets", ["metacode_id"], :name => "index_in_metacode_sets_on_metacode_id" - add_index "in_metacode_sets", ["metacode_set_id"], :name => "index_in_metacode_sets_on_metacode_set_id" + add_index "in_metacode_sets", ["metacode_id"], name: "index_in_metacode_sets_on_metacode_id", using: :btree + add_index "in_metacode_sets", ["metacode_set_id"], name: "index_in_metacode_sets_on_metacode_set_id", using: :btree - create_table "mappings", :force => true do |t| + create_table "mappings", force: :cascade do |t| t.text "category" t.integer "xloc" t.integer "yloc" @@ -31,18 +34,26 @@ ActiveRecord::Schema.define(:version => 20141121204712) do t.integer "synapse_id" t.integer "map_id" t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "mappable_id" + t.string "mappable_type" end - create_table "maps", :force => true do |t| + add_index "mappings", ["map_id", "synapse_id"], name: "index_mappings_on_map_id_and_synapse_id", using: :btree + add_index "mappings", ["map_id", "topic_id"], name: "index_mappings_on_map_id_and_topic_id", using: :btree + add_index "mappings", ["map_id"], name: "index_mappings_on_map_id", using: :btree + add_index "mappings", ["mappable_id", "mappable_type"], name: "index_mappings_on_mappable_id_and_mappable_type", using: :btree + add_index "mappings", ["user_id"], name: "index_mappings_on_user_id", using: :btree + + create_table "maps", force: :cascade do |t| t.text "name" t.boolean "arranged" t.text "desc" t.text "permission" t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.boolean "featured" t.string "screenshot_file_name" t.string "screenshot_content_type" @@ -50,26 +61,28 @@ ActiveRecord::Schema.define(:version => 20141121204712) do t.datetime "screenshot_updated_at" end - create_table "metacode_sets", :force => true do |t| + add_index "maps", ["user_id"], name: "index_maps_on_user_id", using: :btree + + create_table "metacode_sets", force: :cascade do |t| t.string "name" t.text "desc" t.integer "user_id" t.boolean "mapperContributed" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - add_index "metacode_sets", ["user_id"], :name => "index_metacode_sets_on_user_id" + add_index "metacode_sets", ["user_id"], name: "index_metacode_sets_on_user_id", using: :btree - create_table "metacodes", :force => true do |t| + create_table "metacodes", force: :cascade do |t| t.text "name" t.string "icon" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "color" end - create_table "synapses", :force => true do |t| + create_table "synapses", force: :cascade do |t| t.text "desc" t.text "category" t.text "weight" @@ -77,19 +90,25 @@ ActiveRecord::Schema.define(:version => 20141121204712) do t.integer "node1_id" t.integer "node2_id" t.integer "user_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - create_table "topics", :force => true do |t| + add_index "synapses", ["node1_id", "node1_id"], name: "index_synapses_on_node1_id_and_node1_id", using: :btree + add_index "synapses", ["node1_id"], name: "index_synapses_on_node1_id", using: :btree + add_index "synapses", ["node2_id", "node2_id"], name: "index_synapses_on_node2_id_and_node2_id", using: :btree + add_index "synapses", ["node2_id"], name: "index_synapses_on_node2_id", using: :btree + add_index "synapses", ["user_id"], name: "index_synapses_on_user_id", using: :btree + + create_table "topics", force: :cascade do |t| t.text "name" t.text "desc" t.text "link" t.text "permission" t.integer "user_id" t.integer "metacode_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "image_file_name" t.string "image_content_type" t.integer "image_file_size" @@ -100,25 +119,28 @@ ActiveRecord::Schema.define(:version => 20141121204712) do t.datetime "audio_updated_at" end - create_table "users", :force => true do |t| + add_index "topics", ["metacode_id"], name: "index_topics_on_metacode_id", using: :btree + add_index "topics", ["user_id"], name: "index_topics_on_user_id", using: :btree + + create_table "users", force: :cascade do |t| t.string "name" t.string "email" t.text "settings" - t.string "code", :limit => 8 - t.string "joinedwithcode", :limit => 8 + t.string "code", limit: 8 + t.string "joinedwithcode", limit: 8 t.string "crypted_password" t.string "password_salt" t.string "persistence_token" t.string "perishable_token" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "encrypted_password", :limit => 128, :default => "" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "encrypted_password", limit: 128, default: "" t.string "remember_token" t.datetime "remember_created_at" t.string "reset_password_token" t.datetime "last_sign_in_at" t.string "last_sign_in_ip" - t.integer "sign_in_count", :default => 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.string "current_sign_in_ip" t.datetime "reset_password_sent_at" @@ -130,6 +152,6 @@ ActiveRecord::Schema.define(:version => 20141121204712) do t.integer "generation" end - add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true + add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree end diff --git a/metamaps-qa-steps.txt b/metamaps-qa-steps.txt new file mode 100644 index 00000000..36ea193a --- /dev/null +++ b/metamaps-qa-steps.txt @@ -0,0 +1,24 @@ +Metamaps Test Suite + +1) Log in to the interface +2) Create an account using your join code +3) Check your user's "generation" +4) Create three maps: private, public, and another public +5) Change the last map's permissions to commons +6) Change a map's name +7) Create a topic on map #1 +8) Verify (in a private window or another browser) that the second user can't acccess map #1 +9) Create a topic on map #2 +10) Verify that the second user can't edit map #2 +11) Create a topic on map #3 +12) Verify that the second can edit map #3 +13) Pull a topic from map #1 to map #3 +14) Create a private topic on map #1 +15) Verify that the private topic can be pulled from map #1 by the same user +16) Verify that the private topic can't be pulled from map #1 by another user +17) Login as admin. Change metacode sets. +18) Add a number of topics to one of your maps. Reload to see if they are still there. +19) Add a number of synapses to one of your maps. Reload to see if they are still there. +20) Rearrange one of your maps. Reload to see if the layout is preserved. +21) Set the screenshot for one of your maps, and verify the index of maps is updated. +22) Open two browsers on map #3 and verify that realtime editing works.