[merge] lot and lot of improvements, mainly css + new form views

bzr revid: nicolas.vanhoren@openerp.com-20120416102503-latyewf3ksvc6mvv
This commit is contained in:
niv-openerp 2012-04-16 12:25:03 +02:00
commit f7418cc61a
87 changed files with 19878 additions and 17461 deletions

View File

@ -10,3 +10,5 @@ RE:^include/
RE:^share/
RE:^man/
RE:^lib/
RE:^doc/_build/

View File

@ -15,13 +15,11 @@
"static/lib/datejs/parser.js",
"static/lib/datejs/sugarpak.js",
"static/lib/datejs/extras.js",
#"static/lib/jquery/jquery-1.6.4.js",
"static/lib/jquery/jquery-1.7.2b1.js",
"static/lib/jquery.MD5/jquery.md5.js",
"static/lib/jquery.form/jquery.form.js",
"static/lib/jquery.validate/jquery.validate.js",
"static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
"static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js",
"static/lib/jquery.blockUI/jquery.blockUI.js",
"static/lib/jquery.superfish/js/hoverIntent.js",
"static/lib/jquery.superfish/js/superfish.js",
@ -35,11 +33,26 @@
"static/lib/qweb/qweb2.js",
"static/lib/underscore/underscore.js",
"static/lib/underscore/underscore.string.js",
"static/lib/backbone/backbone.js",
"static/lib/visualsearch/lib/js/visualsearch.js",
"static/lib/visualsearch/lib/js/utils/backbone_extensions.js",
"static/lib/visualsearch/lib/js/utils/hotkeys.js",
"static/lib/visualsearch/lib/js/utils/inflector.js",
"static/lib/visualsearch/lib/js/utils/jquery_extensions.js",
"static/lib/visualsearch/lib/js/utils/search_parser.js",
"static/lib/visualsearch/lib/js/models/search_facets.js",
"static/lib/visualsearch/lib/js/models/search_query.js",
"static/lib/visualsearch/lib/js/templates/templates.js",
"static/lib/visualsearch/lib/js/views/search_facet.js",
"static/lib/visualsearch/lib/js/views/search_input.js",
"static/lib/visualsearch/lib/js/views/search_box.js",
"static/lib/labjs/LAB.src.js",
"static/lib/py.js/lib/py.js",
"static/lib/novajs/src/nova.js",
"static/src/js/boot.js",
"static/src/js/core.js",
"static/src/js/corelib.js",
"static/src/js/coresetup.js",
"static/src/js/dates.js",
"static/src/js/formats.js",
"static/src/js/chrome.js",
@ -49,7 +62,6 @@
"static/src/js/data_import.js",
"static/src/js/search.js",
"static/src/js/view_form.js",
"static/src/js/view_page.js",
"static/src/js/view_list.js",
"static/src/js/view_list_editable.js",
"static/src/js/view_tree.js",
@ -57,11 +69,14 @@
],
'css' : [
"static/lib/jquery.superfish/css/superfish.css",
"static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.17.custom.css",
"static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.8.16.custom.css",
"static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
"static/lib/jquery.ui.notify/css/ui.notify.css",
"static/lib/jquery.tipsy/tipsy.css",
"static/src/css/base_old.css",
"static/lib/visualsearch/lib/css/reset.css",
"static/lib/visualsearch/lib/css/workspace.css",
"static/lib/visualsearch/lib/css/icons.css",
# "static/src/css/base_old.css",
"static/src/css/base.css",
"static/src/css/data_export.css",
"static/src/css/data_import.css",

View File

@ -79,7 +79,7 @@ def concat_files(file_list, reader=None, intersperse=""):
if reader is None:
def reader(f):
with open(f) as fp:
with open(f, 'rb') as fp:
return fp.read()
files_content = []
@ -261,8 +261,8 @@ class WebClient(openerpweb.Controller):
def reader(f):
"""read the a css file and absolutify all relative uris"""
with open(f) as fp:
data = fp.read()
with open(f, 'rb') as fp:
data = fp.read().decode('utf-8')
path = file_map[f]
# convert FS path into web path
@ -279,7 +279,7 @@ class WebClient(openerpweb.Controller):
r"""url(\1%s/""" % (web_dir,),
data,
)
return data
return data.encode('utf-8')
content, checksum = concat_files((f[0] for f in files), reader)
@ -802,9 +802,6 @@ def fix_view_modes(action):
new view mode ``list`` which is the result of the ``tree`` view_mode
in conjunction with the ``form`` view_type.
This method also adds a ``page`` view mode in case there is a ``form`` in
the input action.
TODO: this should go into the doc, some kind of "peculiarities" section
:param dict action: an action descriptor
@ -818,8 +815,6 @@ def fix_view_modes(action):
if mode == 'form':
id_form = id
break
if id_form is not None:
action['views'].insert(index + 1, (id_form, 'page'))
if action.pop('view_type', 'form') != 'form':
return action
@ -1154,6 +1149,7 @@ class View(openerpweb.Controller):
arch = fvg['arch'].encode('utf-8')
else:
arch = fvg['arch']
fvg['arch_string'] = arch
if transform:
evaluation_context = session.evaluation_context(context or {})

File diff suppressed because it is too large Load Diff

View File

@ -1,132 +0,0 @@
/*
Fork of some unlicensed library found somewhere, don't hesitate to
patch it directly.
*/
(function($) {
var menu,shadow,trigger,content,hash,currentTarget;
var defaults= {
menuStyle: {
listStyle:'none',
padding:'1px',
margin:'0px',
backgroundColor:'#fff',
border:'1px solid #999',
width:'100px'
},
itemStyle: {
margin:'0px',
color:'#000',
display:'block',
cursor:'default',
padding:'3px',
border:'1px solid #fff',
backgroundColor:'transparent'
},
itemHoverStyle: {
border:'1px solid #0a246a',
backgroundColor:'#b6bdd2'
},
eventPosX:'pageX',
eventPosY:'pageY',
shadow:true,
onContextMenu:null,
onShowMenu:null
};
$.fn.contextMenu= function(id,options) {
if(!menu) {
menu=$('<div id="jqContextMenu" class="openerp"></div>').hide().css({
position:'absolute',
zIndex:'2000'
}).appendTo('body').bind('click', function(e) {
e.stopPropagation()
})
}
if(!shadow) {
shadow=$('<div></div>').css({
backgroundColor:'#000',
position:'absolute',
opacity:0.2,
zIndex:499
}).appendTo('body').hide()
}
hash=hash||[];
hash.push({
id:id,
menuStyle:$.extend({},defaults.menuStyle,options.menuStyle|| {}),
itemStyle:$.extend({},defaults.itemStyle,options.itemStyle|| {}),
itemHoverStyle:$.extend({},defaults.itemHoverStyle,options.itemHoverStyle|| {}),
bindings:options.bindings|| {},
shadow:options.shadow||options.shadow===false?options.shadow:defaults.shadow,
onContextMenu:options.onContextMenu||defaults.onContextMenu,
onShowMenu:options.onShowMenu||defaults.onShowMenu,
eventPosX:options.eventPosX||defaults.eventPosX,
eventPosY:options.eventPosY||defaults.eventPosY
});
var index=hash.length-1;
var callback = function(e) {
var bShowContext=(!!hash[index].onContextMenu)?hash[index].onContextMenu(e):true;
if(bShowContext)
display(index,this,e,options);
return false;
};
if (!options.noRightClick) {
$(this).bind('contextmenu', callback);
}
if (options.leftClickToo || options.noRightClick) {
$(this).click(callback);
}
return this
};
function display(index,trigger,e,options) {
var cur=hash[index];
content=$('#'+cur.id).find('ul:first').clone(true);
content.css(cur.menuStyle).find('li').css(cur.itemStyle).hover( function() {
$(this).css(cur.itemHoverStyle)
}, function() {
$(this).css(cur.itemStyle)
}).find('img').css({
verticalAlign:'middle',
paddingRight:'2px'
});
menu.html(content);
if(!!cur.onShowMenu)
menu=cur.onShowMenu(e,menu);
$.each(cur.bindings, function(id,func) {
$('#'+id,menu).bind('click', function(e) {
hide();
func(trigger,currentTarget)
})
});
menu.css({
'left':e[cur.eventPosX],
'top':e[cur.eventPosY]
}).show();
if(cur.shadow)
shadow.css({
width:menu.width(),
height:menu.height(),
left:e.pageX+2,
top:e.pageY+2
}).show();
$(document).one('click',hide)
}
function hide() {
menu.hide();
shadow.hide()
}
$.contextMenu= {
defaults: function(userDefaults) {
$.each(userDefaults, function(i,val) {
if(typeof val=='object'&&defaults[i]) {
$.extend(defaults[i],val)
} else
defaults[i]=val
})
}
}
})(jQuery);
$( function() {
$('div.contextMenu').hide()
});

View File

@ -63,7 +63,7 @@
}
}
$tip.css(tp).addClass('tipsy-' + gravity);
$tip.css(tp).addClass('openerp oe_tooltip tipsy-' + gravity);
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
if (this.options.className) {
$tip.addClass(maybeCall(this.options.className, this.$element[0]));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
/* ==========================================================
* bootstrap-alerts.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ){
"use strict"
/* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
* ======================================================= */
var transitionEnd
$(document).ready(function () {
$.support.transition = (function () {
var thisBody = document.body || document.documentElement
, thisStyle = thisBody.style
, support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
return support
})()
// set CSS transition event type
if ( $.support.transition ) {
transitionEnd = "TransitionEnd"
if ( $.browser.webkit ) {
transitionEnd = "webkitTransitionEnd"
} else if ( $.browser.mozilla ) {
transitionEnd = "transitionend"
} else if ( $.browser.opera ) {
transitionEnd = "oTransitionEnd"
}
}
})
/* ALERT CLASS DEFINITION
* ====================== */
var Alert = function ( content, options ) {
if (options == 'close') return this.close.call(content)
this.settings = $.extend({}, $.fn.alert.defaults, options)
this.$element = $(content)
.delegate(this.settings.selector, 'click', this.close)
}
Alert.prototype = {
close: function (e) {
var $element = $(this)
, className = 'alert-message'
$element = $element.hasClass(className) ? $element : $element.parent()
e && e.preventDefault()
$element.removeClass('in')
function removeElement () {
$element.remove()
}
$.support.transition && $element.hasClass('fade') ?
$element.bind(transitionEnd, removeElement) :
removeElement()
}
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function ( options ) {
if ( options === true ) {
return this.data('alert')
}
return this.each(function () {
var $this = $(this)
, data
if ( typeof options == 'string' ) {
data = $this.data('alert')
if (typeof data == 'object') {
return data[options].call( $this )
}
}
$(this).data('alert', new Alert( this, options ))
})
}
$.fn.alert.defaults = {
selector: '.close'
}
$(document).ready(function () {
new Alert($('body'), {
selector: '.alert-message[data-alert] .close'
})
})
}( window.jQuery || window.ender );

View File

@ -0,0 +1,64 @@
/* ============================================================
* bootstrap-buttons.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
function setState(el, state) {
var d = 'disabled'
, $el = $(el)
, data = $el.data()
state = state + 'Text'
data.resetText || $el.data('resetText', $el.html())
$el.html( data[state] || $.fn.button.defaults[state] )
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
function toggle(el) {
$(el).toggleClass('active')
}
$.fn.button = function(options) {
return this.each(function () {
if (options == 'toggle') {
return toggle(this)
}
options && setState(this, options)
})
}
$.fn.button.defaults = {
loadingText: 'loading...'
}
$(function () {
$('body').delegate('.btn[data-toggle]', 'click', function () {
$(this).button('toggle')
})
})
}( window.jQuery || window.ender );

View File

@ -0,0 +1,55 @@
/* ============================================================
* bootstrap-dropdown.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#dropdown
* ============================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function ( selector ) {
return this.each(function () {
$(this).delegate(selector || d, 'click', function (e) {
var li = $(this).parent('li')
, isActive = li.hasClass('open')
clearMenus()
!isActive && li.toggleClass('open')
return false
})
})
}
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
var d = 'a.menu, .dropdown-toggle'
function clearMenus() {
$(d).parent('li').removeClass('open')
}
$(function () {
$('html').bind("click", clearMenus)
$('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' )
})
}( window.jQuery || window.ender );

View File

@ -0,0 +1,260 @@
/* =========================================================
* bootstrap-modal.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#modal
* =========================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
!function( $ ){
"use strict"
/* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
* ======================================================= */
var transitionEnd
$(document).ready(function () {
$.support.transition = (function () {
var thisBody = document.body || document.documentElement
, thisStyle = thisBody.style
, support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
return support
})()
// set CSS transition event type
if ( $.support.transition ) {
transitionEnd = "TransitionEnd"
if ( $.browser.webkit ) {
transitionEnd = "webkitTransitionEnd"
} else if ( $.browser.mozilla ) {
transitionEnd = "transitionend"
} else if ( $.browser.opera ) {
transitionEnd = "oTransitionEnd"
}
}
})
/* MODAL PUBLIC CLASS DEFINITION
* ============================= */
var Modal = function ( content, options ) {
this.settings = $.extend({}, $.fn.modal.defaults, options)
this.$element = $(content)
.delegate('.close', 'click.modal', $.proxy(this.hide, this))
if ( this.settings.show ) {
this.show()
}
return this
}
Modal.prototype = {
toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
this.isShown = true
this.$element.trigger('show')
escape.call(this)
backdrop.call(this, function () {
var transition = $.support.transition && that.$element.hasClass('fade')
that.$element
.appendTo(document.body)
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
transition ?
that.$element.one(transitionEnd, function () { that.$element.trigger('shown') }) :
that.$element.trigger('shown')
})
return this
}
, hide: function (e) {
e && e.preventDefault()
if ( !this.isShown ) {
return this
}
var that = this
this.isShown = false
escape.call(this)
this.$element
.trigger('hide')
.removeClass('in')
$.support.transition && this.$element.hasClass('fade') ?
hideWithTransition.call(this) :
hideModal.call(this)
return this
}
}
/* MODAL PRIVATE METHODS
* ===================== */
function hideWithTransition() {
// firefox drops transitionEnd events :{o
var that = this
, timeout = setTimeout(function () {
that.$element.unbind(transitionEnd)
hideModal.call(that)
}, 500)
this.$element.one(transitionEnd, function () {
clearTimeout(timeout)
hideModal.call(that)
})
}
function hideModal (that) {
this.$element
.hide()
.trigger('hidden')
backdrop.call(this)
}
function backdrop ( callback ) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if ( this.isShown && this.settings.backdrop ) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if ( this.settings.backdrop != 'static' ) {
this.$backdrop.click($.proxy(this.hide, this))
}
if ( doAnimate ) {
this.$backdrop[0].offsetWidth // force reflow
}
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one(transitionEnd, callback) :
callback()
} else if ( !this.isShown && this.$backdrop ) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one(transitionEnd, $.proxy(removeBackdrop, this)) :
removeBackdrop.call(this)
} else if ( callback ) {
callback()
}
}
function removeBackdrop() {
this.$backdrop.remove()
this.$backdrop = null
}
function escape() {
var that = this
if ( this.isShown && this.settings.keyboard ) {
$(document).bind('keyup.modal', function ( e ) {
if ( e.which == 27 ) {
that.hide()
}
})
} else if ( !this.isShown ) {
$(document).unbind('keyup.modal')
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
$.fn.modal = function ( options ) {
var modal = this.data('modal')
if (!modal) {
if (typeof options == 'string') {
options = {
show: /show|toggle/.test(options)
}
}
return this.each(function () {
$(this).data('modal', new Modal(this, options))
})
}
if ( options === true ) {
return modal
}
if ( typeof options == 'string' ) {
modal[options]()
} else if ( modal ) {
modal.toggle()
}
return this
}
$.fn.modal.Modal = Modal
$.fn.modal.defaults = {
backdrop: false
, keyboard: false
, show: false
}
/* MODAL DATA- IMPLEMENTATION
* ========================== */
$(document).ready(function () {
$('body').delegate('[data-controls-modal]', 'click', function (e) {
e.preventDefault()
var $this = $(this).data('show', true)
$('#' + $this.attr('data-controls-modal')).modal( $this.data() )
})
})
}( window.jQuery || window.ender );

View File

@ -0,0 +1,90 @@
/* ===========================================================
* bootstrap-popover.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#popover
* ===========================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================================================== */
!function( $ ) {
"use strict"
var Popover = function ( element, options ) {
this.$element = $(element)
this.options = options
this.enabled = true
this.fixTitle()
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TWIPSY.js
========================================= */
Popover.prototype = $.extend({}, $.fn.twipsy.Twipsy.prototype, {
setContent: function () {
var $tip = this.tip()
$tip.find('.title')[this.options.html ? 'html' : 'text'](this.getTitle())
$tip.find('.content > *')[this.options.html ? 'html' : 'text'](this.getContent())
$tip[0].className = 'popover'
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
if (typeof this.options.content == 'string') {
content = $e.attr(this.options.content)
} else if (typeof this.options.content == 'function') {
content = this.options.content.call(this.$element[0])
}
return content
}
, tip: function() {
if (!this.$tip) {
this.$tip = $('<div class="popover" />')
.html(this.options.template)
}
return this.$tip
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function (options) {
if (typeof options == 'object') options = $.extend({}, $.fn.popover.defaults, options)
$.fn.twipsy.initWith.call(this, options, Popover, 'popover')
return this
}
$.fn.popover.defaults = $.extend({} , $.fn.twipsy.defaults, {
placement: 'right'
, content: 'data-content'
, template: '<div class="arrow"></div><div class="inner"><h3 class="title"></h3><div class="content"><p></p></div></div>'
})
$.fn.twipsy.rejectAttrOptions.push( 'content' )
}( window.jQuery || window.ender );

View File

@ -0,0 +1,107 @@
/* =============================================================
* bootstrap-scrollspy.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================== */
!function ( $ ) {
"use strict"
var $window = $(window)
function ScrollSpy( topbar, selector ) {
var processScroll = $.proxy(this.processScroll, this)
this.$topbar = $(topbar)
this.selector = selector || 'li > a'
this.refresh()
this.$topbar.delegate(this.selector, 'click', processScroll)
$window.scroll(processScroll)
this.processScroll()
}
ScrollSpy.prototype = {
refresh: function () {
this.targets = this.$topbar.find(this.selector).map(function () {
var href = $(this).attr('href')
return /^#\w/.test(href) && $(href).length ? href : null
})
this.offsets = $.map(this.targets, function (id) {
return $(id).offset().top
})
}
, processScroll: function () {
var scrollTop = $window.scrollTop() + 10
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& this.activateButton( targets[i] )
}
}
, activateButton: function (target) {
this.activeTarget = target
this.$topbar
.find(this.selector).parent('.active')
.removeClass('active')
this.$topbar
.find(this.selector + '[href="' + target + '"]')
.parent('li')
.addClass('active')
}
}
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
$.fn.scrollSpy = function( options ) {
var scrollspy = this.data('scrollspy')
if (!scrollspy) {
return this.each(function () {
$(this).data('scrollspy', new ScrollSpy( this, options ))
})
}
if ( options === true ) {
return scrollspy
}
if ( typeof options == 'string' ) {
scrollspy[options]()
}
return this
}
$(document).ready(function () {
$('body').scrollSpy('[data-scrollspy] li > a')
})
}( window.jQuery || window.ender );

View File

@ -0,0 +1,80 @@
/* ========================================================
* bootstrap-tabs.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ======================================================== */
!function( $ ){
"use strict"
function activate ( element, container ) {
container
.find('> .active')
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
element.addClass('active')
if ( element.parent('.dropdown-menu') ) {
element.closest('li.dropdown').addClass('active')
}
}
function tab( e ) {
var $this = $(this)
, $ul = $this.closest('ul:not(.dropdown-menu)')
, href = $this.attr('href')
, previous
, $href
if ( /^#\w+/.test(href) ) {
e.preventDefault()
if ( $this.parent('li').hasClass('active') ) {
return
}
previous = $ul.find('.active a').last()[0]
$href = $(href)
activate($this.parent('li'), $ul)
activate($href, $href.parent())
$this.trigger({
type: 'change'
, relatedTarget: previous
})
}
}
/* TABS/PILLS PLUGIN DEFINITION
* ============================ */
$.fn.tabs = $.fn.pills = function ( selector ) {
return this.each(function () {
$(this).delegate(selector || '.tabs li > a, .pills > li > a', 'click', tab)
})
}
$(document).ready(function () {
$('body').tabs('ul[data-tabs] li > a, ul[data-pills] > li > a')
})
}( window.jQuery || window.ender );

View File

@ -0,0 +1,321 @@
/* ==========================================================
* bootstrap-twipsy.js v1.4.0
* http://twitter.github.com/bootstrap/javascript.html#twipsy
* Adapted from the original jQuery.tipsy by Jason Frame
* ==========================================================
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ) {
"use strict"
/* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
* ======================================================= */
var transitionEnd
$(document).ready(function () {
$.support.transition = (function () {
var thisBody = document.body || document.documentElement
, thisStyle = thisBody.style
, support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
return support
})()
// set CSS transition event type
if ( $.support.transition ) {
transitionEnd = "TransitionEnd"
if ( $.browser.webkit ) {
transitionEnd = "webkitTransitionEnd"
} else if ( $.browser.mozilla ) {
transitionEnd = "transitionend"
} else if ( $.browser.opera ) {
transitionEnd = "oTransitionEnd"
}
}
})
/* TWIPSY PUBLIC CLASS DEFINITION
* ============================== */
var Twipsy = function ( element, options ) {
this.$element = $(element)
this.options = options
this.enabled = true
this.fixTitle()
}
Twipsy.prototype = {
show: function() {
var pos
, actualWidth
, actualHeight
, placement
, $tip
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animate) {
$tip.addClass('fade')
}
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.prependTo(document.body)
pos = $.extend({}, this.$element.offset(), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
placement = maybeCall(this.options.placement, this, [ $tip[0], this.$element[0] ])
switch (placement) {
case 'below':
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'above':
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
$tip.find('.twipsy-inner')[this.options.html ? 'html' : 'text'](this.getTitle())
$tip[0].className = 'twipsy'
}
, hide: function() {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeElement () {
$tip.remove()
}
$.support.transition && this.$tip.hasClass('fade') ?
$tip.bind(transitionEnd, removeElement) :
removeElement()
}
, fixTitle: function() {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getTitle: function() {
var title
, $e = this.$element
, o = this.options
this.fixTitle()
if (typeof o.title == 'string') {
title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title)
} else if (typeof o.title == 'function') {
title = o.title.call($e[0])
}
title = ('' + title).replace(/(^\s*|\s*$)/, "")
return title || o.fallback
}
, tip: function() {
return this.$tip = this.$tip || $('<div class="twipsy" />').html(this.options.template)
}
, validate: function() {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function() {
this.enabled = true
}
, disable: function() {
this.enabled = false
}
, toggleEnabled: function() {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
}
/* TWIPSY PRIVATE METHODS
* ====================== */
function maybeCall ( thing, ctx, args ) {
return typeof thing == 'function' ? thing.apply(ctx, args) : thing
}
/* TWIPSY PLUGIN DEFINITION
* ======================== */
$.fn.twipsy = function (options) {
$.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy')
return this
}
$.fn.twipsy.initWith = function (options, Constructor, name) {
var twipsy
, binder
, eventIn
, eventOut
if (options === true) {
return this.data(name)
} else if (typeof options == 'string') {
twipsy = this.data(name)
if (twipsy) {
twipsy[options]()
}
return this
}
options = $.extend({}, $.fn[name].defaults, options)
function get(ele) {
var twipsy = $.data(ele, name)
if (!twipsy) {
twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options))
$.data(ele, name, twipsy)
}
return twipsy
}
function enter() {
var twipsy = get(this)
twipsy.hoverState = 'in'
if (options.delayIn == 0) {
twipsy.show()
} else {
twipsy.fixTitle()
setTimeout(function() {
if (twipsy.hoverState == 'in') {
twipsy.show()
}
}, options.delayIn)
}
}
function leave() {
var twipsy = get(this)
twipsy.hoverState = 'out'
if (options.delayOut == 0) {
twipsy.hide()
} else {
setTimeout(function() {
if (twipsy.hoverState == 'out') {
twipsy.hide()
}
}, options.delayOut)
}
}
if (!options.live) {
this.each(function() {
get(this)
})
}
if (options.trigger != 'manual') {
binder = options.live ? 'live' : 'bind'
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'
this[binder](eventIn, enter)[binder](eventOut, leave)
}
return this
}
$.fn.twipsy.Twipsy = Twipsy
$.fn.twipsy.defaults = {
animate: true
, delayIn: 0
, delayOut: 0
, fallback: ''
, placement: 'above'
, html: false
, live: false
, offset: 0
, title: 'title'
, trigger: 'hover'
, template: '<div class="twipsy-arrow"></div><div class="twipsy-inner"></div>'
}
$.fn.twipsy.rejectAttrOptions = [ 'title' ]
$.fn.twipsy.elementOptions = function(ele, options) {
var data = $(ele).data()
, rejects = $.fn.twipsy.rejectAttrOptions
, i = rejects.length
while (i--) {
delete data[rejects[i]]
}
return $.extend({}, options, data)
}
}( window.jQuery || window.ender );

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-left, .ui-corner-bottom{ border-radius:0px;}
.ui-state-active,.ui-tabs-selected { border-radius:0px;}
.ui-tabs-selected { border-radius:0px;}
.ui-tabs .ui-tabs-nav li{ filter:none;}
.ui-tabs .ui-tabs-nav li a { border-radius:0px; }

View File

@ -0,0 +1,117 @@
/*!
* jQuery UI Bootstrap (0.22)
* http://addyosmani.github.com/jquery-ui-bootstrap
*
* Copyright 2012, Addy Osmani
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Portions copyright jQuery UI & Twitter Bootstrap
*/
.demoHeaders {
margin-top: 1.5em;
margin-bottom: 1em;
}
#dialog_link,#modal_link {
padding: .4em 1em .4em 20px;
text-decoration: none;
position: relative;
}
#dialog_link span.ui-icon, #modal_link span.ui-icon {
margin: 0 5px 0 0;
position: absolute;
left: .2em;
top: 50%;
margin-top: -8px;
}
ul#icons {
margin: 0;
padding: 0;
}
ul#icons li {
margin: 2px;
position: relative;
padding: 4px 0;
cursor: pointer;
float: left;
list-style: none;
}
ul#icons span.ui-icon {
float: left;
margin: 0 4px;
}
#buttons button {
float: left;
margin-right: 4px;
}
#components {
margin: 2em 0 1em;
width: 900px;
margin: 0 auto;
padding: 50px;
}
#dialog2 label, #dialog2 input {
display: block;
}
#dialog2 label {
margin-top: 0.5em;
}
#dialog2 input, #dialog2 textarea {
width: 95%;
}
#tabs2 {
margin-top: 1em;
}
#tabs2 li .ui-icon-close {
float: left;
margin: 0.4em 0.2em 0 0;
cursor: pointer;
}
#add_tab {
cursor: pointer;
}
.span10 {
width: 500px;
margin-right: 10px;
}
.span4 {
width: 290px;
}
.ui-widget-overlay {
width: 500px;
}
.alert-message p{
line-height:18px;
margin-bottom:18px;
}
#toolbar {
padding: 10px 4px;
}
input[type="text"],
input[type="password"],
.ui-autocomplete-input,
textarea,
.uneditable-input{
width: 210px;
height: 18px;
}

View File

@ -0,0 +1,26 @@
/*!
* Bootstrap @VERSION
*
* Copyright 2011 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
* Date: @DATE
*/
// CSS Reset
@import "reset";
// Core variables and mixins
@import "variables"; // Modify this for custom colors, font-sizes, etc
@import "mixins";
// Grid system and page structure
@import "scaffolding";
// Styled patterns and elements
@import "type";
@import "forms";
@import "tables";
@import "patterns";

View File

@ -0,0 +1,479 @@
/* Forms.less
* Base styles for various input types, form layouts, and states
* ------------------------------------------------------------- */
// FORM STYLES
// -----------
form {
margin-bottom: @baseline;
}
// Groups of fields with labels on top (legends)
fieldset {
margin-bottom: @baseline;
padding-top: @baseline;
legend {
display: block;
padding-left: 150px;
font-size: @basefont * 1.5;
line-height: 1;
color: @grayDark;
*padding: 0 0 5px 145px; /* IE6-7 */
*line-height: 1.5; /* IE6-7 */
}
}
// Parent element that clears floats and wraps labels and fields together
form .clearfix {
margin-bottom: @baseline;
.clearfix()
}
// Set font for forms
.bootstrap label,
.bootstrap input,
.bootstrap select,
.bootstrap textarea {
#font > .sans-serif(normal,13px,normal);
}
// Float labels left
.bootstrap label {
padding-top: 6px;
font-size: @basefont;
line-height: @baseline;
float: left;
width: 130px;
text-align: right;
color: @grayDark;
}
// Shift over the inside div to align all label's relevant content
.bootstrap form .input {
margin-left: 150px;
}
// Checkboxs and radio buttons
.bootstrap input[type=checkbox],
.bootstrap input[type=radio] {
cursor: pointer;
}
// Inputs, Textareas, Selects
.bootstrap input,
.bootstrap textarea,
.bootstrap select,
.uneditable-input {
display: inline-block;
width: 210px;
height: @baseline;
padding: 4px;
font-size: @basefont;
line-height: @baseline;
color: @gray;
border: 1px solid #ccc;
.border-radius(3px);
}
// remove padding from select
.bootstrap select {
padding: initial;
}
// mini reset for non-html5 file types
.bootstrap input[type=checkbox],
.bootstrap input[type=radio] {
width: auto;
height: auto;
padding: 0;
margin: 3px 0;
*margin-top: 0; /* IE6-7 */
line-height: normal;
border: none;
}
.bootstrap input[type=file] {
background-color: @white;
padding: initial;
border: initial;
line-height: initial;
.box-shadow(none);
}
.bootstrap input[type=button],
.bootstrap input[type=reset],
.bootstrap input[type=submit] {
width: auto;
height: auto;
}
.bootstrap select,
.bootstrap input[type=file] {
height: @baseline * 1.5; // In IE7, the height of the select element cannot be changed by height, only font-size
*height: auto; // Reset for IE7
line-height: @baseline * 1.5;
*margin-top: 4px; /* For IE7, add top margin to align select with labels */
}
// Make multiple select elements height not fixed
select[multiple] {
height: inherit;
background-color: @white; // Fixes Chromium bug of unreadable items
}
textarea {
height: auto;
}
// For text that needs to appear as an input but should not be an input
.uneditable-input {
background-color: @white;
display: block;
border-color: #eee;
.box-shadow(inset 0 1px 2px rgba(0,0,0,.025));
cursor: not-allowed;
}
// Placeholder text gets special styles; can't be bundled together though for some reason
:-moz-placeholder {
color: @grayLight;
}
::-webkit-input-placeholder {
color: @grayLight;
}
// Focus states
input,
textarea {
@transition: border linear .2s, box-shadow linear .2s;
.transition(@transition);
.box-shadow(inset 0 1px 3px rgba(0,0,0,.1));
}
input:focus,
textarea:focus {
outline: 0;
border-color: rgba(82,168,236,.8);
@shadow: inset 0 1px 3px rgba(0,0,0,.1), 0 0 8px rgba(82,168,236,.6);
.box-shadow(@shadow);
}
input[type=file]:focus,
input[type=checkbox]:focus,
select:focus {
.box-shadow(none); // override for file inputs
outline: 1px dotted #666; // Selet elements don't get box-shadow styles, so instead we do outline
}
// FORM FIELD FEEDBACK STATES
// --------------------------
// Mixin for form field states
.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) {
// Set the text color
> label,
.help-block,
.help-inline {
color: @textColor;
}
// Style inputs accordingly
input,
textarea {
color: @textColor;
border-color: @borderColor;
&:focus {
border-color: darken(@borderColor, 10%);
.box-shadow(0 0 6px lighten(@borderColor, 20%));
}
}
// Give a small background color for input-prepend/-append
.input-prepend .add-on,
.input-append .add-on {
color: @textColor;
background-color: @backgroundColor;
border-color: @textColor;
}
}
// Error
form .clearfix.error {
.formFieldState(#b94a48, #ee5f5b, lighten(#ee5f5b, 30%));
}
// Warning
form .clearfix.warning {
.formFieldState(#c09853, #ccae64, lighten(#CCAE64, 5%));
}
// Success
form .clearfix.success {
.formFieldState(#468847, #57a957, lighten(#57a957, 30%));
}
// Form element sizes
// TODO v2: remove duplication here and just stick to .input-[size] in light of adding .spanN sizes
.input-mini,
input.mini,
textarea.mini,
select.mini {
width: 60px;
}
.input-small,
input.small,
textarea.small,
select.small {
width: 90px;
}
.input-medium,
input.medium,
textarea.medium,
select.medium {
width: 150px;
}
.input-large,
input.large,
textarea.large,
select.large {
width: 210px;
}
.input-xlarge,
input.xlarge,
textarea.xlarge,
select.xlarge {
width: 270px;
}
.input-xxlarge,
input.xxlarge,
textarea.xxlarge,
select.xxlarge {
width: 530px;
}
textarea.xxlarge {
overflow-y: auto;
}
// Grid style input sizes
// This is a duplication of the main grid .columns() mixin, but subtracts 10px to account for input padding and border
.formColumns(@columnSpan: 1) {
display: inline-block;
float: none;
width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 10;
margin-left: 0;
}
input,
textarea {
// Default columns
&.span1 { .formColumns(1); }
&.span2 { .formColumns(2); }
&.span3 { .formColumns(3); }
&.span4 { .formColumns(4); }
&.span5 { .formColumns(5); }
&.span6 { .formColumns(6); }
&.span7 { .formColumns(7); }
&.span8 { .formColumns(8); }
&.span9 { .formColumns(9); }
&.span10 { .formColumns(10); }
&.span11 { .formColumns(11); }
&.span12 { .formColumns(12); }
&.span13 { .formColumns(13); }
&.span14 { .formColumns(14); }
&.span15 { .formColumns(15); }
&.span16 { .formColumns(16); }
}
// Disabled and read-only inputs
input[disabled],
select[disabled],
textarea[disabled],
input[readonly],
select[readonly],
textarea[readonly] {
background-color: #f5f5f5;
border-color: #ddd;
cursor: not-allowed;
}
// Actions (the buttons)
.actions {
background: #f5f5f5;
margin-top: @baseline;
margin-bottom: @baseline;
padding: (@baseline - 1) 20px @baseline 150px;
border-top: 1px solid #ddd;
.border-radius(0 0 3px 3px);
.secondary-action {
float: right;
a {
line-height: 30px;
&:hover {
text-decoration: underline;
}
}
}
}
// Help Text
// TODO: Do we need to set basefont and baseline here?
.help-inline,
.help-block {
font-size: @basefont;
line-height: @baseline;
color: @grayLight;
}
.help-inline {
padding-left: 5px;
*position: relative; /* IE6-7 */
*top: -5px; /* IE6-7 */
}
// Big blocks of help text
.help-block {
display: block;
max-width: 600px;
}
// Inline Fields (input fields that appear as inline objects
.inline-inputs {
color: @gray;
span {
padding: 0 2px 0 1px;
}
}
// Allow us to put symbols and text within the input field for a cleaner look
.input-prepend,
.input-append {
input {
.border-radius(0 3px 3px 0);
}
.add-on {
position: relative;
background: #f5f5f5;
border: 1px solid #ccc;
z-index: 2;
float: left;
display: block;
width: auto;
min-width: 16px;
height: 18px;
padding: 4px 4px 4px 5px;
margin-right: -1px;
font-weight: normal;
line-height: 18px;
color: @grayLight;
text-align: center;
text-shadow: 0 1px 0 @white;
.border-radius(3px 0 0 3px);
}
.active {
background: lighten(@green, 30);
border-color: @green;
}
}
.input-prepend {
.add-on {
*margin-top: 1px; /* IE6-7 */
}
}
.input-append {
input {
float: left;
.border-radius(3px 0 0 3px);
}
.add-on {
.border-radius(0 3px 3px 0);
margin-right: 0;
margin-left: -1px;
}
}
// Stacked options for forms (radio buttons or checkboxes)
.inputs-list {
margin: 0 0 5px;
width: 100%;
li {
display: block;
padding: 0;
width: 100%;
}
label {
display: block;
float: none;
width: auto;
padding: 0;
margin-left: 20px;
line-height: @baseline;
text-align: left;
white-space: normal;
strong {
color: @gray;
}
small {
font-size: @basefont - 2;
font-weight: normal;
}
}
.inputs-list {
margin-left: 25px;
margin-bottom: 10px;
padding-top: 0;
}
&:first-child {
padding-top: 6px;
}
li + li {
padding-top: 2px;
}
input[type=radio],
input[type=checkbox] {
margin-bottom: 0;
margin-left: -20px;
float: left;
}
}
// Stacked forms
.form-stacked {
padding-left: 20px;
fieldset {
padding-top: @baseline / 2;
}
legend {
padding-left: 0;
}
label {
display: block;
float: none;
width: auto;
font-weight: bold;
text-align: left;
line-height: 20px;
padding-top: 0;
}
.clearfix {
margin-bottom: @baseline / 2;
div.input {
margin-left: 0;
}
}
.inputs-list {
margin-bottom: 0;
li {
padding-top: 0;
label {
font-weight: normal;
padding-top: 0;
}
}
}
div.clearfix.error {
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
margin-top: 0;
margin-left: -10px;
}
.actions {
margin-left: -20px;
padding-left: 20px;
}
}

View File

@ -0,0 +1,222 @@
/* Mixins.less
* Snippets of reusable CSS to develop faster and keep code readable
* ----------------------------------------------------------------- */
// Clearfix for clearing floats like a boss h5bp.com/q
.clearfix() {
zoom: 1;
&:before,
&:after {
display: table;
content: "";
zoom: 1;
}
&:after {
clear: both;
}
}
// Center-align a block level element
.center-block() {
display: block;
margin-left: auto;
margin-right: auto;
}
// Sizing shortcuts
.size(@height: 5px, @width: 5px) {
height: @height;
width: @width;
}
.square(@size: 5px) {
.size(@size, @size);
}
// Input placeholder text
.placeholder(@color: @grayLight) {
:-moz-placeholder {
color: @color;
}
::-webkit-input-placeholder {
color: @color;
}
}
// Font Stacks
#font {
.shorthand(@weight: normal, @size: 14px, @lineHeight: 20px) {
font-size: @size;
font-weight: @weight;
line-height: @lineHeight;
}
.sans-serif(@weight: normal, @size: 14px, @lineHeight: 20px) {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: @size;
font-weight: @weight;
line-height: @lineHeight;
}
.serif(@weight: normal, @size: 14px, @lineHeight: 20px) {
font-family: "Georgia", Times New Roman, Times, serif;
font-size: @size;
font-weight: @weight;
line-height: @lineHeight;
}
.monospace(@weight: normal, @size: 12px, @lineHeight: 20px) {
font-family: "Monaco", Courier New, monospace;
font-size: @size;
font-weight: @weight;
line-height: @lineHeight;
}
}
// Grid System
.fixed-container() {
width: @siteWidth;
margin-left: auto;
margin-right: auto;
.clearfix();
}
.columns(@columnSpan: 1) {
width: (@gridColumnWidth * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1));
}
.offset(@columnOffset: 1) {
margin-left: (@gridColumnWidth * @columnOffset) + (@gridGutterWidth * (@columnOffset - 1)) + @extraSpace;
}
// Necessary grid styles for every column to make them appear next to each other horizontally
.gridColumn() {
display: inline;
float: left;
margin-left: @gridGutterWidth;
}
// makeColumn can be used to mark any element (e.g., .content-primary) as a column without changing markup to .span something
.makeColumn(@columnSpan: 1) {
.gridColumn();
.columns(@columnSpan);
}
// Border Radius
.border-radius(@radius: 5px) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}
// Drop shadows
.box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) {
-webkit-box-shadow: @shadow;
-moz-box-shadow: @shadow;
box-shadow: @shadow;
}
// Transitions
.transition(@transition) {
-webkit-transition: @transition;
-moz-transition: @transition;
-ms-transition: @transition;
-o-transition: @transition;
transition: @transition;
}
// Background clipping
.background-clip(@clip) {
-webkit-background-clip: @clip;
-moz-background-clip: @clip;
background-clip: @clip;
}
// CSS3 Content Columns
.content-columns(@columnCount, @columnGap: 20px) {
-webkit-column-count: @columnCount;
-moz-column-count: @columnCount;
column-count: @columnCount;
-webkit-column-gap: @columnGap;
-moz-column-gap: @columnGap;
column-gap: @columnGap;
}
// Make any element resizable for prototyping
.resizable(@direction: both) {
resize: @direction; // Options are horizontal, vertical, both
overflow: auto; // Safari fix
}
// Add an alphatransparency value to any background or border color (via Elyse Holladay)
#translucent {
.background(@color: @white, @alpha: 1) {
background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
}
.border(@color: @white, @alpha: 1) {
border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
background-clip: padding-box;
}
}
// Gradient Bar Colors for buttons and allerts
.gradientBar(@primaryColor, @secondaryColor) {
#gradient > .vertical(@primaryColor, @secondaryColor);
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%);
border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%);
}
// Gradients
#gradient {
.horizontal (@startColor: #555, @endColor: #333) {
background-color: @endColor;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, right top, from(@startColor), to(@endColor)); // Konqueror
background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+
background-image: -ms-linear-gradient(left, @startColor, @endColor); // IE10
background-image: -webkit-gradient(linear, left top, right top, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+
background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+
background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10
background-image: linear-gradient(left, @startColor, @endColor); // Le standard
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",@startColor,@endColor)); // IE9 and down
}
.vertical (@startColor: #555, @endColor: #333) {
background-color: @endColor;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(@startColor), to(@endColor)); // Konqueror
background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+
background-image: -ms-linear-gradient(top, @startColor, @endColor); // IE10
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+
background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+
background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10
background-image: linear-gradient(top, @startColor, @endColor); // The standard
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down
}
.directional (@startColor: #555, @endColor: #333, @deg: 45deg) {
background-color: @endColor;
background-repeat: repeat-x;
background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+
background-image: -ms-linear-gradient(@deg, @startColor, @endColor); // IE10
background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+
background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10
background-image: linear-gradient(@deg, @startColor, @endColor); // The standard
}
.vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) {
background-color: @endColor;
background-repeat: no-repeat;
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor));
background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor);
background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor);
background-image: -ms-linear-gradient(@startColor, @midColor @colorStop, @endColor);
background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor);
background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor);
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down, gets no color-stop at all for proper fallback
}
}
// Reset filters for IE
.reset-filter() {
filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
}
// Opacity
.opacity(@opacity: 100) {
filter: e(%("alpha(opacity=%d)", @opacity));
-khtml-opacity: @opacity / 100;
-moz-opacity: @opacity / 100;
opacity: @opacity / 100;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
/* Reset.less
* Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc).
* ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
// ERIC MEYER RESET
// --------------------------------------------------
html, body { margin: 0; padding: 0; }
h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, cite, code, del, dfn, em, img, q, s, samp, small, strike, strong, sub, sup, tt, var, dd, dl, dt, li, ol, ul, fieldset, form, label, legend, button, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; font-weight: normal; font-style: normal; font-size: 100%; line-height: 1; font-family: inherit; }
table { border-collapse: collapse; border-spacing: 0; }
ol, ul { list-style: none; }
q:before, q:after, blockquote:before, blockquote:after { content: ""; }
// Normalize.css
// Pulling in select resets form the normalize.css project
// --------------------------------------------------
// Display in IE6-9 and FF3
// -------------------------
// Source: http://github.com/necolas/normalize.css
html {
overflow-y: scroll;
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
// Focus states
/*a:focus {
outline: thin dotted;
}*/
// Hover & Active
a:hover,
a:active {
outline: 0;
}
// Display in IE6-9 and FF3
// -------------------------
// Source: http://github.com/necolas/normalize.css
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section {
display: block;
}
// Display block in IE6-9 and FF3
// -------------------------
// Source: http://github.com/necolas/normalize.css
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
// Prevents modern browsers from displaying 'audio' without controls
// -------------------------
// Source: http://github.com/necolas/normalize.css
audio:not([controls]) {
display: none;
}
// Prevents sub and sup affecting line-height in all browsers
// -------------------------
// Source: http://github.com/necolas/normalize.css
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
// Img border in a's and image quality
// -------------------------
// Source: http://github.com/necolas/normalize.css
img {
border: 0;
-ms-interpolation-mode: bicubic;
}
// Forms
// -------------------------
// Source: http://github.com/necolas/normalize.css
// Font size in all browsers, margin changes, misc consistency
.bootstrap button,
.bootstrap input,
.bootstrap select,
.bootstrap textarea {
font-size: 100%;
margin: 0;
vertical-align: baseline;
*vertical-align: middle;
}
.bootstrap button,
.bootstrap input {
line-height: normal; // FF3/4 have !important on line-height in UA stylesheet
*overflow: visible; // Inner spacing ie IE6/7
}
.bootstrap button::-moz-focus-inner,
.bootstrap input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
border: 0;
padding: 0;
}
.bootstrap button,
.bootstrap input[type="button"],
.bootstrap input[type="reset"],
.bootstrap input[type="submit"] {
cursor: pointer; // Cursors on all buttons applied consistently
-webkit-appearance: button; // Style clicable inputs in iOS
}
.bootstrap input[type="search"] { // Appearance in Safari/Chrome
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.bootstrap input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
}
textarea {
overflow: auto; // Remove vertical scrollbar in IE6-9
vertical-align: top; // Readability and alignment cross-browser
}

View File

@ -0,0 +1,139 @@
/*
* Scaffolding
* Basic and global styles for generating a grid system, structural layout, and page templates
* ------------------------------------------------------------------------------------------- */
// STRUCTURAL LAYOUT
// -----------------
body {
background-color: @white;
margin: 0;
#font > .sans-serif(normal,@basefont,@baseline);
color: @grayDark;
}
// Container (centered, fixed-width layouts)
.container {
.fixed-container();
}
// Fluid layouts (left aligned, with sidebar, min- & max-width content)
.container-fluid {
position: relative;
min-width: 940px;
padding-left: 20px;
padding-right: 20px;
.clearfix();
> .sidebar {
position: absolute;
top: 0;
left: 20px;
width: 220px;
}
// TODO in v2: rename this and .popover .content to be more specific
> .content {
margin-left: 240px;
}
}
// BASE STYLES
// -----------
// Links
a {
color: @linkColor;
text-decoration: none;
line-height: inherit;
font-weight: inherit;
&:hover {
color: @linkColorHover;
text-decoration: underline;
}
}
// Quick floats
.pull-right {
float: right;
}
.pull-left {
float: left;
}
// Toggling content
.hide {
display: none;
}
.show {
display: block;
}
// GRID SYSTEM
// -----------
// To customize the grid system, bring up the variables.less file and change the column count, size, and gutter there
.row {
.clearfix();
margin-left: -@gridGutterWidth;
}
// Find all .span# classes within .row and give them the necessary properties for grid columns (supported by all browsers back to IE7)
// Credit to @dhg for the idea
.row > [class*="span"] {
.gridColumn();
}
// Default columns
.span1 { .columns(1); }
.span2 { .columns(2); }
.span3 { .columns(3); }
.span4 { .columns(4); }
.span5 { .columns(5); }
.span6 { .columns(6); }
.span7 { .columns(7); }
.span8 { .columns(8); }
.span9 { .columns(9); }
.span10 { .columns(10); }
.span11 { .columns(11); }
.span12 { .columns(12); }
.span13 { .columns(13); }
.span14 { .columns(14); }
.span15 { .columns(15); }
.span16 { .columns(16); }
// For optional 24-column grid
.span17 { .columns(17); }
.span18 { .columns(18); }
.span19 { .columns(19); }
.span20 { .columns(20); }
.span21 { .columns(21); }
.span22 { .columns(22); }
.span23 { .columns(23); }
.span24 { .columns(24); }
// Offset column options
.row {
> .offset1 { .offset(1); }
> .offset2 { .offset(2); }
> .offset3 { .offset(3); }
> .offset4 { .offset(4); }
> .offset5 { .offset(5); }
> .offset6 { .offset(6); }
> .offset7 { .offset(7); }
> .offset8 { .offset(8); }
> .offset9 { .offset(9); }
> .offset10 { .offset(10); }
> .offset11 { .offset(11); }
> .offset12 { .offset(12); }
}
// Unique column sizes for 16-column grid
.span-one-third { width: 300px; }
.span-two-thirds { width: 620px; }
.row {
> .offset-one-third { margin-left: 340px; }
> .offset-two-thirds { margin-left: 660px; }
}

View File

@ -0,0 +1,224 @@
/*
* Tables.less
* Tables for, you guessed it, tabular data
* ---------------------------------------- */
// BASELINE STYLES
// ---------------
table {
width: 100%;
margin-bottom: @baseline;
padding: 0;
font-size: @basefont;
border-collapse: collapse;
th,
td {
padding: 10px 10px 9px;
line-height: @baseline;
text-align: left;
}
th {
padding-top: 9px;
font-weight: bold;
vertical-align: middle;
}
td {
vertical-align: top;
border-top: 1px solid #ddd;
}
// When scoped to row, fix th in tbody
tbody th {
border-top: 1px solid #ddd;
vertical-align: top;
}
}
// CONDENSED VERSION
// -----------------
.condensed-table {
th,
td {
padding: 5px 5px 4px;
}
}
// BORDERED VERSION
// ----------------
.bordered-table {
border: 1px solid #ddd;
border-collapse: separate; // Done so we can round those corners!
*border-collapse: collapse; /* IE7, collapse table to remove spacing */
.border-radius(4px);
th + th,
td + td,
th + td {
border-left: 1px solid #ddd;
}
thead tr:first-child th:first-child,
tbody tr:first-child td:first-child {
.border-radius(4px 0 0 0);
}
thead tr:first-child th:last-child,
tbody tr:first-child td:last-child {
.border-radius(0 4px 0 0);
}
tbody tr:last-child td:first-child {
.border-radius(0 0 0 4px);
}
tbody tr:last-child td:last-child {
.border-radius(0 0 4px 0);
}
}
// TABLE CELL SIZES
// ----------------
// This is a duplication of the main grid .columns() mixin, but subtracts 20px to account for input padding and border
.tableColumns(@columnSpan: 1) {
width: ((@gridColumnWidth - 20) * @columnSpan) + ((@gridColumnWidth - 20) * (@columnSpan - 1));
}
table {
// Default columns
.span1 { .tableColumns(1); }
.span2 { .tableColumns(2); }
.span3 { .tableColumns(3); }
.span4 { .tableColumns(4); }
.span5 { .tableColumns(5); }
.span6 { .tableColumns(6); }
.span7 { .tableColumns(7); }
.span8 { .tableColumns(8); }
.span9 { .tableColumns(9); }
.span10 { .tableColumns(10); }
.span11 { .tableColumns(11); }
.span12 { .tableColumns(12); }
.span13 { .tableColumns(13); }
.span14 { .tableColumns(14); }
.span15 { .tableColumns(15); }
.span16 { .tableColumns(16); }
}
// ZEBRA-STRIPING
// --------------
// Default zebra-stripe styles (alternating gray and transparent backgrounds)
.zebra-striped {
tbody {
tr:nth-child(odd) td,
tr:nth-child(odd) th {
background-color: #f9f9f9;
}
tr:hover td,
tr:hover th {
background-color: #f5f5f5;
}
}
}
table {
// Tablesorting styles w/ jQuery plugin
.header {
cursor: pointer;
&:after {
content: "";
float: right;
margin-top: 7px;
border-width: 0 4px 4px;
border-style: solid;
border-color: #000 transparent;
visibility: hidden;
}
}
// Style the sorted column headers (THs)
.headerSortUp,
.headerSortDown {
background-color: rgba(141,192,219,.25);
text-shadow: 0 1px 1px rgba(255,255,255,.75);
}
// Style the ascending (reverse alphabetical) column header
.header:hover {
&:after {
visibility:visible;
}
}
// Style the descending (alphabetical) column header
.headerSortDown,
.headerSortDown:hover {
&:after {
visibility:visible;
.opacity(60);
}
}
// Style the ascending (reverse alphabetical) column header
.headerSortUp {
&:after {
border-bottom: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #000;
visibility:visible;
.box-shadow(none); //can't add boxshadow to downward facing arrow :(
.opacity(60);
}
}
// Blue Table Headings
.blue {
color: @blue;
border-bottom-color: @blue;
}
.headerSortUp.blue,
.headerSortDown.blue {
background-color: lighten(@blue, 40%);
}
// Green Table Headings
.green {
color: @green;
border-bottom-color: @green;
}
.headerSortUp.green,
.headerSortDown.green {
background-color: lighten(@green, 40%);
}
// Red Table Headings
.red {
color: @red;
border-bottom-color: @red;
}
.headerSortUp.red,
.headerSortDown.red {
background-color: lighten(@red, 50%);
}
// Yellow Table Headings
.yellow {
color: @yellow;
border-bottom-color: @yellow;
}
.headerSortUp.yellow,
.headerSortDown.yellow {
background-color: lighten(@yellow, 40%);
}
// Orange Table Headings
.orange {
color: @orange;
border-bottom-color: @orange;
}
.headerSortUp.orange,
.headerSortDown.orange {
background-color: lighten(@orange, 40%);
}
// Purple Table Headings
.purple {
color: @purple;
border-bottom-color: @purple;
}
.headerSortUp.purple,
.headerSortDown.purple {
background-color: lighten(@purple, 40%);
}
}

View File

@ -0,0 +1,187 @@
/* Typography.less
* Headings, body text, lists, code, and more for a versatile and durable typography system
* ---------------------------------------------------------------------------------------- */
// BODY TEXT
// ---------
p {
#font > .shorthand(normal,@basefont,@baseline);
margin-bottom: @baseline / 2;
small {
font-size: @basefont - 2;
color: @grayLight;
}
}
// HEADINGS
// --------
h1, h2, h3, h4, h5, h6 {
font-weight: bold;
color: @grayDark;
small {
color: @grayLight;
}
}
h1 {
margin-bottom: @baseline;
font-size: 30px;
line-height: @baseline * 2;
small {
font-size: 18px;
}
}
h2 {
font-size: 24px;
line-height: @baseline * 2;
small {
font-size: 14px;
}
}
h3, h4, h5, h6 {
line-height: @baseline * 2;
}
h3 {
font-size: 18px;
small {
font-size: 14px;
}
}
h4 {
font-size: 16px;
small {
font-size: 12px;
}
}
h5 {
font-size: 14px;
}
h6 {
font-size: 13px;
color: @grayLight;
text-transform: uppercase;
}
// COLORS
// ------
// Unordered and Ordered lists
ul, ol {
margin: 0 0 @baseline 25px;
}
ul ul,
ul ol,
ol ol,
ol ul {
margin-bottom: 0;
}
ul {
list-style: disc;
}
ol {
list-style: decimal;
}
li {
line-height: @baseline;
color: @gray;
}
ul.unstyled {
list-style: none;
margin-left: 0;
}
// Description Lists
dl {
margin-bottom: @baseline;
dt, dd {
line-height: @baseline;
}
dt {
font-weight: bold;
}
dd {
margin-left: @baseline / 2;
}
}
// MISC
// ----
// Horizontal rules
hr {
margin: 20px 0 19px;
border: 0;
border-bottom: 1px solid #eee;
}
// Emphasis
strong {
font-style: inherit;
font-weight: bold;
}
em {
font-style: italic;
font-weight: inherit;
line-height: inherit;
}
.muted {
color: @grayLight;
}
// Blockquotes
blockquote {
margin-bottom: @baseline;
border-left: 5px solid #eee;
padding-left: 15px;
p {
#font > .shorthand(300,14px,@baseline);
margin-bottom: 0;
}
small {
display: block;
#font > .shorthand(300,12px,@baseline);
color: @grayLight;
&:before {
content: '\2014 \00A0';
}
}
}
// Addresses
address {
display: block;
line-height: @baseline;
margin-bottom: @baseline;
}
// Inline and block code styles
code, pre {
padding: 0 3px 2px;
font-family: Monaco, Andale Mono, Courier New, monospace;
font-size: 12px;
.border-radius(3px);
}
code {
background-color: lighten(@orange, 40%);
color: rgba(0,0,0,.75);
padding: 1px 3px;
}
pre {
background-color: #f5f5f5;
display: block;
padding: (@baseline - 1) / 2;
margin: 0 0 @baseline;
line-height: @baseline;
font-size: 12px;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.15);
.border-radius(3px);
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}

View File

@ -0,0 +1,60 @@
/* Variables.less
* Variables to customize the look and feel of Bootstrap
* ----------------------------------------------------- */
// Links
@linkColor: #0069d6;
@linkColorHover: darken(@linkColor, 15);
// Grays
@black: #000;
@grayDark: lighten(@black, 25%);
@gray: lighten(@black, 50%);
@grayLight: lighten(@black, 75%);
@grayLighter: lighten(@black, 90%);
@white: #fff;
// Accent Colors
@blue: #049CDB;
@blueDark: #0064CD;
@green: #46a546;
@red: #9d261d;
@yellow: #ffc40d;
@orange: #f89406;
@pink: #c3325f;
@purple: #7a43b6;
// Baseline grid
@basefont: 13px;
@baseline: 18px;
// Griditude
// Modify the grid styles in mixins.less
@gridColumns: 16;
@gridColumnWidth: 40px;
@gridGutterWidth: 20px;
@extraSpace: (@gridGutterWidth * 2); // For our grid calculations
@siteWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
// Color Scheme
// Use this to roll your own color schemes if you like (unused by Bootstrap by default)
@baseColor: @blue; // Set a base color
@complement: spin(@baseColor, 180); // Determine a complementary color
@split1: spin(@baseColor, 158); // Split complements
@split2: spin(@baseColor, -158);
@triad1: spin(@baseColor, 135); // Triads colors
@triad2: spin(@baseColor, -135);
@tetra1: spin(@baseColor, 90); // Tetra colors
@tetra2: spin(@baseColor, -90);
@analog1: spin(@baseColor, 22); // Analogs colors
@analog2: spin(@baseColor, -22);
// More variables coming soon:
// - @basefont to @baseFontSize
// - @baseline to @baseLineHeight
// - @baseFontFamily
// - @primaryButtonColor
// - anything else? File an issue on GitHub

View File

@ -1,484 +0,0 @@
/*
Copyright (c) 2011, OpenERP S.A.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
nova = (function() {
var lib = {};
lib.internal = {};
/*
* (Almost) unmodified John Resig's inheritance.
*
* Defines The Class object. That object can be used to define and inherit classes using
* the extend() method.
*
* Example:
*
* var Person = nova.Class.extend({
* init: function(isDancing){
* this.dancing = isDancing;
* },
* dance: function(){
* return this.dancing;
* }
* });
*
* The init() method act as a constructor. This class can be instancied this way:
*
* var person = new Person(true);
* person.dance();
*
* The Person class can also be extended again:
*
* var Ninja = Person.extend({
* init: function(){
* this._super( false );
* },
* dance: function(){
* // Call the inherited version of dance()
* return this._super();
* },
* swingSword: function(){
* return true;
* }
* });
*
* When extending a class, each re-defined method can use this._super() to call the previous
* implementation of that method.
*/
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
this.Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
for(el in this) {
Class[el] = this[el];
}
return Class;
};
}).call(lib);
// end of John Resig's code
/**
* Mixin to express the concept of destroying an object.
* When an object is destroyed, it should release any resource
* it could have reserved before.
*/
lib.DestroyableMixin = {
init: function() {
this.__destroyableDestroyed = false;
},
/**
* Returns true if destroy() was called on the current object.
*/
isDestroyed : function() {
return this.__destroyableDestroyed;
},
/**
* Inform the object it should destroy itself, releasing any
* resource it could have reserved.
*/
destroy : function() {
this.__destroyableDestroyed = true;
}
};
/**
* Mixin to structure objects' life-cycles folowing a parent-children
* relationship. Each object can a have a parent and multiple children.
* When an object is destroyed, all its children are destroyed too.
*/
lib.ParentedMixin = _.extend({}, lib.DestroyableMixin, {
__parentedMixin : true,
init: function() {
lib.DestroyableMixin.init.call(this);
this.__parentedChildren = [];
this.__parentedParent = null;
},
/**
* Set the parent of the current object. When calling this method, the
* parent will also be informed and will return the current object
* when its getChildren() method is called. If the current object did
* already have a parent, it is unregistered before, which means the
* previous parent will not return the current object anymore when its
* getChildren() method is called.
*/
setParent : function(parent) {
if (this.getParent()) {
if (this.getParent().__parentedMixin) {
this.getParent().__parentedChildren = _.without(this
.getParent().getChildren(), this);
}
}
this.__parentedParent = parent;
if (parent && parent.__parentedMixin) {
parent.__parentedChildren.push(this);
}
},
/**
* Return the current parent of the object (or null).
*/
getParent : function() {
return this.__parentedParent;
},
/**
* Return a list of the children of the current object.
*/
getChildren : function() {
return _.clone(this.__parentedChildren);
},
destroy : function() {
_.each(this.getChildren(), function(el) {
el.destroy();
});
this.setParent(undefined);
lib.DestroyableMixin.destroy.call(this);
}
});
/*
* Yes, we steal Backbone's events :)
*
* This class just handle the dispatching of events, it is not meant to be extended,
* nor used directly. All integration with parenting and automatic unregistration of
* events is done in EventDispatcherMixin.
*/
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
lib.internal.Events = lib.Class.extend({
on : function(events, callback, context) {
var ev;
events = events.split(/\s+/);
var calls = this._callbacks || (this._callbacks = {});
while (ev = events.shift()) {
var list = calls[ev] || (calls[ev] = {});
var tail = list.tail || (list.tail = list.next = {});
tail.callback = callback;
tail.context = context;
list.tail = tail.next = {};
}
return this;
},
off : function(events, callback, context) {
var ev, calls, node;
if (!events) {
delete this._callbacks;
} else if (calls = this._callbacks) {
events = events.split(/\s+/);
while (ev = events.shift()) {
node = calls[ev];
delete calls[ev];
if (!callback || !node)
continue;
while ((node = node.next) && node.next) {
if (node.callback === callback
&& (!context || node.context === context))
continue;
this.on(ev, node.callback, node.context);
}
}
}
return this;
},
trigger : function(events) {
var event, node, calls, tail, args, all, rest;
if (!(calls = this._callbacks))
return this;
all = calls['all'];
(events = events.split(/\s+/)).push(null);
// Save references to the current heads & tails.
while (event = events.shift()) {
if (all)
events.push({
next : all.next,
tail : all.tail,
event : event
});
if (!(node = calls[event]))
continue;
events.push({
next : node.next,
tail : node.tail
});
}
rest = Array.prototype.slice.call(arguments, 1);
while (node = events.pop()) {
tail = node.tail;
args = node.event ? [ node.event ].concat(rest) : rest;
while ((node = node.next) !== tail) {
node.callback.apply(node.context || this, args);
}
}
return this;
}
});
// end of Backbone's events class
lib.EventDispatcherMixin = _.extend({}, lib.ParentedMixin, {
__eventDispatcherMixin: true,
init: function() {
lib.ParentedMixin.init.call(this);
this.__edispatcherEvents = new lib.internal.Events();
this.__edispatcherRegisteredEvents = [];
},
on: function(events, dest, func) {
var self = this;
events = events.split(/\s+/);
_.each(events, function(eventName) {
self.__edispatcherEvents.on(eventName, func, dest);
if (dest && dest.__eventDispatcherMixin) {
dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
}
});
return this;
},
off: function(events, dest, func) {
var self = this;
events = events.split(/\s+/);
_.each(events, function(eventName) {
self.__edispatcherEvents.off(eventName, func, dest);
if (dest && dest.__eventDispatcherMixin) {
dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
return !(el.name === eventName && el.func === func && el.source === self);
});
}
});
return this;
},
trigger: function(events) {
this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
return this;
},
destroy: function() {
var self = this;
_.each(this.__edispatcherRegisteredEvents, function(event) {
event.source.__edispatcherEvents.off(event.name, event.func, self);
});
this.__edispatcherRegisteredEvents = [];
this.__edispatcherEvents.off();
lib.ParentedMixin.destroy.call(this);
}
});
lib.GetterSetterMixin = _.extend({}, lib.EventDispatcherMixin, {
init: function() {
lib.EventDispatcherMixin.init.call(this);
this.__getterSetterInternalMap = {};
},
set: function(map) {
var self = this;
var changed = false;
_.each(map, function(val, key) {
var tmp = self.__getterSetterInternalMap[key];
if (tmp === val)
return;
changed = true;
self.__getterSetterInternalMap[key] = val;
self.trigger("change:" + key, self, {
oldValue: tmp,
newValue: val
});
});
if (changed)
self.trigger("change", self);
},
get: function(key) {
return this.__getterSetterInternalMap[key];
}
});
lib.Widget = lib.Class.extend(_.extend({}, lib.GetterSetterMixin, {
/**
* Tag name when creating a default $element.
* @type string
*/
tagName: 'div',
/**
* Constructs the widget and sets its parent if a parent is given.
*
* @constructs openerp.web.Widget
* @extends openerp.web.CallbackEnabled
*
* @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
* @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
* to bind the current Widget to an already existing part of the DOM, which is not compatible
* with the DOM insertion methods provided by the current implementation of Widget. So
* for new components this argument should not be provided any more.
*/
init: function(parent) {
lib.GetterSetterMixin.init.call(this);
this.$element = $(document.createElement(this.tagName));
this.setParent(parent);
},
/**
* Destroys the current widget, also destroys all its children before destroying itself.
*/
destroy: function() {
_.each(this.getChildren(), function(el) {
el.destroy();
});
if(this.$element != null) {
this.$element.remove();
}
lib.GetterSetterMixin.destroy.call(this);
},
/**
* Renders the current widget and appends it to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
appendTo: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
self.$element.appendTo(t);
}, target);
},
/**
* Renders the current widget and prepends it to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
prependTo: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
self.$element.prependTo(t);
}, target);
},
/**
* Renders the current widget and inserts it after to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
insertAfter: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
self.$element.insertAfter(t);
}, target);
},
/**
* Renders the current widget and inserts it before to the given jQuery object or Widget.
*
* @param target A jQuery object or a Widget instance.
*/
insertBefore: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
self.$element.insertBefore(t);
}, target);
},
/**
* Renders the current widget and replaces the given jQuery object.
*
* @param target A jQuery object or a Widget instance.
*/
replace: function(target) {
return this.__widgetRenderAndInsert(_.bind(function(t) {
this.$element.replaceAll(t);
}, this), target);
},
__widgetRenderAndInsert: function(insertion, target) {
this.renderElement();
insertion(target);
return this.start();
},
/**
* This is the method to implement to render the Widget.
*/
renderElement: function() {},
/**
* Method called after rendering. Mostly used to bind actions, perform asynchronous
* calls, etc...
*
* By convention, the method should return a promise to inform the caller when
* this widget has been initialized.
*
* @returns {jQuery.Deferred}
*/
start: function() {}
}));
return lib;
})();

File diff suppressed because it is too large Load Diff

View File

@ -1,226 +0,0 @@
/**
* QUnit v1.2.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<link rel="stylesheet" href="qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="qunit.js"></script>
<script type="text/javascript" src="jquery-1.6.4.js"></script>
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="../src/nova.js"></script>
<script type="text/javascript" src="test.js"></script>
</head>
<body>
<h1 id="qunit-header">QUnit example</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body>
</html>

View File

@ -1,999 +0,0 @@
// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) { return new wrapper(obj); };
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root['_'] = _;
}
// Current version.
_.VERSION = '1.3.1';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
if (obj.length === +obj.length) results.length = obj.length;
return results;
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var reversed = _.toArray(obj).reverse();
if (context && !initial) iterator = _.bind(iterator, context);
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function(obj, target) {
var found = false;
if (obj == null) return found;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
found = any(obj, function(value) {
return value === target;
});
return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Return the maximum element or (element-based computation).
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array.
_.shuffle = function(obj) {
var shuffled = [], rand;
each(obj, function(value, index, list) {
if (index == 0) {
shuffled[0] = value;
} else {
rand = Math.floor(Math.random() * (index + 1));
shuffled[index] = shuffled[rand];
shuffled[rand] = value;
}
});
return shuffled;
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}), 'value');
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, val) {
var result = {};
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
each(obj, function(value, index) {
var key = iterator(value, index);
(result[key] || (result[key] = [])).push(value);
});
return result;
};
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
iterator || (iterator = _.identity);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
_.toArray = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return slice.call(iterable);
if (_.isArguments(iterable)) return slice.call(iterable);
return _.values(iterable);
};
// Return the number of elements in an object.
_.size = function(obj) {
return _.toArray(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = function(array, n, guard) {
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the last entry of the array. Especcialy useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if ((n != null) && !guard) {
return slice.call(array, Math.max(array.length - n, 0));
} else {
return array[array.length - 1];
}
};
// Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = function(array, index, guard) {
return slice.call(array, (index == null) || guard ? 1 : index);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, function(value){ return !!value; });
};
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
return _.reduce(array, function(memo, value) {
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
memo[memo.length] = value;
return memo;
}, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator) {
var initial = iterator ? _.map(array, iterator) : array;
var result = [];
_.reduce(initial, function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
memo[memo.length] = el;
result[result.length] = array[i];
}
return memo;
}, []);
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays. (Aliased as "intersect" for back-compat.)
_.intersection = _.intersect = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = _.flatten(slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.include(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
return results;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i, l;
if (isSorted) {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item) {
if (array == null) return -1;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (i in array && array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(len);
while(idx < len) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(func, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
var context, args, timeout, throttling, more;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
return function() {
context = this; args = arguments;
var later = function() {
timeout = null;
if (more) func.apply(context, args);
whenDone();
};
if (!timeout) timeout = setTimeout(later, wait);
if (throttling) {
more = true;
} else {
func.apply(context, args);
}
whenDone();
throttling = true;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
return memo = func.apply(this, arguments);
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func].concat(slice.call(arguments, 0));
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
if (times <= 0) return func();
return function() {
if (--times < 1) { return func.apply(this, arguments); }
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
return _.map(obj, _.identity);
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function.
function eq(a, b, stack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// Invoke a custom `isEqual` method if one is provided.
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (stack[length] == a) return true;
}
// Add the first object to the stack of traversed objects.
stack.push(a);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
}
}
} else {
// Objects with different constructors are not equivalent.
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
stack.pop();
return result;
}
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Is a given variable an arguments object?
_.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Is a given value a function?
_.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
};
// Is a given value a string?
_.isString = function(obj) {
return toString.call(obj) == '[object String]';
};
// Is a given value a number?
_.isNumber = function(obj) {
return toString.call(obj) == '[object Number]';
};
// Is the given value `NaN`?
_.isNaN = function(obj) {
// `NaN` is the only value for which `===` is not reflexive.
return obj !== obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value a date?
_.isDate = function(obj) {
return toString.call(obj) == '[object Date]';
};
// Is the given value a regular expression?
_.isRegExp = function(obj) {
return toString.call(obj) == '[object RegExp]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Has own property?
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function (n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
// Escape a string for HTML interpolation.
_.escape = function(string) {
return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
addToWrapper(name, _[name] = obj[name]);
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /.^/;
// Within an interpolation, evaluation, or escaping, remove HTML escaping
// that had been previously added.
var unescape = function(code) {
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(str, data) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.escape || noMatch, function(match, code) {
return "',_.escape(" + unescape(code) + "),'";
})
.replace(c.interpolate || noMatch, function(match, code) {
return "'," + unescape(code) + ",'";
})
.replace(c.evaluate || noMatch, function(match, code) {
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ "');}return __p.join('');";
var func = new Function('obj', '_', tmpl);
if (data) return func(data, _);
return function(data) {
return func.call(this, data, _);
};
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// The OOP Wrapper
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function(obj) { this._wrapped = obj; };
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
return chain ? _(obj).chain() : obj;
};
// A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) {
wrapper.prototype[name] = function() {
var args = slice.call(arguments);
unshift.call(args, this._wrapped);
return result(func.apply(_, args), this._chain);
};
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
var wrapped = this._wrapped;
method.apply(wrapped, arguments);
var length = wrapped.length;
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
return result(wrapped, this._chain);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
return result(method.apply(this._wrapped, arguments), this._chain);
};
});
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
this._chain = true;
return this;
};
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
return this._wrapped;
};
}).call(this);

View File

@ -0,0 +1,22 @@
Copyright (c) 2011 Samuel Clay, @samuelclay, DocumentCloud
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,16 @@
__ ___ _ _____ _ _
\ \ / (_) | |/ ____| | | (_)
\ \ / / _ ___ _ _ __ _| | (___ ___ __ _ _ __ ___| |__ _ ___
\ \/ / | / __| | | |/ _` | |\___ \ / _ \/ _` | '__/ __| '_ \ | / __|
\ / | \__ \ |_| | (_| | |____) | __/ (_| | | | (__| | | |_| \__ \
\/ |_|___/\__,_|\__,_|_|_____/ \___|\__,_|_| \___|_| |_(_) |___/
_/ |
|__/
VisualSearch.js enhances ordinary search boxes with the ability to autocomplete
faceted search queries. Specify the facets for completion, along with the
completable values for any facet. You can retrieve the search query as a
structured object, so you don't have to parse the query string yourself.
For documentation, pre-packed downloads, demos, and tests, see:
http://documentcloud.github.com/visualsearch

View File

@ -0,0 +1,37 @@
require 'rubygems'
require 'jammit'
require 'fileutils'
desc "Use Jammit to compile the multiple versions of Visual Search"
task :build do
$VS_MIN = false
Jammit.package!({
:config_path => "assets.yml",
:output_folder => "build"
})
$VS_MIN = true
Jammit.package!({
:config_path => "assets.yml",
:output_folder => "build-min"
})
# Move the JSTs back to lib to accomodate the demo page.
FileUtils.mv("build/visualsearch_templates.js", "lib/js/templates/templates.js")
# Fix image url paths.
['build', 'build-min'].each do |build|
File.open("#{build}/visualsearch.css", 'r+') do |file|
css = file.read
css.gsub!(/url\((.*?)images\/embed\/icons/, 'url(../images/embed/icons')
file.rewind
file.write(css)
file.truncate(css.length)
end
end
end
desc "Build the docco documentation"
task :docs do
sh "docco lib/js/*.js lib/js/**/*.js"
end

View File

@ -0,0 +1,28 @@
embed_assets: datauri
javascript_compressor: closure
template_function: _.template
gzip_assets: <% if $VS_MIN %>on<% else %>off<% end %>
compress_assets: <% if $VS_MIN %>on<% else %>off<% end %>
javascripts:
dependencies:
- vendor/jquery-*.js
- vendor/jquery.ui.core.js
- vendor/jquery.ui.widget.js
- vendor/jquery.ui.position.js
- vendor/jquery.ui.*.js
- vendor/underscore-*.js
- vendor/backbone-*.js
visualsearch:
- lib/js/visualsearch.js
- lib/js/views/*.js
- lib/js/utils/*.js
- lib/js/models/*.js
- lib/js/templates/*.jst
<% unless $VS_MIN %>visualsearch_templates:
- lib/js/templates/*.jst
<% end %>
stylesheets:
visualsearch:
- lib/css/*.css

View File

@ -0,0 +1,310 @@
.VS-search .VS-icon {
background-repeat: no-repeat;
background-position: center center;
vertical-align: middle;
width: 16px; height: 16px;
}
.VS-search .VS-icon-cancel {
width: 11px; height: 11px;
background-position: center 0;
background-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAWCAYAAAAW5GZjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAb9JREFUeNqUUr1qAkEQ3j0khQp6kihaeGgEEa18gTQR0iRY+BaBSMDGwidIEUKqFL6BopgqBAJ5AMFGjUU0d4WHEvwJarvZ77gRIzGYgb1hZr+Z75vZ40IIzqTNZrPj8Xicn0wmmcViEXS73aaqqq+BQODG6/W+A8MBNk3zfDAY3C6Xy0O2ZS6X6zMSiVwHg8FHLjtq7Xb7RQKj7BeTzVCgJ5PJU2U0GhUk7REuMpkMi8fjFggeMeecrVYrFRId0CgTAgDDMFg4HLbA8IjJgHNgGEr0er0fQIphUmZAwdSUADUB4RFDsz3oSMF6CLzZkQqgGebz+Z75dDqNdTqdp13bgDmdTj2VSp0oWHg0Gr2UNH2Z/9o+yMv7K4/HY/C/XhDUfr//jl7QQVT9fp/V63VWqVRYt9tliUSCZbPZg1wux9Lp9PqFeK1Wu9A0DdXz7YM87i0FrVZLs4Fi1wmFQh/NZjOmVKvVgq7rR/QflMtlixGedjwcDlUpMQ9tbzalkAAB2/R297mNW+sT2wUbUnA//V/nYrH4QOBNABUQuFQq3TNMuc82sDVrz41G42yvPeODAwZQ0QzwiJEnzLcAAwBJ6WXlwoBgZAAAAABJRU5ErkJggg==");
cursor: pointer;
}
.VS-search .VS-icon-cancel:hover {
background-position: center -11px;
}
.VS-search .VS-icon-search {
width: 12px; height: 12px;
background-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUZJREFUeNpUUM2qgmAQzS8NiUgLzTIXLZQW1QuI9AY9QPSW9gQ9QiriwpJQEBVrVWT2d7p2L9xZzDdzZs7M+YYqy/J8Ptu2vd/v4zgeDAaqqk4mE47jar9GnU6nzWbjOA5FUa/Xq0Jns9l8Pud5vkpp58cwAOzhcBhFkeu6GNztdg3D+Db5vo9nOp2iiWGYTqdDCMFe4LquI0aVpGmKR9M0lmUbjQY8YiBJklTb4YkoilBzOBzq9TogeMQIJEmqmlAlo9EIyXa7tSyrKAp4xEBkWUb5q2k8Hh+PR8/zwjCEgufz+aESstvtoKnVan2GgY31kBkEAfT1ej1FUZDiNIIgrFYr9H1ug3teLpfH43G/3/FBUJGu1+s8z8FZLpc0mmiabrfbf5fEumazuVgsTNO8Xq+3242qRNT+G0CMz7IMzH6//xZgAA60tj6rqzxpAAAAAElFTkSuQmCC");
}
/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
/*
Eric Meyer's final reset.css
Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
*/
.VS-search div, .VS-search span, .VS-search a, .VS-search img,
.VS-search ul, .VS-search li, .VS-search form, .VS-search label,
.VS-interface ul, .VS-interface li, .VS-interface {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
.VS-search :focus {
outline: 0;
}
.VS-search {
line-height: 1;
color: black;
}
.VS-search ol, .VS-search ul {
list-style: none;
}
/* ===================== */
/* = General and Reset = */
/* ===================== */
.VS-search {
font-family: Arial, sans-serif;
color: #373737;
font-size: 12px;
}
.VS-search input {
display: block;
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
outline: none;
margin: 0; padding: 4px;
background: transparent;
font-size: 16px;
line-height: 20px;
width: 100%;
}
.VS-interface, .VS-search .dialog, .VS-search input {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
line-height: 1.1em;
}
/* ========== */
/* = Layout = */
/* ========== */
.VS-search .VS-search-box {
cursor: text;
position: relative;
background: transparent;
border: 2px solid #ccc;
border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px;
background-color: #fafafa;
-webkit-box-shadow: inset 0px 0px 3px #ccc;
-moz-box-shadow: inset 0px 0px 3px #ccc;
box-shadow: inset 0px 0px 3px #ccc;
min-height: 28px;
height: auto;
}
.VS-search .VS-search-box.VS-focus {
border-color: #acf;
-webkit-box-shadow: inset 0px 0px 3px #acf;
-moz-box-shadow: inset 0px 0px 3px #acf;
box-shadow: inset 0px 0px 3px #acf;
}
.VS-search .VS-search-inner {
position: relative;
margin: 0 20px 0 22px;
overflow: hidden;
}
.VS-search input {
width: 100px;
}
.VS-search input,
.VS-search .VS-input-width-tester {
padding: 6px 0;
float: left;
color: #808080;
font: 13px/17px Helvetica, Arial;
}
.VS-search.VS-focus input {
color: #606060;
}
.VS-search .VS-icon-search {
position: absolute;
left: 9px; top: 8px;
}
.VS-search .VS-icon-cancel {
position: absolute;
right: 9px; top: 8px;
}
/* ================ */
/* = Search Facet = */
/* ================ */
.VS-search .search_facet {
float: left;
margin: 0;
padding: 0 0 0 14px;
position: relative;
border: 1px solid transparent;
height: 20px;
margin: 3px -3px 3px 0;
}
.VS-search .search_facet.is_selected {
margin-left: -3px;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
background-color: #d2e6fd;
background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #d2e6fd, #b0d1f9);
border: 1px solid #6eadf5;
}
.VS-search .search_facet .category {
float: left;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
color: #808080;
padding: 8px 0 5px;
line-height: 13px;
cursor: pointer;
padding: 4px 0 0;
}
.VS-search .search_facet.is_selected .category {
margin-left: 3px;
}
.VS-search .search_facet .search_facet_input_container {
float: left;
}
.VS-search .search_facet input {
margin: 0;
padding: 0;
color: #000;
font-size: 13px;
line-height: 16px;
padding: 5px 0 5px 4px;
height: 16px;
width: auto;
z-index: 100;
position: relative;
padding-top: 1px;
padding-bottom: 2px;
padding-right: 3px;
}
.VS-search .search_facet.is_editing input,
.VS-search .search_facet.is_selected input {
color: #000;
}
.VS-search .search_facet .search_facet_remove {
position: absolute;
left: 0;
top: 4px;
}
.VS-search .search_facet.is_selected .search_facet_remove {
opacity: 0.4;
left: 3px;
filter: alpha(opacity=40);
background-position: center -11px;
}
.VS-search .search_facet .search_facet_remove:hover {
opacity: 1;
}
.VS-search .search_facet.is_editing .category,
.VS-search .search_facet.is_selected .category {
color: #000;
}
.VS-search .search_facet.search_facet_maybe_delete .category,
.VS-search .search_facet.search_facet_maybe_delete input {
color: darkred;
}
/* ================ */
/* = Search Input = */
/* ================ */
.VS-search .search_input {
height: 28px;
float: left;
margin-left: -1px;
}
.VS-search .search_input input {
padding: 6px 3px 6px 2px;
line-height: 10px;
height: 22px;
margin-top: -4px;
width: 10px;
z-index: 100;
min-width: 4px;
position: relative;
}
.VS-search .search_input.is_editing input {
color: #202020;
}
/* ================ */
/* = Autocomplete = */
/* ================ */
.VS-interface.ui-autocomplete {
position: absolute;
border: 1px solid #C0C0C0;
border-top: 1px solid #D9D9D9;
background-color: #F6F6F6;
cursor: pointer;
z-index: 10000;
padding: 0;
margin: 0;
width: auto;
min-width: 80px;
max-width: 220px;
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
font-size: 13px;
top: 5px;
opacity: 0.97;
box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5);
}
.VS-interface.ui-autocomplete .ui-autocomplete-category {
text-transform: capitalize;
font-size: 11px;
padding: 4px 4px 4px;
border-top: 1px solid #A2A2A2;
border-bottom: 1px solid #A2A2A2;
background-color: #B7B7B7;
text-shadow: 0 -1px 0 #999;
font-weight: bold;
color: white;
cursor: default;
}
.VS-interface.ui-autocomplete .ui-menu-item {
float: none;
}
.VS-interface.ui-autocomplete .ui-menu-item a {
color: #000;
outline: none;
display: block;
padding: 3px 4px 5px;
border-radius: none;
line-height: 1;
background-color: #F8F8F8;
background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #F8F8F8, #F3F3F3);
border-top: 1px solid #FAFAFA;
border-bottom: 1px solid #f0f0f0;
}
.VS-interface.ui-autocomplete .ui-menu-item a:active {
outline: none;
}
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover {
background-color: #6483F7;
background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #648bF5, #2465f3);
border-top: 1px solid #5b83ec;
border-bottom: 1px solid #1459e9;
border-left: none;
border-right: none;
color: white;
margin: 0;
}
.VS-interface.ui-autocomplete .ui-corner-all {
border-radius: 0;
}
.VS-interface.ui-autocomplete li {
list-style: none;
width: auto;
}

View File

@ -0,0 +1,310 @@
.VS-search .VS-icon {
background-repeat: no-repeat;
background-position: center center;
vertical-align: middle;
width: 16px; height: 16px;
}
.VS-search .VS-icon-cancel {
width: 11px; height: 11px;
background-position: center 0;
background-image: url(../images/embed/icons/cancel_search.png?1311104738);
cursor: pointer;
}
.VS-search .VS-icon-cancel:hover {
background-position: center -11px;
}
.VS-search .VS-icon-search {
width: 12px; height: 12px;
background-image: url(../images/embed/icons/search_glyph.png?1311104738);
}
/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
/*
Eric Meyer's final reset.css
Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
*/
.VS-search div, .VS-search span, .VS-search a, .VS-search img,
.VS-search ul, .VS-search li, .VS-search form, .VS-search label,
.VS-interface ul, .VS-interface li, .VS-interface {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
.VS-search :focus {
outline: 0;
}
.VS-search {
line-height: 1;
color: black;
}
.VS-search ol, .VS-search ul {
list-style: none;
}
/* ===================== */
/* = General and Reset = */
/* ===================== */
.VS-search {
font-family: Arial, sans-serif;
color: #373737;
font-size: 12px;
}
.VS-search input {
display: block;
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
outline: none;
margin: 0; padding: 4px;
background: transparent;
font-size: 16px;
line-height: 20px;
width: 100%;
}
.VS-interface, .VS-search .dialog, .VS-search input {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
line-height: 1.1em;
}
/* ========== */
/* = Layout = */
/* ========== */
.VS-search .VS-search-box {
cursor: text;
position: relative;
background: transparent;
border: 2px solid #ccc;
border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px;
background-color: #fafafa;
-webkit-box-shadow: inset 0px 0px 3px #ccc;
-moz-box-shadow: inset 0px 0px 3px #ccc;
box-shadow: inset 0px 0px 3px #ccc;
min-height: 28px;
height: auto;
}
.VS-search .VS-search-box.VS-focus {
border-color: #acf;
-webkit-box-shadow: inset 0px 0px 3px #acf;
-moz-box-shadow: inset 0px 0px 3px #acf;
box-shadow: inset 0px 0px 3px #acf;
}
.VS-search .VS-search-inner {
position: relative;
margin: 0 20px 0 22px;
overflow: hidden;
}
.VS-search input {
width: 100px;
}
.VS-search input,
.VS-search .VS-input-width-tester {
padding: 6px 0;
float: left;
color: #808080;
font: 13px/17px Helvetica, Arial;
}
.VS-search.VS-focus input {
color: #606060;
}
.VS-search .VS-icon-search {
position: absolute;
left: 9px; top: 8px;
}
.VS-search .VS-icon-cancel {
position: absolute;
right: 9px; top: 8px;
}
/* ================ */
/* = Search Facet = */
/* ================ */
.VS-search .search_facet {
float: left;
margin: 0;
padding: 0 0 0 14px;
position: relative;
border: 1px solid transparent;
height: 20px;
margin: 3px -3px 3px 0;
}
.VS-search .search_facet.is_selected {
margin-left: -3px;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
background-color: #d2e6fd;
background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #d2e6fd, #b0d1f9);
border: 1px solid #6eadf5;
}
.VS-search .search_facet .category {
float: left;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
color: #808080;
padding: 8px 0 5px;
line-height: 13px;
cursor: pointer;
padding: 4px 0 0;
}
.VS-search .search_facet.is_selected .category {
margin-left: 3px;
}
.VS-search .search_facet .search_facet_input_container {
float: left;
}
.VS-search .search_facet input {
margin: 0;
padding: 0;
color: #000;
font-size: 13px;
line-height: 16px;
padding: 5px 0 5px 4px;
height: 16px;
width: auto;
z-index: 100;
position: relative;
padding-top: 1px;
padding-bottom: 2px;
padding-right: 3px;
}
.VS-search .search_facet.is_editing input,
.VS-search .search_facet.is_selected input {
color: #000;
}
.VS-search .search_facet .search_facet_remove {
position: absolute;
left: 0;
top: 4px;
}
.VS-search .search_facet.is_selected .search_facet_remove {
opacity: 0.4;
left: 3px;
filter: alpha(opacity=40);
background-position: center -11px;
}
.VS-search .search_facet .search_facet_remove:hover {
opacity: 1;
}
.VS-search .search_facet.is_editing .category,
.VS-search .search_facet.is_selected .category {
color: #000;
}
.VS-search .search_facet.search_facet_maybe_delete .category,
.VS-search .search_facet.search_facet_maybe_delete input {
color: darkred;
}
/* ================ */
/* = Search Input = */
/* ================ */
.VS-search .search_input {
height: 28px;
float: left;
margin-left: -1px;
}
.VS-search .search_input input {
padding: 6px 3px 6px 2px;
line-height: 10px;
height: 22px;
margin-top: -4px;
width: 10px;
z-index: 100;
min-width: 4px;
position: relative;
}
.VS-search .search_input.is_editing input {
color: #202020;
}
/* ================ */
/* = Autocomplete = */
/* ================ */
.VS-interface.ui-autocomplete {
position: absolute;
border: 1px solid #C0C0C0;
border-top: 1px solid #D9D9D9;
background-color: #F6F6F6;
cursor: pointer;
z-index: 10000;
padding: 0;
margin: 0;
width: auto;
min-width: 80px;
max-width: 220px;
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
font-size: 13px;
top: 5px;
opacity: 0.97;
box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5);
}
.VS-interface.ui-autocomplete .ui-autocomplete-category {
text-transform: capitalize;
font-size: 11px;
padding: 4px 4px 4px;
border-top: 1px solid #A2A2A2;
border-bottom: 1px solid #A2A2A2;
background-color: #B7B7B7;
text-shadow: 0 -1px 0 #999;
font-weight: bold;
color: white;
cursor: default;
}
.VS-interface.ui-autocomplete .ui-menu-item {
float: none;
}
.VS-interface.ui-autocomplete .ui-menu-item a {
color: #000;
outline: none;
display: block;
padding: 3px 4px 5px;
border-radius: none;
line-height: 1;
background-color: #F8F8F8;
background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #F8F8F8, #F3F3F3);
border-top: 1px solid #FAFAFA;
border-bottom: 1px solid #f0f0f0;
}
.VS-interface.ui-autocomplete .ui-menu-item a:active {
outline: none;
}
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover {
background-color: #6483F7;
background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #648bF5, #2465f3);
border-top: 1px solid #5b83ec;
border-bottom: 1px solid #1459e9;
border-left: none;
border-right: none;
color: white;
margin: 0;
}
.VS-interface.ui-autocomplete .ui-corner-all {
border-radius: 0;
}
.VS-interface.ui-autocomplete li {
list-style: none;
width: auto;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,453 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>DocumentCloud's VisualSearch.js</title>
<style>
body {
font-size: 16px;
line-height: 24px;
background: #FEF3CA;
color: #022;
height: 100%;
font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif;
}
div.container {
width: 720px;
margin: 50px 0 50px 50px;
}
p, li {
margin: 16px 0 16px 0;
width: 550px;
}
p.break {
margin-top: 35px;
}
ol {
padding-left: 24px;
}
ol li {
font-weight: bold;
margin-left: 0;
}
a, a:visited {
padding: 0 2px;
text-decoration: none;
background: #f0c095;
color: #252519;
}
a:active, a:hover {
color: #FFF;
background: #C25D00;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 40px;
}
b.header {
font-size: 18px;
}
span.alias {
font-size: 14px;
font-style: italic;
margin-left: 20px;
}
table {
margin: 16px 0; padding: 0;
}
tr, td, th {
margin: 0; padding: 0;
text-align: left;
}
th {
padding: 24px 0 0;
}
tr:first-child th {
padding-top: 0;
}
td {
padding: 6px 15px 6px 0;
}
td.definition {
line-height: 18px;
font-size: 14px;
}
table.downloads td {
padding-left: 18px;
}
.demo-hint {
font-size: 13px;
margin: 0 0 12px 12px;
font-weight: normal;
}
#VS code, #VS pre, #VS tt {
font-family: Monaco, Consolas, "Lucida Console", monospace;
font-size: 12px;
line-height: 18px;
color: #444;
background: none;
}
#VS code {
margin-left: 8px;
padding: 0 0 0 12px;
font-weight: normal;
}
#VS pre {
font-size: 12px;
padding: 2px 0 2px 0;
border-left: 6px solid #829C37;
margin: 12px 0;
}
#search_query {
margin: 18px 0;
opacity: 0;
}
#search_query .raquo {
font-size: 18px;
line-height: 12px;
font-weight: bold;
margin-right: 4px;
}
#search_query2 {
margin: 18px 0;
opacity: 0;
}
#search_query2 .raquo {
font-size: 18px;
line-height: 12px;
font-weight: bold;
margin-right: 4px;
}
</style>
<link rel="stylesheet" href="lib/css/reset.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="lib/css/icons.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="lib/css/workspace.css" type="text/css" media="screen" charset="utf-8">
<script src="vendor/jquery-1.6.1.js" type="text/javascript" charset="utf-8"></script>
<!-- <script src="vendor/backported/jquery-1.4.4.js" type="text/javascript" charset="utf-8"></script> -->
<script src="vendor/jquery.ui.core.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/jquery.ui.widget.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/jquery.ui.position.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/jquery.ui.autocomplete.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/underscore-1.1.5.js" type="text/javascript" charset="utf-8"></script>
<script src="vendor/backbone-0.5.0.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/visualsearch.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/views/search_box.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/views/search_facet.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/views/search_input.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/models/search_facets.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/models/search_query.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/backbone_extensions.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/hotkeys.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/jquery_extensions.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/search_parser.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/utils/inflector.js" type="text/javascript" charset="utf-8"></script>
<script src="lib/js/templates/templates.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div class="container" id="VS">
<h1><a href="index.html">VisualSearch.js</a></h1>
<p>
<a href="http://github.com/documentcloud/visualsearch">VisualSearch.js</a>
enhances ordinary search boxes with the ability to autocomplete
faceted search queries. Specify the facets for completion, along with the
completable values for any facet. You can retrieve the search query as a
structured object, so you don't have to parse the query string yourself.
</p>
<p>
<a href="docs/visualsearch.html">The complete annotated source code</a>
is also available.
</p>
<p>
The project is
<a href="http://github.com/documentcloud/visualsearch/">hosted on GitHub</a>.
You can report bugs and discuss features on the
<a href="http://github.com/documentcloud/visualsearch/issues">issues page</a>,
on Freenode in the <tt>#documentcloud</tt> channel,
or send tweets to <a href="http://twitter.com/documentcloud">@documentcloud</a>.
</p>
<p>
<i>VisualSearch.js is an open-source component of <a href="http://documentcloud.org/">DocumentCloud</a>.</i>
</p>
<h2 id="demo">Demo <span class="demo-hint"><i>Try searching for: <b>account</b>, <b>filter</b>, <b>access</b>, <b>title</b>, <b>city</b>, <b>state</b>, or <b>country</b>.</i></span></h2>
<div id="search_box_container"></div>
<div id="search_query">&nbsp;</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
window.visualSearch = VS.init({
container : $('#search_box_container'),
query : 'country: "South Africa" account: 5-samuel title: "Pentagon Papers"',
// query : '',
unquotable : [
'text',
'account',
'filter',
'access'
],
callbacks : {
search : function(query, searchCollection) {
var $query = $('#search_query');
$query.stop().animate({opacity : 1}, {duration: 300, queue: false});
$query.html('<span class="raquo">&raquo;</span> You searched for: <b>' + searchCollection.serialize() + '</b>');
clearTimeout(window.queryHideDelay);
window.queryHideDelay = setTimeout(function() {
$query.animate({
opacity : 0
}, {
duration: 1000,
queue: false
});
}, 2000);
},
valueMatches : function(category, searchTerm, callback) {
switch (category) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
case 'city':
callback([
'Cleveland',
'New York City',
'Brooklyn',
'Manhattan',
'Queens',
'The Bronx',
'Staten Island',
'San Francisco',
'Los Angeles',
'Seattle',
'London',
'Portland',
'Chicago',
'Boston'
])
break;
case 'state':
callback([
"Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida",
"Georgia", "Guam", "Hawaii", "Idaho", "Illinois",
"Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
"Texas", "Utah", "Vermont", "Virginia", "Virgin Islands",
"Washington", "West Virginia", "Wisconsin", "Wyoming"
]);
break
case 'country':
callback([
"China", "India", "United States", "Indonesia", "Brazil",
"Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan",
"Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt",
"Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo",
"France", "United Kingdom", "Italy", "Myanmar", "South Africa",
"South Korea", "Colombia", "Ukraine", "Spain", "Tanzania",
"Sudan", "Kenya", "Argentina", "Poland", "Algeria",
"Canada", "Uganda", "Morocco", "Iraq", "Nepal",
"Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan",
"Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique",
"Taiwan", "Syria", "Ivory Coast", "Australia", "Romania",
"Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile",
"Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi",
"Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia",
"Senegal", "Zimbabwe", "Chad", "Cuba", "Greece",
"Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea",
"Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary",
"Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan",
"Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria",
"Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea",
"Togo", "Libya", "Jordan", "Paraguay", "Laos",
"El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark",
"Slovakia", "Finland", "Eritrea", "Turkmenistan"
], {preserveOrder: true});
break;
}
},
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'address', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
]);
}
}
});
});
</script>
<div id="search_box_container2"></div>
<div id="search_query2">&nbsp;</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
var visualSearch = VS.init({
container : $('#search_box_container2'),
query : '',
// query : '',
unquotable : [
'text',
'account',
'filter',
'access'
],
callbacks : {
search : function(query, searchCollection) {
console.log(["query", searchCollection.facets(), query]);
var $query = $('#search_query2');
$query.stop().animate({opacity : 1}, {duration: 300, queue: false});
$query.html('<span class="raquo">&raquo;</span> You searched for: <b>' + searchCollection.serialize() + '</b>');
clearTimeout(window.queryHideDelay2);
window.queryHideDelay2 = setTimeout(function() {
$query.animate({
opacity : 0
}, {
duration: 1000,
queue: false
});
}, 2000);
},
valueMatches : function(category, searchTerm, callback) {
switch (category) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
case 'city':
callback([
'Cleveland',
'New York City',
'Brooklyn',
'Manhattan',
'Queens',
'The Bronx',
'Staten Island',
'San Francisco',
'Los Angeles',
'Seattle',
'London',
'Portland',
'Chicago',
'Boston'
])
break;
case 'state':
callback([
"Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida",
"Georgia", "Guam", "Hawaii", "Idaho", "Illinois",
"Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
"Texas", "Utah", "Vermont", "Virginia", "Virgin Islands",
"Washington", "West Virginia", "Wisconsin", "Wyoming"
]);
break
case 'country':
callback([
"China", "India", "United States", "Indonesia", "Brazil",
"Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan",
"Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt",
"Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo",
"France", "United Kingdom", "Italy", "Myanmar", "South Africa",
"South Korea", "Colombia", "Ukraine", "Spain", "Tanzania",
"Sudan", "Kenya", "Argentina", "Poland", "Algeria",
"Canada", "Uganda", "Morocco", "Iraq", "Nepal",
"Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan",
"Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique",
"Taiwan", "Syria", "Ivory Coast", "Australia", "Romania",
"Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile",
"Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi",
"Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia",
"Senegal", "Zimbabwe", "Chad", "Cuba", "Greece",
"Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea",
"Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary",
"Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan",
"Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria",
"Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea",
"Togo", "Libya", "Jordan", "Paraguay", "Laos",
"El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark",
"Slovakia", "Finland", "Eritrea", "Turkmenistan"
]);
break;
}
},
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'address', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
], {
preserveOrder: true
});
}
}
});
});
</script>
</div>
</body>
</html>

View File

@ -0,0 +1,537 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>DocumentCloud's VisualSearch.js</title>
<style>
body {
font-size: 16px;
line-height: 24px;
background: #FEF3CA;
height: 100%;
color: #022;
font-family: Arial;
font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif;
}
div.container {
width: 720px;
margin: 50px 0 50px 50px;
}
p, li {
margin: 16px 0 16px 0;
width: 550px;
}
p.break {
margin-top: 35px;
}
ol {
padding-left: 24px;
}
ol li {
font-weight: bold;
margin-left: 0;
}
a, a:visited {
padding: 0 2px;
text-decoration: none;
background: #f0c095;
color: #252519;
}
a:active, a:hover {
color: #000;
background: #e0a070;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 40px;
}
b.header {
font-size: 18px;
}
span.alias {
font-size: 14px;
font-style: italic;
margin-left: 20px;
}
table {
margin: 16px 0; padding: 0;
}
tr, td, th {
margin: 0; padding: 0;
text-align: left;
}
th {
padding: 24px 0 0;
}
tr:first-child th {
padding-top: 0;
}
td {
padding: 6px 15px 6px 0;
}
td.definition {
line-height: 18px;
font-size: 14px;
}
table.downloads td {
padding-left: 18px;
}
.demo-hint {
font-size: 13px;
margin: 0 0 12px 12px;
font-weight: normal;
}
#VS code, #VS pre, #VS tt {
font-family: Monaco, Consolas, "Lucida Console", monospace;
font-size: 12px;
line-height: 18px;
color: #444;
background: none;
}
#VS code {
margin-left: 8px;
padding: 0 0 0 12px;
font-weight: normal;
}
#VS pre {
font-size: 12px;
padding: 2px 0 2px 0;
border-left: 6px solid #829C37;
margin: 12px 0;
}
#search_query {
margin: 18px 0 -24px;
opacity: 0;
}
#search_query .raquo {
font-size: 18px;
line-height: 12px;
font-weight: bold;
margin-right: 4px;
}
.attribution {
margin: -12px 0 24px;
font-size: 14px;
}
</style>
<link rel="stylesheet" href="build-min/visualsearch-datauri.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="docs/assets/github.css" type="text/css" media="screen" charset="utf-8">
<script src="build-min/dependencies.js" type="text/javascript" charset="utf-8"></script>
<script src="build-min/visualsearch.js" type="text/javascript" charset="utf-8"></script>
<script src="docs/assets/highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="docs/assets/javascript.js" type="text/javascript" charset="utf-8"></script>
<script src="docs/assets/xml.js" type="text/javascript" charset="utf-8"></script>
<script>
hljs.initHighlightingOnLoad();
</script>
</head>
<body>
<div class="container" id="VS">
<h1>VisualSearch.js</h1>
<p class="attribution">
<i>
Created by <a href="http://github.com/samuelclay">Samuel Clay</a>,
<a href="http://twitter.com/samuelclay">@samuelclay</a>.
</i>
</p>
<p>
<a href="http://github.com/documentcloud/visualsearch">VisualSearch.js</a>
enhances ordinary search boxes with the ability to autocomplete
faceted search queries. Specify the facets for completion, along with the
completable values for any facet. You can retrieve the search query as a
structured object, so you don't have to parse the query string yourself.
</p>
<p>
<a href="https://www.documentcloud.org/public/#search/group%3A%20dcloud%20chair%3A%20%22Christopher%20Dodd%22%20TARP">Here's an example of a search on DocumentCloud.org that uses facets.</a>
</p>
<p>
The project is
<a href="http://github.com/documentcloud/visualsearch/">hosted on GitHub</a>.
You can report bugs and discuss features on the
<a href="http://github.com/documentcloud/visualsearch/issues">issues page</a>,
on Freenode in the <tt>#documentcloud</tt> channel,
or send tweets to <a href="http://twitter.com/documentcloud">@documentcloud</a>.
</p>
<p>
<i>VisualSearch.js is an open-source component of <a href="http://documentcloud.org/">DocumentCloud</a>.
<br />
<a href="docs/visualsearch.html">The complete annotated source code</a>
is also available.</i>
</p>
<h2>Table of Contents</h2>
<a href="#demo">Demo</a> | <a href="#downloads">Downloads</a> | <a href="#usage">Usage</a> | <a href="#changelog">Change Log</a>
<h2 id="demo">Demo <span class="demo-hint"><i>Try searching for: <b>account</b>, <b>filter</b>, <b>access</b>, <b>title</b>, <b>city</b>, <b>state</b>, or <b>country</b>.</i></span></h2>
<div id="search_box_container"></div>
<div id="search_query">&nbsp;</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
var visualSearch = VS.init({
container : $('#search_box_container'),
query : 'country: "United States" state: "New York" account: 5-samuel title: "Pentagon Papers"',
unquotable : [
'text',
'account',
'filter',
'access'
],
callbacks : {
search : function(query, searchCollection) {
var $query = $('#search_query');
var count = searchCollection.size();
$query.stop().animate({opacity : 1}, {duration: 300, queue: false});
$query.html('<span class="raquo">&raquo;</span> You searched for: ' +
'<b>' + (query || '<i>nothing</i>') + '</b>. ' +
'(' + count + ' facet' + (count==1 ? '' : 's') + ')');
clearTimeout(window.queryHideDelay);
window.queryHideDelay = setTimeout(function() {
$query.animate({
opacity : 0
}, {
duration: 1000,
queue: false
});
}, 2000);
},
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
]);
},
valueMatches : function(facet, searchTerm, callback) {
switch (facet) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
case 'city':
callback([
'Cleveland',
'New York City',
'Brooklyn',
'Manhattan',
'Queens',
'The Bronx',
'Staten Island',
'San Francisco',
'Los Angeles',
'Seattle',
'London',
'Portland',
'Chicago',
'Boston'
]);
break;
case 'state':
callback([
"Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida",
"Georgia", "Guam", "Hawaii", "Idaho", "Illinois",
"Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
"Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota",
"Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
"New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
"North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee",
"Texas", "Utah", "Vermont", "Virginia", "Virgin Islands",
"Washington", "West Virginia", "Wisconsin", "Wyoming"
]);
break;
case 'country':
callback([
"China", "India", "United States", "Indonesia", "Brazil",
"Pakistan", "Bangladesh", "Nigeria", "Russia", "Japan",
"Mexico", "Philippines", "Vietnam", "Ethiopia", "Egypt",
"Germany", "Turkey", "Iran", "Thailand", "D. R. of Congo",
"France", "United Kingdom", "Italy", "Myanmar", "South Africa",
"South Korea", "Colombia", "Ukraine", "Spain", "Tanzania",
"Sudan", "Kenya", "Argentina", "Poland", "Algeria",
"Canada", "Uganda", "Morocco", "Iraq", "Nepal",
"Peru", "Afghanistan", "Venezuela", "Malaysia", "Uzbekistan",
"Saudi Arabia", "Ghana", "Yemen", "North Korea", "Mozambique",
"Taiwan", "Syria", "Ivory Coast", "Australia", "Romania",
"Sri Lanka", "Madagascar", "Cameroon", "Angola", "Chile",
"Netherlands", "Burkina Faso", "Niger", "Kazakhstan", "Malawi",
"Cambodia", "Guatemala", "Ecuador", "Mali", "Zambia",
"Senegal", "Zimbabwe", "Chad", "Cuba", "Greece",
"Portugal", "Belgium", "Czech Republic", "Tunisia", "Guinea",
"Rwanda", "Dominican Republic", "Haiti", "Bolivia", "Hungary",
"Belarus", "Somalia", "Sweden", "Benin", "Azerbaijan",
"Burundi", "Austria", "Honduras", "Switzerland", "Bulgaria",
"Serbia", "Israel", "Tajikistan", "Hong Kong", "Papua New Guinea",
"Togo", "Libya", "Jordan", "Paraguay", "Laos",
"El Salvador", "Sierra Leone", "Nicaragua", "Kyrgyzstan", "Denmark",
"Slovakia", "Finland", "Eritrea", "Turkmenistan"
], {preserveOrder: true});
break;
}
}
}
});
});
</script>
<h2 id="downloads">Downloads <i style="padding-left: 12px; font-size:12px;">(Right-click, and use "Save As")</i></h2>
<table class="downloads">
<tr>
<th colspan="2">0. Everything (<tt>visualsearch.zip</tt>)</th>
</tr>
<tr>
<td><a href="https://github.com/documentcloud/visualsearch/zipball/master">Download everything</a></td>
</tr>
<tr>
<th colspan="2">1. VisualSearch JavaScript (<tt>visualsearch.js</tt>)</th>
</tr>
<tr>
<td><a href="build-min/visualsearch.js">Production Version (0.2.2)</a></td>
<td><i>8kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build/visualsearch.js">Development Version (0.2.2)</a></td>
<td><i>45kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<th colspan="2">2. VisualSearch Stylesheets (<tt>visualsearch.css</tt>)</th>
</tr>
<tr>
<td colspan="2"><i>You should include both the datauri and image urls versions. <a href="#css">See how to include both</a></i></td>
</tr>
<tr>
<td><a href="build-min/visualsearch-datauri.css">Production Version - datauri</a></td>
<td><i>4kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build-min/visualsearch.css">Production Version - image urls</a></td>
<td><i>4kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build/visualsearch.css">Development Version</a></td>
<td><i>8kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<th colspan="2">3. VisualSearch Images</th>
</tr>
<tr>
<td><img src="lib/images/embed/icons/search_glyph.png"> <a href="lib/images/embed/icons/search_glyph.png">Search Glyph</a></td>
<td><i>4kb, embedded in visualsearch-datauri.css</i></td>
</tr>
<tr>
<td><img src="lib/images/embed/icons/cancel_search.png"> <a href="lib/images/embed/icons/cancel_search.png">Cancel Button</a></td>
<td><i>4kb, embedded in visualsearch-datauri.css</i></td>
</tr>
<tr>
<th colspan="2">4. VisualSearch Dependencies (<tt>jQuery 1.4+, jQuery UI, Underscore.js, Backbone.js</tt>)</th>
</tr>
<tr>
<td colspan="2"><i>You should only include the dependencies you don't already have.</i></td>
</tr>
<tr>
<td><a href="build-min/dependencies.js">Production Version - All</a></td>
<td><i>49kb, Minified and Gzipped</i></td>
</tr>
<tr>
<td><a href="build/dependencies.js">Development Version - All</a></td>
<td><i>340kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="vendor/jquery-1.6.1.js">jQuery 1.6.1</a></td>
<td><i>238kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td>jQuery UI 1.8.13: <br /><a href="vendor/jquery.ui.core.js">Core</a> <a href="vendor/jquery.ui.position.js">Position</a> <a href="vendor/jquery.ui.widget.js">Widget</a> <a href="vendor/jquery.ui.autocomplete.js">Autocomplete</a></td>
<td><i>48kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="vendor/underscore-1.1.5.js">Underscore 1.1.5</a></td>
<td><i>29kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="vendor/backbone-0.5.0.js">Backbone 0.5.0</a></td>
<td><i>41kb, Uncompressed with Comments</i></td>
</tr>
</table>
<h2 id="usage">Usage</h2>
<p>To use VisualSearch.js on your site, follow these instructions on installation, configuration, and customization.</p>
<ol>
<li class="xml" id="css">Insert the JavaScript and CSS into your page:<br />
<pre><code>&lt;script src="visualsearch.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;!--[if (!IE)|(gte IE 8)]&gt;&lt;!--&gt;
&lt;link href="visualsearch-datauri.css" media="screen" rel="stylesheet" type="text/css" /&gt;
&lt;!--&lt;![endif]--&gt;
&lt;!--[if lte IE 7]&gt;&lt;!--&gt;
&lt;link href="visualsearch.css" media="screen" rel="stylesheet" type="text/css" /&gt;
&lt;!--&lt;![endif]--&gt;</code></pre>
</li>
<li>Initialize the Visual Search box:<br />
<pre class="javascript"><code>&lt;div class="visual_search"&gt;&lt;/div&gt;
&lt;script type="text/javascript" charset="utf-8"&gt;
$(document).ready(function() {
var visualSearch = VS.init({
container : $('.visual_search'),
query : '',
callbacks : {
search : function(query, searchCollection) {},
facetMatches : function(callback) {},
valueMatches : function(facet, searchTerm, callback) {}
}
});
});
&lt;/script&gt;</code></pre>
</li>
<li class="javascript">Customize the autocompleted facets and values:
<pre><code>callbacks : {
...
// These are the facets that will be autocompleted in an empty input.
facetMatches : function(callback) {
callback([
'account', 'filter', 'access', 'title',
{ label: 'city', category: 'location' },
{ label: 'address', category: 'location' },
{ label: 'country', category: 'location' },
{ label: 'state', category: 'location' },
]);
}
...
// These are the values that match specific categories, autocompleted
// in a category's input field. searchTerm can be used to filter the
// list on the server-side, prior to providing a list to the widget.
valueMatches : function(facet, searchTerm, callback) {
switch (facet) {
case 'account':
callback([
{ value: '1-amanda', label: 'Amanda' },
{ value: '2-aron', label: 'Aron' },
{ value: '3-eric', label: 'Eric' },
{ value: '4-jeremy', label: 'Jeremy' },
{ value: '5-samuel', label: 'Samuel' },
{ value: '6-scott', label: 'Scott' }
]);
break;
case 'filter':
callback(['published', 'unpublished', 'draft']);
break;
case 'access':
callback(['public', 'private', 'protected']);
break;
case 'title':
callback([
'Pentagon Papers',
'CoffeeScript Manual',
'Laboratory for Object Oriented Thinking',
'A Repository Grows in Brooklyn'
]);
break;
}
}
...
}</code></pre>
</li>
<li class="javascript">Inspect the Visual Search box
<pre><code>// Returns the unstructured search query
visualSearch.searchBox.value()
// "country: "South Africa" account: 5-samuel title: "Pentagon Papers""
// Returns an array of Facet model instances
visualSearch.searchQuery.facets()
// [FacetModel&lt;country:"South Africa">,
// FacetModel&lt;account:5-samuel>,
// FacetModel&lt;title:"Pentagon Papers">]
// Set the search query with raw text
visualSearch.searchBox.value("Country: US State: \"New York\" Key: Value")
</code></pre>
</li>
</ol>
<h2 id="changelog">Change Log</h2>
<p>
<b class="header">0.2.2</b> <i>March 10th, 2012</i><br />
If you do not want to automatically filter the value matches, you can pass an
options hash with <tt>preserveMatches: true</tt> as the second argument to the callback.
<a href="https://github.com/documentcloud/visualsearch/pull/44">See pull request #44</a> for details.
</p>
<p>
<b class="header">0.2.1</b> <i>November 14th, 2011</i><br />
The autocompleted facets and values that are provided by your callbacks <tt>facetMatches</tt>
and <tt>valueMatches</tt> can now preserve the order of items you give them. Simply pass an
options hash with <tt>preserveOrder: true</tt> as the second argument to the callback. See
<a href="demo.html">the demo page</a> for an example.
</p>
<p>
<b class="header">0.2.0</b> <i>August 10th, 2011</i><br />
Multiple instances of VisualSearch on a single page. <tt>VS.init</tt> now returns
a reference to the instance. The <tt>search</tt> callback now contains both the
serialized search query and a reference to the search query collection (as a
<a href="http://documentcloud.github.com/backbone/#Collection">Backbone.Collection</a>),
which can be used to manipulate each facet directly. See
<a href="docs/search_query.html">the source code for search_query.js</a> for available
methods on the collection.
</p>
<p>
<b class="header">0.1.0</b> <i>June 23rd, 2011</i><br />
Initial release of VisualSearch.js.
</p>
<p>
<a href="http://documentcloud.org/" title="A DocumentCloud Project" style="background:none;">
<img src="http://jashkenas.s3.amazonaws.com/images/a_documentcloud_project.png" alt="A DocumentCloud Project" />
</a>
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,3 @@
base.css: base.sass
sass -t expanded base.sass base.css

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,149 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
This is a custom SVG webfont generated by Font Squirrel.
Copyright : MIT License
Designer : Carlos Elas
Foundry : Carlos Elias
Foundry URL : httpcarloseliascl
</metadata>
<defs>
<font id="webfontfL60xSgA" horiz-adv-x="1536" >
<font-face units-per-em="2048" ascent="1638" descent="-410" />
<missing-glyph horiz-adv-x="512" />
<glyph horiz-adv-x="0" />
<glyph horiz-adv-x="0" />
<glyph unicode="&#xd;" horiz-adv-x="512" />
<glyph unicode=" " horiz-adv-x="512" />
<glyph unicode="&#x09;" horiz-adv-x="512" />
<glyph unicode="&#xa0;" horiz-adv-x="512" />
<glyph unicode="!" horiz-adv-x="1556" d="M1302 840q30 -10 52 -31t35 -47t16 -55t-7 -59q-12 -34 -36 -58t-54 -38q-26 -52 -52 -114t-38 -96q-10 -32 -23 -80l-27 -98q32 -38 32 -92q0 -64 -44 -108t-108 -44q-34 0 -65 15t-51 41q-30 2 -62 6t-63 6t-59 4t-46 2q-36 0 -97 -5t-115 -9q-28 -38 -72 -52 q-60 -20 -116 8t-76 86q-14 40 -6 78t32 68l-23 90q-15 60 -31 112q-12 34 -34 86l-44 100q-40 12 -74 50q-40 50 -33 112t55 102q66 52 146 26l33 21q21 13 44 30l47 34q24 17 44 35q34 28 86 76l94 88q4 50 32 84q40 48 102 54t112 -34q28 -22 41 -51t15 -63l91 -89 q49 -47 77 -71q28 -22 77 -57l95 -65q46 18 98 2zM1160 814h-2h2zM1148 804l-4 -4zM1138 792l-4 -4zM1128 780q-4 -4 -4 -6q0 2 4 6zM1118 764q0 -2 -1 -2t-1 -2q0 2 1 2t1 2zM156 552h2h-2zM138 552h2h-2zM214 558h-6h6zM174 552h-2h2zM226 562l4 4zM242 570q0 2 2 4 q-2 -2 -2 -4zM260 584l12 12zM522 64v4v-4zM500 -6v2v-2zM506 10q2 2 4 2q-2 0 -4 -2zM514 26v2v-2zM518 104v-6v6zM520 86q2 -2 2 -6q0 4 -2 6zM920 152q36 52 90 66q22 48 41 97t31 83q18 52 32 112t22 90q-16 20 -25 44t-9 52l-88 84q-48 44 -82 72q-42 34 -93 70t-79 52 q-56 -20 -108 6l-93 -66q-47 -34 -75 -56q-30 -22 -78 -70l-90 -88q0 -50 -32 -92l25 -101q13 -51 23 -81q6 -18 17 -44l23 -54l26 -58l26 -56q48 -20 76 -66q0 2 -2 2q54 -4 114 -7t100 -3q54 0 116 4t92 8zM514 120q-4 12 -8 16q4 -4 8 -16zM1110 744q-2 -6 -2 -16 q0 10 2 16zM1106 722q-2 -4 -2 -6q0 2 2 6zM1132 198v2v-2zM1118 206l-2 2zM1104 212q-2 2 -6 2q4 0 6 -2zM1088 218q-2 2 -6 2q4 0 6 -2zM1072 222h-6h6z" />
<glyph unicode="&#x22;" horiz-adv-x="1526" d="M16 338q0 70 26 132t72 108t108 72t132 26h18q10 60 40 111t74 89t100 59t120 21q70 0 131 -26t107 -72t73 -107t27 -129q0 -12 -2 -21t-2 -17q12 4 24 4h22q60 0 114 -23t93 -63t62 -93t23 -113t-23 -114t-62 -93t-93 -62t-114 -23h-22h-684q-6 0 -13 -2t-13 -2 q-70 0 -132 27t-108 73t-72 107t-26 131z" />
<glyph unicode="#" horiz-adv-x="1520" d="M1286 216v-10q0 -44 -16 -81t-44 -65t-65 -44t-79 -16h-802q-42 0 -80 16t-66 44t-44 65t-16 81v12l114 -68l490 -10l494 10zM1352 592v-206l-66 -66v-80l-116 -68l-484 -10h-8l-488 10l-116 68v72l-76 74v206l76 74v90q0 42 16 79t44 66t66 45t80 16h130l102 104h118v86 q4 34 16 58q4 4 6 10t6 10q6 8 22 20q8 4 28 12q10 2 19 3t19 1h56q16 26 46 26q22 0 38 -16t16 -38t-16 -38t-38 -16q-30 0 -46 26h-56q-12 0 -28 -8q-10 -2 -20 -16t-10 -34v-86h114l102 -104h178q42 0 79 -16t65 -45t44 -66t16 -79v-100zM318 284q22 0 37 15t15 37 t-15 39t-37 17t-39 -17t-17 -39t17 -37t39 -15zM1024 284q22 0 37 15t15 37t-15 39t-37 17t-39 -17t-17 -39t17 -37t39 -15z" />
<glyph unicode="$" horiz-adv-x="1460" d="M642 1288q132 0 249 -51t204 -138t138 -204t51 -249q0 -134 -51 -251t-138 -204t-204 -138t-249 -51q-134 0 -252 51t-205 138t-137 204t-50 251q0 132 50 249t137 204t205 138t252 51zM642 1222q-14 0 -25 -1t-25 -1q-4 -2 -11 -2t-13 -2h-10l-46 -8q-24 -4 -46 -12 q58 4 117 0t113 -32q6 -22 27 -26t44 -5t40 -6t15 -31l4 -4q-2 -2 -1 -6t3 2q8 -8 18 -20t16 -22t4 -16t-18 -4q-22 4 -31 -12t-8 -39t11 -44t26 -29q14 -8 25 -5t15 11t-1 18t-19 18q-2 20 11 17t26 -17t20 -32t-9 -28q-4 -2 -6 -4v2q0 -2 -2 -2q-18 -12 -23 -33t-6 -44 l-2 -47q-1 -24 -7 -44q-12 -28 -1 -37t27 -4t30 20t8 37q-8 20 4 27t28 11t28 8t4 18q24 -12 36 -4t14 25t-4 37t-16 32q6 18 13 19t13 -7t8 -21t-4 -23q2 -20 16 -27t26 -11q2 -2 4 -2q12 -4 16 -14t-6 -32q-16 -48 -57 -57t-81 1q-20 -8 -33 -23t-22 -33t-18 -38t-19 -36 q-12 -22 -21 -46t-12 -49t4 -48t27 -41q18 -18 38 -21t40 4t39 20l37 25q30 4 47 -10t22 -36t-1 -47t-20 -43q-2 -6 -2 -14q64 76 101 171t37 203q0 34 -6 64q-12 -6 -27 5t-23 23t-6 18t24 -12q20 0 26 10q-8 52 -30 104q-6 10 -16 16q2 6 1 12t-1 10v2q-36 72 -89 132 t-120 102t-146 66t-163 24zM398 1146q-34 -28 -73 -64l-78 -75q-39 -39 -72 -81t-53 -84q-18 -52 -19 -105t3 -109q16 -52 49 -98t61 -94q18 -24 15 -49t-5 -49v-2q0 -16 3 -30t15 -28q24 -32 54 -59t46 -67v-2q30 -18 61 -32t65 -26q18 2 35 7t35 9v2q28 10 54 24t48 30 q18 24 13 43t-23 33t-43 19t-43 -1q-44 28 -81 67t-85 61q-22 18 -52 25t-58 15t-47 24t-19 54q4 34 -17 55t-43 37q-8 26 8 44t40 23t45 -6t21 -41q16 0 28 13t24 28l25 30q13 15 31 19q10 8 26 10t29 4t20 7t-3 23q10 8 26 14t29 14t18 19t-9 31q-4 20 -18 35t-30 19 t-31 -3t-23 -31q-22 -6 -30 11t-4 39t20 38t40 10q16 0 24 5t8 14t-6 17t-16 14q0 2 -2 2q-4 2 -16 2q-22 12 -17 33t14 43t11 41t-28 29zM570 1056q-2 0 -3 -1t-3 -1q-10 -2 -14 -9t-2 -13t10 -7t20 11q6 4 8 11t-8 7q-4 2 -8 2zM616 1046q-16 -2 -29 -9t-21 -19t-8 -26 t14 -28q10 -12 6 -30l-8 -35q-4 -17 -1 -32t27 -21q8 20 24 39t29 38t20 41t1 46q-4 22 -21 30t-33 6zM496 978q-6 0 -8 -2q6 -12 4 -24t-5 -24t-3 -24t10 -22q12 6 20 22t8 32t-6 29t-20 13zM830 842q-6 0 -6 -5t12 -15q-10 -22 6 -24t26 14q2 12 -13 21t-25 9zM494 752 q-6 -2 -7 -6t15 -14q2 0 9 1t1 -7q22 14 8 21t-26 5zM372 704q-6 0 -12 -2q2 0 2 -2h2q6 -2 9 1t-1 3zM254 542q-20 -4 12 -24q4 -4 11 -9t15 -1q4 12 -3 20t-17 12q-2 2 -10 2h-8z" />
<glyph unicode="%" horiz-adv-x="1478" d="M180 1142q32 0 53 -22t21 -52v-146h-146v146q0 30 21 52t51 22zM290 886q30 0 52 -22t22 -54v-72q0 -32 -22 -53t-52 -21h-220q-30 0 -51 21t-21 53v72q0 32 21 54t51 22h220zM254 628v-548q0 -32 -21 -53t-53 -21q-30 0 -51 21t-21 53v548h146zM1104 1142q30 0 52 -22 t22 -52v-146h-148v146q0 30 22 52t52 22zM1214 886q30 0 51 -22t21 -54v-72q0 -32 -21 -53t-51 -21h-220q-30 0 -51 21t-21 53v72q0 32 21 54t51 22h220zM1178 628v-548q0 -32 -22 -53t-52 -21t-52 21t-22 53v548h148zM632 6q-30 0 -52 21t-22 53v146h146v-146 q0 -32 -21 -53t-51 -21zM522 262q-32 0 -53 21t-21 53v72q0 30 21 52t53 22h220q30 0 51 -22t21 -52v-72q0 -32 -21 -53t-51 -21h-220zM558 518v550q0 30 22 52t52 22t51 -22t21 -52v-550h-146z" />
<glyph unicode="&#x26;" d="M1200 276q54 -14 71 -34t11 -44t-30 -48t-54 -46t-60 -38l-46 -24q-26 -12 -83 -25t-125 -25t-135 -20t-111 -8t-112 9t-136 21t-124 25t-76 23q-12 4 -38 20t-56 38t-56 47t-34 49t7 45t69 35q80 20 121 68t59 114t20 142t2 154q0 76 38 128t84 84q52 36 122 58 q-2 44 10 80q10 30 34 56t70 26t70 -26t34 -56q10 -36 10 -80q68 -22 122 -58q46 -32 84 -84t38 -128q0 -78 2 -154t20 -142t59 -114t119 -68zM660 88q92 0 175 11t146 26t99 33t36 32q0 12 -27 23t-74 21t-112 16t-139 10q0 -42 -29 -71t-71 -29t-71 29t-29 71 q-76 -4 -140 -10t-111 -16t-74 -21t-27 -23q0 -14 35 -32t95 -33t142 -26t176 -11z" />
<glyph unicode="'" horiz-adv-x="1252" d="M1188 2h-1186l206 776h140q-32 42 -50 91t-18 105q0 66 25 123t67 99t100 67t124 25t123 -25t100 -67t68 -99t25 -123q0 -108 -66 -196h138zM596 1086q-46 0 -80 -33t-34 -79q0 -48 34 -82t80 -34q48 0 81 34t33 82q0 46 -33 79t-81 33z" />
<glyph unicode="(" horiz-adv-x="1198" d="M34 506q-18 22 -27 50t-9 57t9 55t27 44l528 432h58q58 0 92 -22t34 -78v-106q0 -20 6 -36t42 -16h306q36 0 68 -31t32 -69v-354q0 -40 -32 -70t-68 -30h-306q-36 0 -42 -14t-6 -34v-100q0 -60 -34 -95t-92 -35q-10 6 -18 12q-8 4 -18 8t-22 4z" />
<glyph unicode=")" horiz-adv-x="1198" d="M1158 698q18 -22 27 -50t9 -57t-9 -55t-27 -44l-528 -432h-58q-58 0 -92 22t-34 78v106q0 20 -6 36t-42 16h-306q-36 0 -68 31t-32 69v354q0 40 32 70t68 30h306q36 0 42 14t6 34v100q0 60 34 95t92 35l16 -12q8 -4 19 -8t23 -4z" />
<glyph unicode="*" horiz-adv-x="1638" d="M1532 1222q16 0 28 -12t12 -30v-1080q0 -16 -12 -28t-28 -12h-1492q-16 0 -28 12t-12 28v1080q0 18 12 30t28 12h1492zM588 750q10 10 8 20q4 14 -8 22l-94 94q-4 2 -5 4t-3 2l-206 206q-20 16 -40 0l-96 -96q-6 -8 -6 -20t6 -18l192 -194l-192 -190q-6 -8 -6 -20t6 -20 l96 -96q20 -16 40 0l206 208q4 0 8 4zM1302 448q0 16 -12 28t-28 12h-582q-16 0 -28 -12t-12 -28v-90q0 -16 12 -29t28 -13h582q16 0 28 13t12 29v90z" />
<glyph unicode="+" horiz-adv-x="1340" d="M1010 1280q62 0 105 -20t69 -51t38 -72t12 -83v-840q0 -42 -12 -81t-38 -71t-69 -51t-105 -19h-786q-42 0 -81 19t-71 51t-51 71t-19 81v840q0 42 19 83t51 72t71 51t81 20h786zM1010 720h-280v334h-226v-334h-280v-226h280v-280h226v280h280v226z" />
<glyph unicode="," horiz-adv-x="1328" d="M1072 566l146 -146q28 -28 42 -65t14 -75t-14 -74t-42 -62l-72 -72q-28 -28 -64 -42t-73 -14t-74 14t-65 42l-146 146zM622 1016l358 -360l-348 -346l-358 358zM384 628q16 -16 36 -16t36 16q14 12 14 33t-14 35q-16 16 -36 15t-36 -17q-14 -12 -14 -33t14 -33zM662 490 q-12 14 -33 14t-35 -14q-16 -16 -16 -36t16 -34q14 -16 35 -16t33 16q16 14 16 34t-16 36zM768 594q-16 14 -36 14t-36 -14q-14 -16 -14 -37t14 -37q16 -12 36 -12t36 14q14 16 14 36t-14 36zM558 594q-14 14 -34 14t-34 -14q-16 -16 -16 -37t16 -37q12 -12 33 -12t35 12 q16 16 16 37t-16 37zM870 694q-14 16 -34 17t-36 -15q-14 -14 -14 -35t14 -33q16 -16 36 -16t34 16q16 12 16 33t-16 33zM662 696q-12 16 -33 16t-35 -16q-16 -14 -16 -35t16 -33q14 -16 35 -16t33 16q14 12 14 33t-14 35zM768 800q-16 16 -36 16t-36 -16q-14 -14 -14 -35 t12 -33q16 -16 37 -16t37 16q14 14 14 34t-14 34zM558 800q-14 16 -34 16t-34 -16q-16 -14 -16 -34t16 -32q14 -16 34 -16t34 16q16 12 16 32t-16 34zM662 834q16 16 16 36t-16 36q-12 14 -33 14t-35 -14q-16 -16 -16 -36t16 -36q14 -14 35 -14t33 14zM144 1218q26 28 62 42 t74 14t75 -15t67 -43l108 -112l-344 -344l-114 108q-28 30 -42 66t-14 74t14 74t42 64z" />
<glyph unicode="-" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
<glyph unicode="." horiz-adv-x="1072" d="M0 1184q0 44 31 75t77 31h642q46 0 77 -31t31 -75v-1074q0 -44 -31 -75t-77 -31h-642q-46 0 -77 31t-31 75v1074zM194 1074q-24 0 -42 -18t-18 -42v-692q0 -26 18 -43t42 -17h470q24 0 42 17t18 43v692q0 24 -18 42t-42 18h-470zM362 144q0 -28 20 -48t48 -20t47 20 t19 48t-19 47t-47 19t-48 -19t-20 -47zM542 1158q0 28 -28 28h-170q-10 0 -19 -8t-9 -20q0 -10 9 -19t19 -9h170q12 0 20 9t8 19z" />
<glyph unicode="/" horiz-adv-x="1224" d="M904 274q16 -14 16 -34t-16 -36q-14 -14 -34 -14t-34 14l-92 92q-4 -4 -9 -10t-9 -10q-22 -22 -45 -37t-47 -25l-2 -88q-2 -12 -9 -27t-17 -25l-62 -64q-16 -16 -36 -16t-34 16q-16 16 -16 36t16 34l42 42q10 12 12 22l2 44q-44 -4 -89 0t-87 30q-2 0 -4 2q-4 2 -8 6 l-54 54l-4 4l-54 54q-6 2 -6 8l-4 4q-24 44 -27 89t-1 87l-44 -2q-14 0 -22 -12l-42 -42q-16 -14 -35 -14t-35 14q-16 16 -16 35t16 35l62 62q10 10 25 17t27 7l88 4q24 52 64 92l9 9q5 5 11 9l-92 92q-14 14 -14 34t14 36q14 14 34 14t36 -14l48 -50v2l66 -66q14 4 27 7 l29 7l4 120q0 14 9 30t19 26l64 64q14 14 34 14t34 -14q16 -16 16 -36t-16 -34l-44 -46q-12 -12 -14 -22l-2 -76q34 10 69 28t69 52q38 40 91 46t99 -22l-64 -64q-12 -12 -13 -28t11 -26q0 -2 2 -4q12 -10 28 -10t26 10l66 68q28 -46 22 -99t-46 -93q-34 -34 -52 -68 t-30 -70l78 4q8 0 20 12l48 46q14 14 34 14t34 -14q16 -16 16 -36t-16 -34l-62 -64q-10 -10 -27 -18t-29 -8l-120 -6q-4 -14 -7 -27l-7 -29l64 -64z" />
<glyph unicode="0" horiz-adv-x="1114" d="M128 1250q30 8 60 3t56 -21t45 -42t23 -56q4 -48 5 -97t1 -97q124 0 246 2t244 -4q46 -8 83 -41t43 -81q8 -30 2 -61t-23 -57t-43 -45t-56 -23q-126 -10 -250 -7t-250 1q2 -46 2 -91t6 -91q8 -44 41 -79t79 -39q94 -8 187 -5t187 -5q40 -6 69 -34t43 -65t9 -77t-33 -70 q-14 -26 -37 -40t-49 -20t-54 -7t-54 -1q-96 2 -193 -1t-189 25q-70 20 -130 64t-103 102t-68 127t-25 143q-4 150 -3 300t1 300q-4 32 3 64t24 58t43 45t58 23z" />
<glyph unicode="1" horiz-adv-x="1198" d="M874 774q-20 -10 -28 -26t-9 -34t4 -36t11 -36q6 -16 19 -33t21 -33q12 -28 14 -59t-2 -63q-4 -28 -18 -52t-34 -45t-42 -40l-42 -35q-24 -18 -48 -26t-44 -16t-34 -23t-18 -49t-6 -48t-3 -18t-1 -6t-2 -14q0 -2 16 -4t37 -4l41 -4q20 -2 28 -6t21 -12t24 -17t19 -18 t8 -11q0 -4 -45 -5t-100 -1t-102 1t-53 5q-6 2 2 13t19 23t21 24t10 16t-1 23l-2 40q-1 21 -5 40t-10 23q-14 10 -42 16t-58 16q-44 14 -98 11t-100 -3q-70 0 -141 -12t-141 -12l-40 60q62 8 114 16l78 11q36 5 36 9t-24 14t-52 20q-34 12 -74 28l64 16q24 6 42 10t14 2 q58 22 123 30t123 32q16 6 52 32t76 60t75 68t47 56l22 43l19 37q9 18 17 37t14 45l15 65q7 31 39 49q28 18 64 18t68 -8q24 -6 53 -31t56 -56t46 -60t23 -43q2 -4 -23 -5t-55 -1t-58 1t-32 -1q-4 0 -8 -4z" />
<glyph unicode="2" horiz-adv-x="1460" d="M1282 1282v-1280h-1282v1280h1282zM1106 1190h-174q-70 -2 -115 -22t-71 -52t-36 -76t-10 -94v-110h-116v-196h116v-548h232v548h152l22 196h-174v110q0 26 12 42t42 16h120v186z" />
<glyph unicode="3" d="M1024 976q78 0 146 -29t119 -80t81 -118t30 -143q0 -78 -30 -147t-81 -120t-119 -81t-146 -30q-102 0 -188 52t-132 138q-52 -86 -137 -138t-189 -52q-78 0 -146 30t-119 81t-80 120t-29 147q0 76 29 143t80 118t119 80t146 29q104 0 189 -51t137 -135q46 84 132 135 t188 51zM1218 410q82 76 82 196q0 110 -82 192q-36 36 -86 57t-108 21q-56 0 -105 -21t-85 -57q-40 -40 -61 -89t-21 -103q0 -58 21 -109t61 -87q74 -80 190 -80q58 0 108 20t86 60z" />
<glyph unicode="4" horiz-adv-x="1466" d="M0 1284h1286v-1286h-1286v1286zM1170 114v528h-526v526h-526v-528h526v-526h526z" />
<glyph unicode="5" horiz-adv-x="1240" d="M314 1072q0 44 28 66q24 26 72 26q42 0 66 -26q26 -24 26 -68v-186q0 -22 -5 -40t-21 -30q-26 -26 -70 -26q-42 0 -68 26q-28 22 -28 72v186zM382 880q0 -16 8 -24q4 -4 20 -4t20 4q10 10 10 24v194q0 14 -10 20q-8 8 -20 8q-6 0 -10 -2t-10 -6q-8 -6 -8 -20v-194z M156 988l-90 294h76l48 -196h6l46 196h76l-86 -282v-202h-76v190zM610 794q-26 0 -34 14q-6 10 -10 20t-4 28v298h68v-276q0 -8 4 -16q2 -6 16 -6q6 0 18 6q8 12 18 18v274h68v-356h-68v36l-18 -18q-8 -8 -18 -14q-22 -8 -40 -8zM1082 524q4 -50 6 -101t2 -101 q0 -54 -2 -104t-6 -102q0 -66 -47 -111t-111 -45q-94 -4 -188 -7t-192 -3q-100 0 -194 3t-190 7q-64 0 -111 45t-47 111q-4 52 -6 102t-2 104q0 50 2 101t6 101q0 32 13 61t34 51t50 35t61 13q188 6 384 6q194 0 380 -6q32 0 61 -13t50 -35t34 -51t13 -61zM232 500h76v68 h-226v-68h76v-416h74v416zM500 442h-70v-270q-12 -16 -20 -22q-10 -6 -16 -6q-12 0 -16 4q-4 8 -4 20v274h-66v-298q0 -32 12 -50q12 -14 34 -14q20 0 40 10q10 4 19 14t17 20v-40h70v358zM754 356q0 44 -18 68q-16 22 -46 22q-16 0 -30 -8q-18 -6 -30 -26v156h-68v-484h68 v30q6 -10 14 -16t16 -10q14 -8 32 -8q30 0 46 20q8 12 12 25t4 33v198zM998 346q0 54 -22 78q-28 28 -72 28t-72 -28q-12 -16 -20 -35t-8 -43v-160q0 -50 26 -82q22 -28 68 -28q52 0 76 24q24 30 24 86v28h-68v-26q0 -16 -1 -27t-5 -15q-8 -8 -24 -8q-10 0 -22 8 q-6 12 -6 42v66h126v92zM900 388q18 0 24 -6q6 -16 6 -36v-36h-58v36q0 20 6 36q8 6 22 6zM660 390q12 0 20 -8q4 -8 4 -28v-184q0 -24 -4 -26q-8 -8 -20 -8q-4 0 -16 6q-8 0 -14 10v224q4 4 14 12q4 0 8 1t8 1z" />
<glyph unicode="6" horiz-adv-x="1442" d="M1214 802q-12 -178 -248 -482q-250 -320 -420 -320q-102 0 -178 196l-48 178l-50 178q-54 196 -116 196q-16 0 -96 -56l-58 74q46 38 89 78l89 80q58 52 104 78t74 30q140 12 176 -194q18 -112 30 -180t16 -96q42 -184 92 -184q36 0 114 118q74 122 82 182 q10 104 -82 104q-22 0 -45 -5t-47 -15q92 290 344 284q186 -8 178 -244z" />
<glyph unicode="7" horiz-adv-x="1466" d="M1174 1284q46 0 78 -32t32 -78v-1066q0 -46 -32 -78t-78 -32h-1066q-46 0 -78 32t-32 78v1066q0 46 32 78t78 32h1066zM952 352q-74 -50 -152 -50q-46 0 -76 22q-24 16 -36 38q-8 24 -8 112v260h240v160h-240v256h-144q-12 -78 -36 -130q-14 -26 -31 -48t-39 -38 q-42 -38 -100 -56v-144h110v-354q0 -68 16 -106t54 -68q18 -16 40 -29t50 -23q54 -16 122 -16q60 0 112 12q28 4 57 16t61 28v158z" />
<glyph unicode="8" horiz-adv-x="1454" d="M682 702q0 2 2 2v-2h-2zM1180 1282q38 0 66 -26t28 -66v-1100q0 -38 -28 -65t-66 -27h-1088q-40 0 -67 27t-27 65v1100q0 40 27 66t67 26h1088zM382 788h-190v-580h190v580zM288 866q50 0 79 29t29 73q0 40 -29 69t-79 29q-48 0 -78 -29t-30 -69q0 -44 29 -73t77 -29h2z M1078 540q0 134 -63 198t-159 64q-38 0 -66 -10t-48 -24t-34 -32l-24 -32v84h-194q0 -10 1 -41t1 -74v-94v-103q0 -122 -2 -268h194v324v24q0 12 4 22q10 26 35 49t65 23q54 0 76 -37t22 -95v-310h192v332z" />
<glyph unicode="9" horiz-adv-x="1310" d="M1162 514q24 -40 38 -85t14 -95q0 -70 -27 -132t-73 -108t-108 -73t-134 -27q-50 0 -94 14t-84 38q-40 -8 -88 -8q-118 0 -220 44t-179 121t-121 179t-44 220q0 48 8 86q-24 40 -38 85t-14 95q0 72 27 134t73 108t108 73t132 27q50 0 95 -14t85 -38q38 8 88 8 q116 0 219 -44t180 -121t121 -180t44 -219q0 -50 -8 -88zM874 344q16 28 26 56t10 62q0 52 -22 90q-16 36 -56 62q-40 24 -90 42q-26 8 -55 15l-61 15l-44 10q-18 4 -30 8q-20 6 -44 16q-22 12 -32 28q-12 12 -12 34q0 34 36 56q30 22 94 22t92 -20q14 -10 26 -26t22 -34 q14 -30 30 -42q18 -14 42 -14q32 0 50 22q20 20 20 48q0 14 -4 28t-10 30q-16 26 -52 56q-32 26 -86 44q-52 16 -120 16q-90 0 -152 -24q-68 -28 -102 -70q-32 -44 -32 -106q0 -64 30 -102q38 -44 90 -66q54 -24 138 -42q28 -8 53 -13t43 -11q40 -14 58 -34q24 -22 24 -54 q0 -42 -44 -70q-42 -32 -108 -32q-48 0 -80 16q-28 16 -46 36q-8 10 -15 25l-15 31q-14 30 -30 44t-42 14q-32 0 -52 -18q-20 -24 -20 -46q0 -24 8 -47t24 -47q32 -46 86 -76q76 -40 186 -40q92 0 164 30q34 14 59 34t45 44z" />
<glyph unicode=":" horiz-adv-x="1382" d="M1002 390l128 -16l-14 -112l-128 16zM1028 462l-2 504l218 -26l-122 -490zM466 956q108 0 194 -28t148 -77t95 -114t33 -139t-34 -139t-96 -113t-149 -76t-191 -28t-190 28t-148 76t-96 113t-34 139t33 139t94 114t148 77t193 28zM754 710q-8 0 -23 1t-32 1h-33h-28h-33 h-37h-32h-20l4 -28q6 0 34 -3t40 -7q-2 -8 -18 -23l-35 -32q-19 -17 -37 -31t-22 -20q-16 22 -54 70t-68 94q6 2 18 3t26 1t26 1t16 3l4 20q-4 0 -22 1t-40 1h-45h-37q-46 -4 -86 -1t-46 -1v-24q6 0 18 -2t25 -3t23 -3t14 -4q12 -10 38 -38t52 -60t46 -59t22 -33 q2 -10 2 -21v-17v-16v-37q0 -9 -4 -13q-10 -10 -39 -10h-39l-6 -26h26q18 0 37 1t36 1h27h144v26h-17h-27q-14 0 -27 1t-15 1q-2 10 -4 26t-2 28v20q0 20 4 36q2 10 26 32t52 45t53 41t31 20l34 7q24 5 32 5z" />
<glyph unicode=";" horiz-adv-x="1204" d="M594 628q22 -12 36 -35t14 -51q0 -26 -14 -48t-36 -36v-64h-98v64q-22 14 -36 36t-14 48q0 28 14 51t36 35v212h98v-212zM978 870q52 -68 82 -151t30 -177q0 -112 -43 -211t-117 -173t-174 -117t-212 -43t-211 43t-173 117t-117 173t-43 211q0 108 38 202t105 167 t158 119t195 56v102h-50v98h198v-98h-50v-102q78 -8 149 -36t129 -72l110 108l106 -106zM544 98q92 0 173 35t142 95t96 141t35 173t-35 173t-96 142t-142 96t-173 35t-173 -35t-141 -96t-95 -142t-35 -173t35 -173t95 -141t141 -95t173 -35z" />
<glyph unicode="&#x3c;" horiz-adv-x="1198" d="M614 1290q78 0 145 -29t118 -80t80 -119t29 -146q0 -50 -18 -138t-47 -190t-68 -207t-81 -190t-85 -139t-79 -54q-44 0 -89 54t-85 139t-75 190t-61 207t-41 189t-15 139q0 78 29 146t80 119t118 80t145 29zM614 828q58 0 100 42t42 100t-42 100t-100 42t-100 -42 t-42 -100t42 -100t100 -42z" />
<glyph unicode="=" horiz-adv-x="1400" d="M1270 930q34 0 59 -25t25 -61v-650q0 -34 -25 -58t-59 -24h-1186q-34 0 -58 24t-24 58v650q0 36 24 61t58 25h252l54 102q0 12 9 23t25 11h510q12 0 22 -11t10 -23l52 -102h252zM676 192q70 0 132 27t108 73t73 107t27 131t-27 132t-73 108t-108 73t-132 27t-131 -27 t-107 -73t-73 -108t-27 -132t27 -131t73 -107t107 -73t131 -27zM886 530q0 -42 -17 -80t-45 -67t-66 -45t-82 -16q-42 0 -80 16t-67 45t-45 67t-16 80q0 44 16 82t45 66t67 45t80 17q44 0 82 -17t66 -45t45 -66t17 -82z" />
<glyph unicode="&#x3e;" horiz-adv-x="1204" d="M146 16q-24 -22 -57 -22t-59 22q-22 26 -22 59t22 57l1028 1024q24 22 58 22t58 -22q22 -24 22 -58t-22 -58zM530 12q-22 -22 -56 -22t-58 22q-24 24 -24 58t24 58l650 652q26 24 59 24t57 -24q22 -22 22 -56t-22 -58zM940 30q-24 -22 -58 -22t-56 22q-22 26 -22 59 t22 57l228 228q24 24 58 24t58 -24q22 -22 22 -56t-22 -56z" />
<glyph unicode="?" horiz-adv-x="1400" d="M646 1284q132 0 249 -50t204 -137t137 -205t50 -252q0 -132 -50 -249t-137 -204t-204 -137t-249 -50t-250 50t-205 137t-138 204t-51 249q0 134 51 252t138 205t205 137t250 50zM650 258q6 42 -14 76q-24 32 -64 42q-44 10 -76 -14q-38 -22 -44 -62q-10 -44 14 -76 q24 -36 64 -44q44 -10 76 14q36 24 44 64zM978 776q18 80 -12 152q-26 70 -90 118q-56 46 -144 64q-42 10 -94 6q-50 -2 -102 -22q-26 -10 -46 -23t-38 -29q-36 -36 -44 -82q-8 -40 6 -70q18 -30 52 -38q12 -4 25 -1t23 7q20 12 54 36q16 12 31 22t31 16q26 10 64 4 q44 -8 68 -44q28 -34 18 -74q-12 -52 -42 -66q-26 -16 -82 -22q-30 -2 -52 -3t-34 -5q-24 -6 -30 -36l-26 -128q-8 -38 12 -68q18 -28 60 -38q38 -8 72 10q30 20 38 58l10 46q106 2 180 56q72 56 92 154z" />
<glyph unicode="@" d="M1368 948q28 0 47 -18t19 -46v-818q0 -28 -19 -47t-47 -19h-1304q-28 0 -47 19t-19 47v818q0 28 19 46t47 18h1304zM1366 806q16 14 20 34t-10 36q-14 20 -36 22t-38 -8l-584 -446l-588 438q-20 12 -40 8t-34 -20q-14 -18 -10 -39t20 -33l620 -458q12 -12 32 -12 q18 0 30 12z" />
<glyph unicode="A" horiz-adv-x="882" d="M178 1260q12 10 27 10q9 0 19 -4q22 -10 28 -32l88 -264q10 -16 0 -36q-8 -20 -24 -26l-44 -20q-22 -10 -30 -30t2 -42l32 -92l28 -79q14 -41 28 -69q12 -28 31 -63l35 -67l42 -76q8 -22 29 -30t43 2l44 20q16 10 38 0q20 -10 26 -24l138 -242q7 -12 7 -24q0 -10 -5 -20 q-12 -22 -32 -30h-6q-48 -12 -112 -20q-17 -2 -33 -2q-45 0 -85 16q-36 14 -75 49t-77 86t-74 114t-66 131l-36 76q-4 4 -4 5t-2 5l-9 21q-9 21 -19 47t-20 47t-10 23q-30 68 -52 137t-34 131q-13 68 -13 122q0 48 13 82q10 26 29 51t41 46t46 38l44 31z" />
<glyph unicode="B" horiz-adv-x="1466" d="M1234 742q18 0 34 -16t16 -34v-100q0 -22 -16 -36t-34 -14h-60q-20 0 -39 -13t-27 -33l-34 -84q-8 -20 -5 -42t19 -38l40 -40q16 -16 16 -35t-16 -35l-70 -70q-16 -16 -35 -16t-35 16l-40 40q-16 16 -38 19t-42 -5l-80 -34q-22 -8 -36 -27t-14 -39v-60q0 -18 -14 -34 t-36 -16h-100q-18 0 -31 16t-13 34v60q0 20 -16 39t-34 27l-80 34q-20 8 -42 5t-38 -19l-40 -40q-16 -16 -38 -16t-32 16l-70 70q-16 16 -16 35t16 35l40 40q16 16 18 38t-8 42l-30 84q-8 20 -27 33t-39 13h-60q-18 0 -34 14t-16 36v100q0 18 16 34t34 16l60 -6q20 0 39 16 t27 34l30 80q10 20 8 42t-18 38l-40 46q-16 10 -16 32t16 38l70 64q10 16 32 16t38 -16l40 -40q16 -16 38 -18t42 8l80 36q18 4 34 22t16 38v60q0 22 13 36t31 14h100q50 0 50 -50v-60q0 -20 14 -38t36 -22l80 -36q20 -10 42 -8t38 18l40 40q16 16 35 16t35 -16l70 -64 q16 -16 16 -38t-16 -32l-40 -46q-16 -16 -19 -38t5 -42l34 -80q8 -18 27 -31t39 -13h60zM888 642q0 50 -19 95t-53 78t-80 52t-98 19q-50 0 -95 -19t-78 -52t-52 -78t-19 -95q0 -52 19 -98t52 -80t78 -53t95 -19q52 0 98 19t80 53t53 80t19 98z" />
<glyph unicode="C" horiz-adv-x="1574" d="M1224 1280q38 0 71 -14t58 -39t39 -58t14 -71v-612q0 -74 -53 -127t-129 -53h-672v-318l-356 318h-14q-38 0 -72 14t-59 39t-39 58t-14 69v612q0 38 14 71t39 58t59 39t72 14h1042z" />
<glyph unicode="D" horiz-adv-x="1466" d="M820 470v414q0 34 -24 58t-58 24h-12q-34 0 -59 -24t-25 -58v-414q0 -34 25 -58t59 -24h12q34 0 58 24t24 58zM820 214q0 38 -27 63t-63 25q-38 0 -64 -25t-26 -63q0 -40 26 -66t64 -26q36 0 63 26t27 66zM1358 -2h-1258q-58 0 -79 38q-9 16 -9 34q0 26 18 56l628 1086 q28 54 71 54t75 -54l626 -1086q19 -31 19 -58q0 -17 -8 -32q-21 -38 -83 -38z" />
<glyph unicode="E" horiz-adv-x="1502" d="M1152 842l-322 326l102 106q22 18 53 18t53 -18l220 -220q22 -22 22 -53t-22 -53zM-4 48l58 310q0 30 22 56l678 676l324 -326l-676 -676q-6 -6 -22 -14t-22 -12l-322 -58q-5 -1 -9 -1q-14 0 -23 10t-9 25q0 4 1 10z" />
<glyph unicode="F" horiz-adv-x="1514" d="M1114 1416l4 -96l-88 34l-88 -34l6 96l-62 74l94 24l50 78l50 -78l92 -24zM770 1512l8 -142l-130 50l-132 -50l10 142l-92 108l138 36l76 118l76 -118l134 -36zM1240 1062l-178 178q-18 18 -42 18t-44 -18l-262 -260h4q17 0 56 -26q44 -30 98 -82q52 -52 82 -97 q24 -36 24 -52q0 -4 -2 -7l264 260q18 20 18 44t-18 42zM812 814q-56 54 -101 85q-34 23 -48 23q-5 0 -7 -2l-4 -2l-620 -620v-4l-12 -10q-16 -18 -16 -42t16 -42l180 -178q16 -18 41 -18t41 18l60 56h-4l580 578q2 2 2 7q0 14 -22 50q-30 47 -86 101z" />
<glyph unicode="G" d="M642 1288q134 0 252 -51t205 -140t138 -207t51 -252q0 -132 -51 -250t-138 -206t-205 -139t-252 -51t-252 51t-207 139t-140 206t-51 250q0 134 51 252t140 207t207 140t252 51zM642 202q90 0 169 35t138 94t94 138t35 169t-35 170t-94 140t-138 94t-169 34t-170 -34 t-140 -94t-94 -140t-34 -170t34 -169t94 -138t140 -94t170 -35zM878 638q0 -48 -19 -91t-51 -75t-75 -51t-91 -19q-50 0 -93 19t-76 51t-52 75t-19 91q0 50 19 93t52 76t76 52t93 19q48 0 91 -19t75 -52t51 -76t19 -93z" />
<glyph unicode="H" horiz-adv-x="1300" d="M1198 1282q36 0 63 -27t27 -63l-46 -466q-4 -26 -18 -40q-22 -22 -52 -22t-50 22l-52 50q-26 20 -52 0l-216 -220q-22 -22 -51 -22t-51 22l-178 178q-22 22 -22 52t22 52l218 216q10 10 10 25t-10 25l-50 52q-22 22 -22 50t22 50q14 18 40 22zM892 416q10 6 14 8l102 104 v-476q0 -22 -16 -38t-40 -16h-900q-22 0 -38 16t-16 38v900q0 24 16 40t38 16h488l-108 -108l-8 -8h-272q-16 0 -27 -11t-11 -27v-702q0 -18 11 -28t27 -10h704q16 0 26 10t10 28v264z" />
<glyph unicode="I" horiz-adv-x="1204" d="M100 96q-50 50 -75 112t-25 127t25 127t75 112q70 70 161 93q41 10 82 10q50 0 99 -15l418 418l202 -22l22 -200l-158 -40l-22 -140l-150 -32l-18 -136l-72 -72q16 -50 16 -101q0 -40 -10 -80q-22 -91 -92 -161q-50 -50 -112 -75t-127 -25t-127 25t-112 75zM332 330 q-34 34 -83 34t-83 -34t-34 -82t34 -82t83 -34t83 34t34 82t-34 82z" />
<glyph unicode="J" horiz-adv-x="1460" d="M638 1288q134 0 251 -51t205 -139t139 -205t51 -251t-51 -251t-139 -205t-205 -139t-251 -51t-251 51t-205 139t-139 205t-51 251t51 251t139 205t205 139t251 51zM638 1124q-30 0 -51 -20t-21 -52v-306q-54 -34 -54 -104q0 -54 36 -93t90 -39t92 39t38 93q0 34 -15 61 t-39 43v306q0 32 -23 52t-53 20zM810 574q-28 -76 -104 -104l104 -104q22 -24 52 -24t52 24q22 22 22 53t-22 51z" />
<glyph unicode="K" horiz-adv-x="1370" d="M442 156l-282 282v402l282 284h402l284 -284v-402l-284 -282h-402zM878 74l332 332v468l-332 332h-468l-332 -332v-468l332 -332h468zM348 26l-318 318q-12 14 -20 33t-8 35v454q0 16 8 36t20 32l318 320q14 10 34 18t34 8h454q16 0 36 -8t32 -18l316 -320q12 -12 22 -32 t10 -36v-454q0 -16 -10 -35t-22 -33l-316 -318q-12 -12 -32 -20t-36 -8h-454q-14 0 -34 8t-34 20z" />
<glyph unicode="L" horiz-adv-x="1442" d="M642 968q52 80 121 120t141 46q14 1 27 1q58 0 114 -21q69 -26 123 -77t87 -123t33 -160q0 -90 -36 -169t-91 -146t-123 -123l-130 -104l-106 -81l-71 -56l-50 -41q-21 -18 -39 -38q-20 20 -42 38l-52 41l-72 55l-104 82l-129 105q-67 57 -123 123t-92 143t-36 167 q0 88 33 161t87 125t124 78q57 21 115 21q13 0 27 -1q72 -6 141 -46t123 -120z" />
<glyph unicode="M" horiz-adv-x="1484" d="M1060 444l26 -444l-416 162l-418 -162l26 444l-282 348l432 114l242 378l244 -378l432 -114z" />
<glyph unicode="N" d="M744 726q48 0 83 -36t35 -88q0 -50 -35 -86t-83 -36q-52 0 -89 36t-37 86q0 52 37 88t89 36zM744 1094q76 0 148 -21t138 -56t124 -79t108 -90q116 -110 214 -246q-98 -138 -214 -246q-50 -46 -108 -90t-124 -78t-138 -55t-148 -21t-148 21t-138 55t-125 78t-109 90 q-118 108 -216 246q98 136 216 246q50 46 109 90t125 79t138 56t148 21zM744 298q62 0 118 24t98 65t66 96t24 119t-24 120t-66 97t-98 65t-118 24q-66 0 -122 -24t-97 -65t-65 -97t-24 -120t24 -119t65 -96t97 -65t122 -24z" />
<glyph unicode="O" horiz-adv-x="858" d="M276 -2v152q-80 2 -151 22t-117 46l48 184q50 -28 117 -49t145 -21q70 0 111 26t41 72t-39 77t-129 61q-128 44 -207 109t-79 179q0 108 70 184t198 98v152h156v-140q78 -4 134 -18t96 -34l-48 -180q-30 16 -86 36t-142 20q-76 0 -105 -28t-29 -62q0 -38 44 -68t146 -68 q142 -48 203 -118t61 -178q0 -52 -18 -100t-54 -86t-88 -65t-120 -39v-164h-158z" />
<glyph unicode="P" horiz-adv-x="1460" d="M832 670q-16 -10 -36 -10q-26 0 -43 17t-17 39v104q-60 -2 -113 -25t-93 -63t-63 -93t-23 -113q0 -62 23 -116t63 -94t94 -63t116 -23q54 0 102 18t87 50t65 76t36 96q4 20 25 38t53 18h140q22 0 37 -17t15 -39v-14q-14 -104 -63 -194t-124 -157t-170 -105t-203 -38 q-118 0 -220 44t-179 121t-121 179t-44 220q0 116 44 218t120 178t178 120t218 44v104q0 26 17 41t43 15q10 0 19 -1t17 -7l306 -230q27 -28 27 -55.5t-27 -54.5z" />
<glyph unicode="Q" horiz-adv-x="1258" d="M204 402q16 -20 12 -46t-28 -40q-20 -16 -46 -11t-42 23q-46 70 -72 148t-26 162v27q0 13 4 27v10q0 6 3 14t3 18v4q4 8 4 13t4 9q0 8 2 17t2 15q4 4 5 8t5 10q0 16 8 32q6 6 6 8v2q12 18 18 40l2 2v2q20 34 43 65t51 59q8 8 16 15t20 15q4 4 6 10q8 4 15 9l15 11h2 q136 94 306 94v108l252 -170l-252 -176v112q-76 0 -140 -26t-116 -66q-2 0 -3 -2t-5 -2l-13 -13q-5 -5 -13 -11v-4q-22 -22 -38 -42t-32 -48h-2q-6 -8 -8 -17t-6 -15v-4q-8 -20 -14 -32v-10q-4 -6 -7 -13t-3 -15t-4 -12q0 -20 -4 -26v-16v-32q-1 -11 -1 -22q0 -52 16 -103 q19 -61 55 -115zM542 -4l-258 170l258 174v-110q70 0 137 25t121 67v4q40 30 69 68t45 86q4 8 7 17l5 15q0 2 4 10q0 4 3 11t3 13t2 8t2 6q0 8 2 14t2 14v8v38q1 12 1 23q0 115 -73 217q-14 22 -9 47t29 37q16 16 36 16q5 0 10 -1q26 -5 42 -25q96 -142 96 -310v-10v-19 q0 -11 -4 -21v-18q0 -8 -2 -13t-2 -13v-10q-6 -6 -6 -20q-4 -6 -4 -14t-4 -16q-4 -4 -4 -12q-4 -10 -7 -21t-7 -21l-4 -6q-50 -118 -150 -198l-6 -4q-6 -8 -13 -12t-19 -12l-2 -4q-64 -46 -141 -69t-159 -23v-106z" />
<glyph unicode="R" horiz-adv-x="1328" d="M1248 914q24 -24 24 -57t-24 -57l-322 -268q-18 -12 -40 -12q-28 0 -46 18t-18 46v64q0 14 -10 23t-22 9h-194q-28 0 -47 18t-19 46v226q0 28 19 46t47 18h194q12 0 22 10t10 24v64q0 26 18 45t46 19q24 0 40 -14zM350 666q12 14 36 14q28 0 46 -19t18 -45v-66 q0 -12 10 -22t24 -10h194q26 0 45 -18t19 -46v-226q0 -28 -19 -46t-45 -18h-194q-14 0 -24 -9t-10 -23v-64q0 -28 -18 -46t-46 -18q-24 0 -36 12l-326 268q-24 24 -24 57t24 57z" />
<glyph unicode="S" d="M1184 1288q42 0 72 -30t30 -74v-1082q0 -42 -30 -72t-72 -30h-1084q-42 0 -72 30t-30 72v1082q0 44 30 74t72 30h1084zM244 836q0 -28 17 -45t43 -17h674q28 0 45 17t17 45v388v2h-796v-2v-388zM1140 594q0 20 -15 34t-37 14h-894q-22 0 -36 -14t-14 -34v-446 q0 -20 14 -34t36 -14h894q22 0 37 14t15 34v446zM496 868q0 -10 -8 -19t-20 -9h-106q-10 0 -19 9t-9 19v280q0 12 9 20t19 8h106q28 0 28 -28v-280zM1082 530q0 -24 -26 -24h-830q-26 0 -26 24q0 26 26 26h830q26 0 26 -26zM1082 376q0 -26 -26 -26h-830q-26 0 -26 26t26 26 h830q26 0 26 -26zM1082 212q0 -10 -8 -17t-18 -7h-830q-10 0 -18 7t-8 17t8 19t18 9h830q10 0 18 -9t8 -19z" />
<glyph unicode="T" horiz-adv-x="1358" d="M1026 840q40 38 40 93t-40 95q-38 38 -92 38t-92 -38q-40 -40 -40 -95t40 -93q38 -40 92 -40t92 40zM36 588l640 636q16 18 37 24t43 4q6 2 20 2l400 -42h6q34 0 56 -20q19 -18 19 -45q0 -7 -1 -15l14 -368q-2 -8 -7 -21t-11 -26t-13 -25t-13 -18l-640 -638 q-28 -28 -65 -28t-65 28l-418 418q-28 28 -28 67t26 67z" />
<glyph unicode="U" horiz-adv-x="1400" d="M874 492q-12 -24 -5 -53t19 -59l24 -60q12 -30 11 -54t-20 -40t-67 -18q-9 -1 -18 -1q-51 0 -105 20q-63 23 -122 57t-107 72t-74 62h-2q-4 2 -8 6t-9 8t-9 10l-4 2q-22 22 -56 61t-65 88t-55 106t-24 115v11q0 39 13 57q15 20 39 22t54 -8t61 -19t61 -13t52 10 q32 20 70 57t69 77t51 79t20 65q0 24 8 47t27 37t50 15h3q30 0 72 -19q76 -32 129 -67t83 -57q2 -2 5 -4t5 -4q2 -4 5 -6t5 -4q26 -28 64 -78t78 -120q24 -49 24 -84q0 -26 -11 -44q-13 -20 -35 -30t-48 -10q-24 -2 -61 -25t-75 -58t-71 -75t-51 -74zM-6 0l252 466 q14 -18 28 -38l27 -36q15 -20 31 -36t37 -33t41 -31q22 -16 48 -30z" />
<glyph unicode="V" horiz-adv-x="1418" d="M638 0q-132 0 -249 51t-204 138t-138 204t-51 249t51 249t138 204t204 138t249 51t249 -51t204 -138t138 -204t51 -249t-51 -249t-138 -204t-204 -138t-249 -51zM196 642q0 -92 35 -172t95 -140t140 -95t172 -35q130 0 238 68l-612 612q-68 -108 -68 -238zM1080 642 q0 92 -34 172t-94 140t-141 95t-173 35q-130 0 -238 -68l612 -612q68 108 68 238z" />
<glyph unicode="W" horiz-adv-x="1132" d="M846 800q24 0 40 -18t16 -42v-684q0 -24 -16 -41t-40 -17h-788q-24 0 -40 17t-16 41v684q0 24 16 42t40 18h42v124q0 72 28 136t76 112t112 76t136 28q74 0 139 -28t113 -76t76 -112t28 -136v-124h38zM678 800v124q0 46 -18 87t-49 72t-72 49t-87 18t-87 -18t-72 -49 t-49 -72t-18 -87v-124h452z" />
<glyph unicode="X" horiz-adv-x="1484" d="M-8 58v688q0 26 17 42t43 16h624v124q0 48 -18 89t-49 72t-72 49t-89 18t-90 -18t-74 -50l-114 -12l54 -102q-4 -10 -4 -22v-24v-34h-130v34q0 74 28 139t77 114t114 77t139 28t139 -28t114 -77t77 -114t28 -139v-124h38q22 0 39 -16t17 -42v-688q0 -26 -17 -43t-39 -17 h-792q-26 0 -43 17t-17 43z" />
<glyph unicode="Y" d="M1138 1282l310 -770v-514h-1446v564l294 720h842zM984 506l294 6l-224 594h-652l-218 -572h282v-284h518v256z" />
<glyph unicode="Z" d="M1452 516v-516h-1452v566l298 722h844zM1284 516l-228 596h-654l-216 -574h282v-286h518v258zM772 488q-18 -16 -45 -16t-43 16l-184 224v24q0 54 42 54h46q8 0 15 3t7 19v130q0 16 13 29t29 13h152q16 0 29 -13t13 -29v-130q0 -16 6 -19t14 -3h42q56 0 56 -54 q-4 -2 -6 -6q-4 -8 -4 -18z" />
<glyph unicode="[" horiz-adv-x="1364" d="M1000 626q22 -22 22 -52t-22 -52l-486 -486q-22 -22 -51 -22t-51 22l-370 372q-22 22 -22 52t22 52l486 486q22 22 52 22t52 -22l6 -8l242 244q26 26 62 26t62 -26l238 -238q26 -26 26 -62t-26 -62l-244 -242zM992 914q-12 -14 -12 -32t12 -30l20 -22q30 -30 64 0l20 22 q14 12 14 30t-14 32l-20 22q-34 26 -64 0zM840 1068q-30 -34 0 -66l20 -18q12 -14 31 -14t31 14l22 18q26 32 0 66l-22 18q-12 14 -31 14t-31 -14zM912 542q12 12 12 29t-12 29l-308 310q-12 12 -29 12t-29 -12l-424 -424q-12 -12 -12 -29t12 -29l310 -310q12 -12 29 -12 t29 12z" />
<glyph unicode="\" horiz-adv-x="1448" d="M638 1286q132 0 248 -51t203 -138t138 -203t51 -248q0 -134 -51 -251t-138 -204t-203 -138t-248 -51q-134 0 -250 51t-203 138t-137 204t-50 251q0 132 50 248t137 203t203 138t250 51zM638 152q102 0 191 39t157 106t107 157t39 192t-39 191t-107 155t-157 105t-191 39 t-192 -39t-157 -105t-106 -155t-39 -191t39 -192t106 -157t157 -106t192 -39zM536 748l380 176l-174 -384l-382 -176zM568 714l-140 -278l282 138z" />
<glyph unicode="]" horiz-adv-x="1102" d="M774 758q8 -24 32 -41t54 -31l59 -27q29 -13 45 -31t14 -42t-36 -60q-40 -44 -103 -71t-129 -42t-126 -20t-96 -7l-2 -2q-4 -2 -10 -2h-24l-4 2q-32 0 -83 5t-108 18t-113 39t-98 68q-34 34 -37 59t13 42t45 31l58 29q29 15 53 32t30 43q10 36 12 88t-3 103t-17 93 t-30 60t-27 41t-5 46t25 45t67 38q76 28 139 39t101 15q2 0 6 1t6 1q4 -2 8 -1t6 1q38 -4 101 -13t139 -33q46 -16 69 -38t28 -45t-5 -46t-28 -41q-16 -18 -27 -60t-14 -93t0 -103t15 -90zM474 -204l-138 512l46 -8q20 -4 44 -7t46 -7q22 -2 50 1t52 7l56 12z" />
<glyph unicode="^" horiz-adv-x="1016" d="M730 452q18 -6 25 -22t1 -34q-4 -16 -18 -20l-606 -368q-16 -12 -36 -2q-16 4 -24 20t-2 32q4 6 8 14l200 278l-242 94q-18 6 -25 23t-1 33l8 12q4 6 10 8l348 212l-236 90q-18 6 -25 23t-1 33q8 14 20 22l604 368q20 8 38 2q16 -6 24 -23t0 -33q-4 -2 -4 -5t-2 -5 l-200 -280l240 -94q18 -6 26 -23t0 -33q-2 -14 -18 -22l-344 -208z" />
<glyph unicode="_" horiz-adv-x="1398" d="M954 82q-46 52 -46 132q0 44 17 83t46 68t68 46t83 17q60 0 112 -34q-44 -102 -116 -181t-164 -131zM640 1282q132 0 249 -51t204 -138t138 -204t51 -249q0 -52 -8 -101t-22 -95q-32 16 -63 25t-67 9q-56 0 -105 -21t-85 -57t-57 -84t-21 -102q0 -46 14 -84t38 -72 q-126 -58 -266 -58q-142 0 -256 56q-18 6 -44 20q-4 2 -6 4t-6 4v-2q-96 52 -168 131t-112 181l4 4q-4 2 -8 10l-7 18q-3 8 -5 18h-2q-30 92 -30 196q0 132 50 249t137 204t204 138t249 51zM272 884q10 20 4 41t-28 31q-16 10 -38 6t-32 -26q-10 -18 -6 -39t26 -33 q18 -10 40 -6t34 26zM640 1206q-22 0 -38 -17t-16 -39t16 -37t38 -15q24 0 38 15t14 37t-14 39t-38 17zM428 1040q18 12 24 34t-6 38q-12 20 -33 26t-41 -4t-23 -32t7 -40q6 -22 28 -27t44 5zM674 510q16 24 40 75t44 105t29 98t-5 52q-12 8 -48 -17t-79 -64t-82 -80 t-53 -65q-32 -46 -32 -94t34 -70q32 -22 77 -4t75 64zM924 1062q12 18 8 40t-26 32q-18 10 -39 4t-33 -26q-10 -16 -6 -38t26 -34q18 -10 39 -5t31 27zM1082 864q20 12 26 33t-4 39q-10 22 -32 26t-40 -6q-22 -10 -28 -31t4 -41q12 -22 33 -26t41 6z" />
<glyph unicode="`" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" />
<glyph unicode="a" horiz-adv-x="1144" d="M904 970l-82 -930q0 -16 -12 -28t-28 -12h-574q-16 0 -28 12t-12 28l-82 930h818zM966 1136q22 -14 22 -30v-54q0 -22 -22 -22h-334h-280h-334q-20 0 -20 22v54q0 16 20 30q20 6 38 14q16 6 31 13t21 13q4 6 11 20t13 30q8 18 12 38q4 6 9 15t19 9h180h280h180 q14 0 19 -9t9 -15q4 -20 12 -38q6 -16 13 -30t13 -20t20 -13t30 -13q18 -8 38 -14zM622 1158q0 20 -20 20h-218q-20 0 -20 -20v-14q0 -22 20 -22h218q20 0 20 22v14z" />
<glyph unicode="b" horiz-adv-x="1200" d="M142 694q0 14 14 14h4q8 0 14 -6l34 -58l38 58q2 6 12 6h2q6 0 12 -4t6 -10v-104q0 -6 -6 -12t-12 -6q-4 0 -9 6t-5 12v66l-24 -40q-8 -8 -14 -8t-12 8l-26 38v-66q0 -4 -4 -10t-10 -6t-10 6t-4 10v106zM286 592l46 108q8 12 18 12h2q14 0 18 -12l46 -108q0 -2 2 -2l2 -2 q0 -4 -6 -10t-10 -6q-6 0 -8 4t-6 8l-8 20h-62l-8 -20q-4 -12 -14 -12q-4 0 -8 6t-4 10v4zM370 630l-20 46l-18 -46h38zM428 694q0 14 14 14h46q28 0 38 -12q12 -12 12 -30q0 -34 -26 -42l20 -26q6 -6 6 -10q0 -16 -14 -16q-8 0 -10 3t-6 7l-28 36h-24v-28q0 -6 -3 -12 t-11 -6q-6 0 -10 6t-4 12v104zM486 644q22 0 22 18q0 20 -22 20h-30v-38h30zM514 436l-28 -8h-14q-14 0 -25 9t-11 23q0 30 26 34l52 14q12 4 20 7t18 3q18 0 29 -12t11 -28v-292q0 -18 -11 -30t-29 -12q-16 0 -27 12t-11 30v250zM682 206l136 236h-138q-16 0 -26 10t-10 26 t10 25t26 9h196q34 0 34 -34q0 -22 -10 -42l-146 -262q-16 -30 -36 -30q-18 0 -31 11t-13 27q0 4 3 12t5 12zM362 1026q0 -40 -27 -67t-67 -27t-66 27t-26 67v160q0 40 26 68t66 28t67 -28t27 -68v-160zM916 1026q0 -40 -26 -67t-66 -27t-68 27t-28 67v160q0 40 28 68t68 28 t66 -28t26 -68v-160zM1018 1094h-34v-90q0 -34 -13 -63t-35 -50t-51 -34t-61 -13q-68 0 -113 47t-45 113v90h-10h-226h-2v-90q0 -66 -47 -113t-113 -47q-64 0 -111 47t-47 113v90h-32q-34 0 -56 -23t-22 -55v-938q0 -34 22 -56t56 -22h940q30 0 53 22t23 56v938q0 32 -23 55 t-53 23zM994 170q0 -24 -18 -41t-42 -17h-772q-24 0 -42 17t-18 41v532q0 24 18 41t42 17h772q24 0 42 -17t18 -41v-532z" />
<glyph unicode="c" horiz-adv-x="1466" d="M544 644q0 -18 1 -29t5 -20t14 -19l28 -28q36 -42 92 -44h7q52 0 93 26q6 0 12 6t6 12l230 222l58 58q44 42 44 104t-44 106t-106 44t-104 -44l-20 -18q-52 22 -106 23h-6q-51 0 -100 -15l144 144q44 44 99 65t113 21t113 -21t99 -65l8 -8q44 -44 65 -98t21 -109 t-21 -109t-65 -98l-192 -192l-144 -144q-58 -58 -138 -78q-38 -10 -76 -10q-41 0 -82 12q-30 8 -64 28t-62 48v10q-28 28 -36 44t-22 52zM792 626q0 36 -6 53t-42 53t-92 41q-8 1 -17 1q-46 0 -83 -24q-6 -8 -12 -10t-6 -8l-230 -222l-58 -66q-44 -44 -44 -106t44 -106 t106 -44t104 44l20 20q52 -16 106 -19h13q47 0 93 19l-144 -154q-44 -40 -99 -60t-113 -20t-113 20t-99 60l-8 18q-44 40 -66 93t-22 110t22 112t66 99l192 192l144 144q58 58 138 73q32 6 65 6q47 0 93 -13q30 -6 64 -25t62 -41v-10q14 -14 22 -26t14 -24t10 -25t12 -31z " />
<glyph unicode="d" horiz-adv-x="1072" d="M906 778v-676q0 -42 -30 -72t-72 -30h-638q-44 0 -74 30t-30 72v676h844zM336 626q0 46 -44 46q-40 0 -40 -46v-464q0 -18 11 -32t29 -14q20 0 32 14t12 32v464zM534 626q0 46 -44 46q-16 0 -30 -12t-14 -34v-464q0 -18 14 -32t30 -14q20 0 32 14t12 32v464zM718 626 q0 22 -14 34t-30 12q-44 0 -44 -46v-464q0 -18 12 -32t32 -14q16 0 30 14t14 32v464zM968 864h-966v162q0 24 17 43t43 19h162v162q0 14 9 24t23 10h440q16 0 25 -10t9 -24v-162h178q24 0 42 -19t18 -43v-162zM642 1152q0 14 -10 23t-24 9h-262q-14 0 -24 -9t-10 -23v-64 h330v64z" />
<glyph unicode="e" horiz-adv-x="1430" d="M264 180q56 4 93 35q33 28 33 76v11q-4 54 -45 79q-35 22 -80 22q-7 0 -15 -1q-54 -4 -91 -34q-33 -27 -33 -75v-11q4 -54 44 -80q35 -23 80 -23q7 0 14 1zM228 754q54 4 90 35q32 28 32 74v11q-4 56 -44 80q-34 21 -79 21q-7 0 -15 -1q-56 -4 -93 -33q-33 -26 -33 -74 v-11q4 -56 45 -81q35 -22 82 -22q7 0 15 1zM204 1058q10 1 20 1q77 0 136 -42q66 -47 76 -137q2 -46 -16 -78t-46 -64l142 -66l512 348q44 25 89 25l31 -3q60 -12 124 -50l-612 -388l658 -302q-58 -46 -116 -65q-26 -8 -52 -8q-33 0 -68 13l-556 276l-130 -84q34 -26 53 -53 t23 -73q1 -11 1 -21q0 -77 -53 -126q-60 -55 -148 -61q-13 -1 -26 -1q-75 0 -132 40q-66 47 -72 137q-1 8 -1 17q0 79 53 128q58 55 146 61l150 104l-170 86q-86 0 -149 46t-67 132q-1 8 -1 17q0 79 53 129q58 56 148 62z" />
<glyph unicode="f" horiz-adv-x="1258" d="M966 1288q46 0 77 -31t31 -77v-1070q0 -46 -31 -77t-77 -31h-856q-46 0 -77 31t-31 77v1070q0 46 31 77t77 31h856zM862 592h-218v374h-212v-374h-214l322 -320z" />
<glyph unicode="g" horiz-adv-x="864" d="M686 1084v-738q0 -44 -15 -104t-54 -114t-105 -91t-168 -37t-169 37t-106 91t-55 114t-16 104v688q0 4 4 40t28 80q62 126 214 126q76 0 131 -32t83 -94q24 -44 28 -80t4 -40v-638q0 -22 -8 -49t-25 -49t-45 -37t-68 -15q-42 0 -70 15t-46 37t-26 49t-8 49v442 q0 22 14 36t36 14t36 -14t14 -36v-442q0 -16 10 -33t40 -17q28 0 37 17t9 33v638q0 8 -3 33t-17 50t-44 45t-82 20q-88 0 -128 -70q-8 -16 -11 -30l-5 -24q-2 -12 -2 -24v-688q0 -10 5 -50t28 -83t73 -78t140 -35t140 37t74 82t28 84t4 43v738q0 22 14 36t32 14q22 0 36 -14 t14 -36z" />
<glyph unicode="h" horiz-adv-x="1460" d="M342 170q0 -68 -50 -118t-122 -50q-36 0 -68 14t-55 37t-37 53t-14 64q0 72 51 123t123 51t122 -51t50 -123zM840 12h-228q0 120 -46 229t-132 195q-86 90 -195 136t-229 46v228q170 -4 321 -71t263 -179t178 -263t68 -321zM1282 12h-228q-2 216 -84 406t-224 332 t-331 225t-405 85v224q174 -2 336 -48t302 -130t256 -200t199 -257t130 -302t49 -335z" />
<glyph unicode="i" horiz-adv-x="1412" d="M644 1284q132 0 249 -50t204 -137t137 -205t50 -252q0 -132 -50 -249t-137 -204t-204 -137t-249 -50t-250 50t-205 137t-138 204t-51 249q0 134 51 252t138 205t205 137t250 50zM766 814q0 62 -70 62q-32 0 -84 -34q-28 -20 -54 -42t-54 -54q-52 -58 -52 -70v-16 q2 -4 8 -5t10 -1q8 0 36 36q76 90 106 90q10 0 10 -16q0 -10 -5 -27t-19 -43l-162 -326q-14 -34 -22 -60t-8 -46q0 -64 72 -64q36 0 84 30q24 22 51 44t57 50q28 30 40 50t12 24q0 12 -2 16q-8 10 -16 10q-4 0 -46 -44q-78 -88 -96 -88q-10 0 -10 18q0 10 5 27t19 45 l162 326q28 62 28 108zM854 1022q0 34 -28 62q-24 24 -58 24q-16 0 -32 -6t-26 -18q-24 -24 -24 -62q0 -32 24 -56q10 -12 26 -17t32 -5q36 0 58 22q28 28 28 56z" />
<glyph unicode="j" d="M288 1038v236h804l6 -236h-810zM1334 198h-162v372h-958v-372h-156q-28 0 -45 19t-17 43v640q0 28 17 49t45 21h1276q28 0 45 -21t17 -49v-640q0 -24 -17 -43t-45 -19zM444 4l-150 150v342h804v-492h-654z" />
<glyph unicode="k" horiz-adv-x="1072" d="M-4 58v1160q0 26 17 42t41 16h456l396 -392v-826q0 -24 -17 -41t-41 -17h-794q-24 0 -41 17t-17 41z" />
<glyph unicode="l" horiz-adv-x="1112" d="M1002 646v-630q0 -18 -14 -30t-32 -12h-598q-20 0 -33 12t-13 30v884q0 16 13 29t33 13h344zM272 1034q-22 0 -38 -17t-16 -41v-372h-182q-14 0 -23 10t-9 24v614q0 10 9 19t23 9h240l208 -208v-38h-212z" />
<glyph unicode="m" horiz-adv-x="1126" d="M-4 58v1160q0 26 17 42t41 16h456l396 -392v-826q0 -24 -17 -41t-41 -17h-794q-24 0 -41 17t-17 41zM448 148q74 0 141 28t116 78t78 115t29 141q0 74 -29 141t-78 116t-116 78t-141 29q-76 0 -141 -29t-115 -78t-78 -116t-28 -141q0 -76 28 -141t78 -115t115 -78 t141 -28zM198 510q0 38 11 71t29 63l344 -344q-30 -18 -63 -29t-71 -11q-52 0 -97 20t-79 54t-54 79t-20 97zM698 510q0 -76 -38 -134l-346 346q58 38 134 38q52 0 98 -20t80 -54t53 -79t19 -97z" />
<glyph unicode="n" d="M650 206l-276 276l276 278q12 10 12 28t-12 28l-136 138q-15 13 -30 13l-30 -13l-296 -298q-4 0 -5 -2t-5 -4l-138 -136q-14 -14 -10 -32q-4 -16 10 -30l138 -136q6 -6 10 -6l296 -300q15 -12 30 -12l30 12l136 140q12 10 12 28t-12 28zM808 206l276 276l-276 278 q-10 10 -10 28t10 28l138 138q14 14 28.5 14t29.5 -14l296 -298q4 0 5 -2t5 -4l138 -136q14 -14 10 -32q4 -16 -10 -30l-138 -136q-6 -6 -10 -6l-296 -300q-15 -12 -30 -12l-30 12l-136 140q-10 10 -10 28t10 28z" />
<glyph unicode="o" horiz-adv-x="1340" d="M1098 1280q74 0 126 -52t52 -126q0 -76 -50 -126v-2l-154 -156q20 -18 29 -45t9 -56t-9 -56t-29 -47l-154 -154l-104 102l-510 -514l-208 -48q-40 0 -70 30t-30 70l48 208l512 514l-100 100l154 154q20 20 47 29t56 9t56 -9t45 -29l156 154q26 26 59 38t69 12z" />
<glyph unicode="p" horiz-adv-x="1466" d="M802 1238l40 -79q22 -45 47 -87t51 -76t50 -42q14 8 24 18q4 60 44 98t90 44q8 1 16 1q41 0 81 -26q47 -31 63 -109q6 -32 6 -55q0 -16 -3 -29q-7 -30 -26 -47t-47 -23t-59 -10t-60 -10t-51 -22q34 -76 73 -152l75 -148q-26 -16 -60 -39l-70 -46q-36 -23 -73 -42t-69 -25 q-10 4 -12 14t-6 14q4 26 4 50q0 39 -10 73q-16 55 -52 80q-24 16 -55 16q-16 0 -34 -4q-53 -13 -117 -81q-36 -38 -39 -69t13 -58t44 -48l54 -39l45 -31q19 -13 15 -23l-129 -77q-63 -37 -127 -79l-91 142l-91 142q-6 -4 -16 -7t-18 -5q-14 -48 -51 -92t-83 -59 q-17 -6 -34 -6q-29 0 -57 16q-45 25 -75 113q-14 42 -14 72q0 21 7 37q17 37 54 55t85 22t88 8q4 2 9 6t9 8l-80 150l-80 148l136 81l136 83q16 -20 21 -46t6 -54l2 -56q1 -28 11 -49t34 -34q21 -11 58 -11h12q44 0 84 26t64 65q26 42 26 90q0 42 -28 79q-18 10 -38 16 l-38 11q-18 5 -31 11t-17 18q-10 26 -3 26t7 8l132 77z" />
<glyph unicode="q" horiz-adv-x="1620" d="M744 1280q154 0 290 -14t237 -38t159 -57t58 -71v-4q0 -44 -36 -94t-91 -103t-123 -109l-131 -109q-63 -53 -114 -104t-73 -95v-392q0 -38 -52 -63t-124 -25t-124 25t-52 63v392q-22 44 -72 95t-113 104l-131 109q-68 56 -124 109t-92 103t-36 94v4q0 38 58 71t159 57 t237 38t290 14zM744 1204q-118 0 -222 -8t-182 -22t-123 -32t-45 -38q0 -22 45 -39t123 -31t182 -21t222 -7t222 7t182 21t123 31t45 39q0 20 -45 38t-123 32t-182 22t-222 8z" />
<glyph unicode="r" horiz-adv-x="1442" d="M1254 184q30 -32 30 -76t-30 -76q-16 -16 -37 -24t-39 -8q-46 0 -78 32l-308 308q-2 4 -2 6t-4 6q-62 -40 -132 -62t-146 -22q-106 0 -198 40t-160 109t-108 162t-40 197q0 106 40 199t108 162t160 109t198 40t198 -40t161 -109t109 -162t40 -199q0 -76 -21 -146 t-59 -128q4 -4 5 -6t5 -6zM508 428q72 0 136 28t111 75t74 110t27 135t-27 135t-74 110t-111 75t-136 28t-135 -28t-111 -75t-75 -110t-27 -135t27 -135t75 -110t111 -75t135 -28z" />
<glyph unicode="s" horiz-adv-x="1152" d="M672 840q102 -150 182 -282l67 -116q33 -58 59 -112t42 -98t16 -74q0 -58 -25 -89t-53 -47q-36 -18 -78 -20h-368h-364q-44 2 -78 20q-30 16 -54 47t-24 89q0 30 15 74t41 98t59 112l67 116q80 132 184 282v346h-22q-20 0 -34 13t-14 35q0 18 14 31t34 13h358 q20 0 33 -13t13 -31q0 -22 -13 -35t-33 -13h-24v-346z" />
<glyph unicode="t" horiz-adv-x="1466" d="M1308 1022q30 0 52 -23t22 -53v-876q0 -32 -22 -52t-52 -20h-1232q-32 0 -54 20t-22 52v876q0 30 22 53t54 23h226v218q0 16 12 29t28 13h698q18 0 30 -13t12 -29v-218h226zM412 1022h560v96q0 16 -13 28t-29 12h-478q-16 0 -28 -12t-12 -28v-96z" />
<glyph unicode="u" d="M1360 234h-984q-28 0 -47 20t-19 46l-82 614l-204 176q-24 18 -24 48q0 28 21 47t45 19q26 0 44 -14h4l200 -172l1152 -94q28 0 49 -19t21 -43l-108 -562q0 -26 -20 -46t-48 -20zM1332 0q-36 0 -63 28q-24 24 -24 58q0 5 1 10q0 34 28 59t62 25q36 -4 61 -29t25 -65 q0 -36 -27 -61t-63 -25zM386 0q-36 0 -64 27t-28 63t28 63t64 27q34 0 62 -27t28 -63t-28 -63t-62 -27z" />
<glyph unicode="v" horiz-adv-x="1346" d="M762 538v-300l160 -128v-108l-268 108l-268 -108v108l160 128v300l-482 -162v108l482 322v374q2 30 14 54q10 20 32 37t62 17t62 -17t32 -37q12 -24 14 -54v-374l480 -322v-108z" />
<glyph unicode="w" horiz-adv-x="1172" d="M1004 984q-2 -2 -2 -6v-148q2 -6 2 -20q-2 -2 -2 -4v-2v-2q-6 -42 -45 -81t-113 -71q-76 -34 -169 -50t-191 -16h-21q-85 0 -166 14q-91 16 -163 50q-72 32 -107 73q-31 37 -31 74q0 4 1 8.5t1 8.5v146q-2 6 -2 20q2 6 2 18q18 -76 136 -128q72 -34 163 -49t187 -15 q98 0 191 17t169 49q70 30 108 67t48 77q4 -20 4 -30zM1004 674q-2 0 -2 -4v-150q2 -4 2 -18q-2 -2 -2 -8v-2q-6 -40 -45 -79t-113 -71q-76 -34 -169 -51t-191 -17h-20q-85 0 -167 15q-91 17 -163 49q-72 34 -107 75q-31 37 -31 74q0 4 1 8.5t1 8.5v144q-2 8 -2 22q2 4 2 8 v8q18 -72 136 -128q72 -32 163 -48t187 -16q98 2 191 18t169 50q70 30 108 67t48 77q2 -8 3 -15t1 -17zM1004 356q-2 -2 -2 -8v-146q2 -8 2 -20q-2 -2 -2 -6v-4q-6 -40 -45 -79t-113 -69q-76 -34 -169 -51t-191 -19q-96 0 -187 15t-163 49q-138 63 -138 153q0 3 1 7t1 6v146 q-2 6 -2 20q2 6 2 18q10 -34 43 -68t93 -60q72 -34 163 -49t187 -15q98 0 191 17t169 51q70 30 108 67t48 75q4 -20 4 -30zM134 962q-118 56 -136 128q10 40 49 77t107 67q76 34 170 50t192 18q96 0 188 -15t162 -49q118 -54 134 -128q-20 -82 -156 -142q-76 -34 -169 -51 t-191 -17h-21q-85 0 -166 14q-91 16 -163 48z" />
<glyph unicode="x" horiz-adv-x="1358" d="M794 364h11q101 0 171 -36q74 -38 126 -96t86 -124l60 -116h-628h-628l60 116q34 66 85 124t124 96q69 36 168 36h11q34 0 54 23t20 55q0 10 -15 29t-31 37q-20 22 -44 46q-36 38 -64 85t-46 98t-27 102t-9 97q0 70 26 132t72 109t108 74t134 27h2q70 0 132 -27t108 -74 t73 -109t27 -132q0 -46 -9 -97t-27 -102t-45 -98t-63 -85q-26 -24 -46 -46q-16 -18 -30 -37t-14 -29q0 -32 20 -55t48 -23z" />
<glyph unicode="y" horiz-adv-x="1298" d="M630 844v-210h426q14 0 14 -12v-288q0 -12 -14 -12h-426v-304q0 -20 -20 -20h-102q-20 0 -20 20v304h-300v-2q-18 -18 -36 2l-140 136q-10 10 -10 21t8 17l142 144q8 4 18 4t18 -8v-2h300v210h-384q-14 0 -14 12v288q0 12 14 12h384v108q0 18 20 18h102q20 0 20 -18v-108 h340v4q22 18 40 0l138 -142q9 -7 9 -16q0 -10 -9 -22l-138 -138q-8 -8 -19 -8t-19 8v2h-342z" />
<glyph unicode="z" horiz-adv-x="1072" d="M724 942l142 -228q-74 6 -152 14q-66 6 -143 11t-147 5q-102 0 -175 -11t-107 -17v-694q0 -20 -20 -20h-100q-20 0 -20 20v1246q0 20 20 20h100q20 0 20 -20v-88q34 8 107 20t175 12q70 0 147 -6t143 -12l152 -16z" />
<glyph unicode="{" horiz-adv-x="638" d="M452 8l-226 226l-226 -226v1268h452v-1268z" />
<glyph unicode="|" horiz-adv-x="822" d="M540 1266q36 -10 58 -38t24 -64q38 8 71 -6t53 -48q18 -34 13 -70t-31 -62q32 -22 46 -55t4 -69q-10 -38 -38 -60t-66 -24q10 -34 -4 -69t-48 -53q-34 -20 -69 -15t-63 29q-22 -30 -55 -45t-69 -5t-59 40t-25 66q-34 -8 -69 6t-53 48q-20 32 -15 69t29 63q-30 20 -44 53 t-4 73q10 36 39 57t65 23q-8 36 6 70t48 54q32 20 69 14t63 -30q20 32 54 45t70 3zM492 1086q-60 16 -114 -15t-70 -91q-16 -62 15 -115t93 -69q30 -8 59 -3t54 19t43 37t26 53q16 60 -15 114t-91 70zM254 610q2 -36 24 -66t60 -40q36 -10 68 5t54 45q16 -16 35 -22t39 -8 l-144 -538l-80 139q-28 49 -59 60t-80 -16t-143 -87l144 540q42 -22 82 -12z" />
<glyph unicode="}" horiz-adv-x="1406" d="M662 -52q-134 0 -250 56l250 532l92 -226l37 -92l36 -88l32 -78q15 -36 25 -58q-104 -46 -222 -46zM338 120q-74 36 -136 90t-106 123t-69 150t-25 169q0 70 17 136t47 124l522 -260zM932 188q-10 22 -24 57l-31 77l-36 88l-37 92l-92 224l-522 264q36 72 90 131t121 102 t146 67t165 24q120 0 227 -46t186 -126t125 -187t46 -229q0 -92 -27 -175t-75 -153t-115 -124t-147 -86z" />
<glyph unicode="~" d="M1328 686q32 -34 34 -80t-30 -74h-8v-484q0 -22 -16 -38t-38 -16h-238v454q0 32 -23 55t-53 23h-224q-32 0 -54 -23t-22 -55v-454h-546q-20 0 -37 16t-17 38v478h-8q-32 34 -34 79t30 73l568 572q34 28 79 27t75 -35z" />
<glyph unicode="&#xad;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
<glyph unicode="&#xc9;" horiz-adv-x="1502" />
<glyph unicode="&#xd1;" horiz-adv-x="1030" d="M718 1178q-22 -116 4 -224t61 -212t59 -205t-4 -203q-22 -80 -73 -142t-118 -99t-147 -47t-162 12t-144 72t-101 117t-50 145t11 158q18 68 58 124t90 105t107 95t110 97t98 109t73 132l4 14q8 28 31 41t51 5q26 -8 39 -31t7 -49zM418 744q26 4 44 24t16 48q-4 28 -25 46 t-49 14t-45 -24t-15 -48t24 -45t50 -15zM306 594q16 12 20 34t-8 40q-16 22 -39 28t-47 -10q-28 -16 -50 -56t-36 -90q-12 -52 -9 -102t19 -74q14 -24 37 -29t45 7q16 10 24 32t0 40q-16 46 -5 95t49 85z" />
<glyph unicode="&#xe1;" horiz-adv-x="1394" d="M252 152q18 22 18 50t-18 50q-22 18 -52 18t-48 -18q-22 -22 -22 -50t22 -50q18 -22 48 -22t52 22zM64 294l18 18l500 500q-32 92 -10 187t92 165q62 62 141 88t159 12l-200 -200l66 -234l234 -66l200 200q10 -80 -14 -158t-86 -136q-70 -76 -167 -98t-185 10l-500 -500 l-18 -18q-48 -48 -114 -48t-116 48q-48 50 -48 118t48 112z" />
<glyph unicode="&#xe9;" horiz-adv-x="1376" d="M626 1022q-80 0 -149 -30t-120 -81t-81 -120t-30 -149q0 -82 30 -152t81 -121t120 -81t149 -30t151 30t122 81t81 121t30 152q0 80 -30 149t-81 120t-122 81t-151 30zM198 580l-168 14q-10 0 -20 12t-10 36q0 22 10 33t20 11l168 16v-122zM1054 702l170 -16q46 0 46 -44 q0 -14 -12 -31t-34 -17l-170 -14q14 22 14 61t-14 61zM688 214l-14 -170q0 -12 -10 -29t-20 -17q-24 0 -36 17t-12 29l-30 170q12 -6 30 -10t37 -4t34 4t21 10zM920 1008l104 92q0 12 17 12t31 -12q10 -12 10 -32t-10 -30l-92 -106q-12 22 -31 43t-29 33zM368 304l-108 -90 q-22 -28 -44 0q-14 10 -14 29t14 31l74 108q26 -56 78 -78zM994 382l78 -108q20 -24 0 -46q0 -10 -18 -10t-30 10l-104 90q22 0 43 26zM290 932l-92 106q-24 22 0 46q26 22 48 0l104 -92q-6 0 -16 -7t-20 -17t-17 -20t-7 -16zM566 1070l16 168q0 22 17 34t27 12q48 0 48 -46 l14 -168q-22 10 -61 10t-61 -10z" />
<glyph unicode="&#xed;" horiz-adv-x="1400" d="M638 1272q134 0 251 -49t203 -135t135 -203t49 -251t-49 -250t-135 -202t-203 -135t-251 -49t-250 49t-202 135t-135 202t-49 250t49 251t135 203t202 135t250 49zM638 100q114 0 212 41t170 112t113 169t41 212t-41 212t-113 170t-170 113t-212 41v-1070z" />
<glyph unicode="&#xf1;" d="M1222 972q22 0 37 -17t15 -39v-232q0 -22 -15 -37t-37 -15h-948q-22 0 -37 15t-15 37v232q0 22 15 39t37 17h948zM408 710q40 0 66 26t26 64q0 40 -26 67t-66 27q-38 0 -65 -27t-27 -67q0 -38 27 -64t65 -26zM882 740q-6 14 -8 29t-2 31q0 40 10 62h-268l8 -29 q4 -15 4 -33q0 -16 -4 -31l-8 -29h268zM1088 710q40 0 66 26t26 64q0 40 -26 67t-66 27q-38 0 -65 -27t-27 -67q0 -38 27 -64t65 -26zM1362 1172q52 0 87 -36t35 -86v-868q0 -52 -35 -87t-87 -35h-1236q-52 0 -88 35t-36 87v868q0 50 36 86t88 36h1236zM240 130q18 0 30 13 t12 31t-12 30t-30 12q-16 0 -28 -12t-12 -30t12 -31t28 -13zM368 242q18 0 30 12t12 30q0 16 -12 28t-30 12t-30 -12t-12 -28q0 -18 12 -30t30 -12zM1140 242q16 0 28 12t12 30q0 16 -12 28t-28 12q-18 0 -30 -12t-12 -28q0 -18 12 -30t30 -12zM1266 130q18 0 30 13t12 31 t-12 30t-30 12q-16 0 -28 -12t-12 -30t12 -31t28 -13zM1396 1020q0 22 -15 39t-39 17h-1190q-22 0 -37 -17t-15 -39v-568q0 -22 15 -37t37 -15h1190q24 0 39 15t15 37v568z" />
<glyph unicode="&#xf3;" horiz-adv-x="1418" d="M304 1284q30 0 53 -22t23 -52v-226q0 -32 -23 -54t-53 -22h-226q-32 0 -55 22t-23 54v226q0 30 23 52t55 22h226zM756 1284q32 0 53 -22t21 -52v-226q0 -32 -21 -54t-53 -22h-228q-30 0 -52 22t-22 54v226q0 30 22 52t52 22h228zM1210 1284q32 0 53 -22t21 -52v-226 q0 -32 -21 -54t-53 -22h-226q-32 0 -54 22t-22 54v226q0 30 22 52t54 22h226zM304 830q30 0 53 -21t23 -53v-228q0 -30 -23 -52t-53 -22h-226q-32 0 -55 22t-23 52v228q0 32 23 53t55 21h226zM756 830q32 0 53 -21t21 -53v-228q0 -30 -21 -52t-53 -22h-228q-30 0 -52 22 t-22 52v228q0 32 22 53t52 21h228zM1210 830q32 0 53 -21t21 -53v-228q0 -30 -21 -52t-53 -22h-226q-32 0 -54 22t-22 52v228q0 32 22 53t54 21h226zM304 380q30 0 53 -23t23 -55v-228q0 -28 -23 -51t-53 -23h-226q-32 0 -55 23t-23 51v228q0 32 23 55t55 23h226zM756 380 q32 0 53 -23t21 -55v-228q0 -28 -21 -51t-53 -23h-228q-30 0 -52 23t-22 51v228q0 32 22 55t52 23h228zM1210 380q32 0 53 -23t21 -55v-228q0 -28 -21 -51t-53 -23h-226q-32 0 -54 23t-22 51v228q0 32 22 55t54 23h226z" />
<glyph unicode="&#xf6;" horiz-adv-x="1412" d="M1214 1282q32 0 53 -22t21 -54v-226q0 -32 -21 -53t-53 -21h-114h-112h-906q-32 0 -53 21t-21 53v226q0 32 21 54t53 22h906h112h114zM1214 828q32 0 53 -21t21 -51v-228q0 -32 -21 -53t-53 -21h-114h-112h-906q-32 0 -53 21t-21 53v228q0 30 21 51t53 21h906h112h114z M1214 378q32 0 53 -23t21 -55v-224q0 -30 -21 -53t-53 -23h-114h-112h-906q-32 0 -53 23t-21 53v224q0 32 21 55t53 23h906h112h114z" />
<glyph unicode="&#xfa;" horiz-adv-x="1370" d="M1248 698q24 -24 24 -57t-24 -57l-552 -552q-26 -26 -58 -26t-58 26l-554 552q-22 24 -22 57t22 57l554 552q26 24 58 24t58 -24zM720 1104q10 8 10 21t-10 23l-62 60q-22 20 -42 0l-60 -60q-10 -10 -10 -23t10 -21l60 -60q10 -10 21 -10t21 10zM558 944q20 20 0 42 l-58 60q-24 24 -44 0l-58 -60q-12 -10 -12 -21t12 -21l58 -62q20 -20 44 0zM404 786q22 22 0 46l-62 56q-22 20 -42 0l-60 -56q-22 -24 0 -46l60 -58q10 -10 21 -10t21 10zM240 624q20 20 0 42l-58 62q-10 10 -23 10t-23 -10l-58 -62q-12 -10 -12 -21t12 -21l58 -60 q10 -10 23 -10t23 10zM1196 624q10 10 10 23t-10 23l-56 58q-10 10 -23 10t-23 -10l-58 -58q-12 -10 -12 -23t12 -23l58 -58q10 -10 23 -10t23 10zM1036 466q10 8 10 20t-10 22l-58 62q-10 6 -23 6t-23 -6l-58 -62q-10 -10 -10 -22t10 -20l58 -60q10 -10 23 -10t23 10z M880 308q10 10 10 23t-10 23l-60 58q-22 20 -42 0l-62 -58q-10 -10 -10 -23t10 -23l62 -58q10 -10 21 -10t21 10zM716 146q20 24 0 44l-58 60q-10 10 -22 10t-22 -10l-58 -60q-20 -20 0 -44l58 -60q10 -10 22 -10t22 10zM738 442q10 10 10 23t-10 23l-258 258q-10 10 -23 10 t-23 -10l-226 -226q-20 -20 0 -44l260 -260q10 -10 22 -10t22 10zM1060 764q8 10 8 23t-8 19l-262 262q-22 20 -42 0l-224 -224q-10 -10 -10 -23t10 -23l258 -258q10 -10 23 -10t23 10z" />
<glyph unicode="&#x2000;" horiz-adv-x="886" />
<glyph unicode="&#x2001;" horiz-adv-x="1774" />
<glyph unicode="&#x2002;" horiz-adv-x="886" />
<glyph unicode="&#x2003;" horiz-adv-x="1774" />
<glyph unicode="&#x2004;" horiz-adv-x="590" />
<glyph unicode="&#x2005;" horiz-adv-x="442" />
<glyph unicode="&#x2006;" horiz-adv-x="294" />
<glyph unicode="&#x2007;" horiz-adv-x="294" />
<glyph unicode="&#x2008;" horiz-adv-x="220" />
<glyph unicode="&#x2009;" horiz-adv-x="354" />
<glyph unicode="&#x200a;" horiz-adv-x="98" />
<glyph unicode="&#x2010;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
<glyph unicode="&#x2011;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
<glyph unicode="&#x2012;" horiz-adv-x="1364" d="M1064 1288q48 0 90 -18t73 -50t49 -75t18 -93v-822q0 -48 -18 -91t-49 -75t-73 -51t-90 -19h-828q-48 0 -91 19t-75 51t-51 75t-19 91v822q0 50 19 93t51 75t75 50t91 18h828zM1064 756h-296h-532v-230h532h296v230z" />
<glyph unicode="&#x2013;" horiz-adv-x="1094" d="M842 1288q38 0 71 -18t58 -50t39 -75t14 -93v-822q0 -48 -14 -91t-39 -75t-58 -51t-71 -19h-655q-38 0 -72 19t-59.5 51t-40.5 75t-15 91v822q0 50 15 93t40.5 75t59.5 50t72 18h655zM842 756h-234h-421v-230h421h234v230z" />
<glyph unicode="&#x2014;" horiz-adv-x="2118" d="M1684 1288q76 0 142.5 -18t115.5 -50t77.5 -75t28.5 -93v-822q0 -48 -28.5 -91t-77.5 -75t-115.5 -51t-142.5 -19h-1310q-76 0 -144.5 19t-119 51t-80.5 75t-30 91v822q0 50 30 93t80.5 75t119 50t144.5 18h1310zM1684 756h-468h-842v-230h842h468v230z" />
<glyph unicode="&#x2018;" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" />
<glyph unicode="&#x2019;" horiz-adv-x="512" d="M0 768v512h256v-512h-256z" />
<glyph unicode="&#x201c;" horiz-adv-x="1024" d="M0 768v512h256v-512h-256zM512 768v512h256v-512h-256z" />
<glyph unicode="&#x201d;" horiz-adv-x="1024" d="M0 768v512h256v-512h-256zM512 768v512h256v-512h-256z" />
<glyph unicode="&#x202f;" horiz-adv-x="354" />
<glyph unicode="&#x205f;" horiz-adv-x="442" />
<glyph unicode="&#xe000;" horiz-adv-x="1280" d="M0 1280h1280v-1280h-1280v1280z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

View File

@ -47,26 +47,15 @@
/*---------------------------------------------------------
* OpenERP Web web module split
*---------------------------------------------------------*/
/**
* @namespace
*/
openerp.web = function(instance) {
openerp.web.core(instance);
if (openerp.web.dates) {
openerp.web.dates(instance);
}
openerp.web.formats(instance);
openerp.web.chrome(instance);
openerp.web.data(instance);
var files = ["views","search","list","form", "page","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"];
openerp.web = function(session) {
var files = ["corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"];
for(var i=0; i<files.length; i++) {
if(openerp.web[files[i]]) {
openerp.web[files[i]](instance);
openerp.web[files[i]](session);
}
}
instance.log = function() {
if (instance.connection.debug && window.console) {
session.log = function() {
if (session.connection.debug && window.console) {
console.log.apply(console, arguments);
}
}

View File

@ -5,14 +5,13 @@ openerp.web.chrome = function(openerp) {
var QWeb = openerp.web.qweb,
_t = openerp.web._t;
openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.Notification# */{
openerp.web.Notification = openerp.web.Widget.extend({
template: 'Notification',
init: function() {
this._super.apply(this, arguments);
// move to openerp.web.notification
openerp.notification = this;
},
start: function() {
this._super.apply(this, arguments);
this.$element.notify({
@ -42,7 +41,6 @@ openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.
text: text
}, opts);
}
});
openerp.web.dialog = function(element) {
@ -51,15 +49,8 @@ openerp.web.dialog = function(element) {
return result;
}
openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{
openerp.web.Dialog = openerp.web.Widget.extend({
dialog_title: "",
/**
* @constructs openerp.web.Dialog
* @extends openerp.web.OldWidget
*
* @param parent
* @param options
*/
init: function (parent, options, content) {
var self = this;
this._super(parent);
@ -128,7 +119,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
open: function(options) {
// TODO fme: bind window on resize
if (this.template) {
this.$element.html(this.render());
this.$element.html(this.renderElement());
}
var o = this.get_options(options);
openerp.web.dialog(this.$element, o).dialog('open');
@ -149,7 +140,6 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
//openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height());
},
destroy: function () {
// Destroy widget
this.close();
this.$element.dialog('destroy');
this._super();
@ -172,7 +162,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
}
},
on_managed_error: function(error) {
openerp.web.dialog($('<div>' + QWeb.render('CrashManagerWarning', {error: error}) + '</div>'), {
openerp.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
title: "OpenERP " + _.str.capitalize(error.type),
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@ -213,19 +203,12 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
min_height: '600px',
buttons: buttons
}).open();
dialog.$element.html(QWeb.render('CrashManagerError', {session: openerp.connection, error: error}));
dialog.$element.html(QWeb.render('CrashManager.error', {session: openerp.connection, error: error}));
}
});
openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loading# */{
openerp.web.Loading = openerp.web.Widget.extend({
template: 'Loading',
/**
* @constructs openerp.web.Loading
* @extends openerp.web.OldWidget
*
* @param parent
* @param element_id
*/
init: function(parent) {
this._super(parent);
this.count = 0;
@ -258,10 +241,9 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
this.count += increment;
if (this.count > 0) {
$(".loading",this.$element).text(_.str.sprintf(
_t("Loading (%d)"), this.count));
$(".loading",this.$element).show();
this.getParent().$element.addClass('loading');
this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
this.$element.show();
this.getParent().$element.addClass('oe_wait');
} else {
this.count = 0;
clearTimeout(this.long_running_timer);
@ -270,73 +252,60 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
this.blocked_ui = false;
$.unblockUI();
}
$(".loading",this.$element).fadeOut();
this.getParent().$element.removeClass('loading');
this.$element.fadeOut();
this.getParent().$element.removeClass('oe_wait');
}
}
});
openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Database# */{
template: "DatabaseManager",
/**
* @constructs openerp.web.Database
* @extends openerp.web.OldWidget
*
* @param parent
* @param element_id
* @param option_id
*/
init: function(parent, element_id, option_id) {
this._super(parent, element_id);
openerp.web.DatabaseManager = openerp.web.Widget.extend({
init: function(parent) {
this._super(parent);
this.unblockUIFunction = $.unblockUI;
$.validator.addMethod('matches', function (s, _, re) {
return new RegExp(re).test(s);
}, _t("Invalid database name"));
},
start: function() {
this.$option_id = $("#oe_db_options");
var self = this;
var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
function(result) { self.db_list = result.db_list; },
function (_, ev) { ev.preventDefault(); self.db_list = null; });
var fetch_langs = this.rpc("/web/session/get_lang_list", {}, function(result) {
if (result.error) {
self.display_error(result);
return;
}
function(result) {
self.db_list = result.db_list;
},
function (_, ev) {
ev.preventDefault();
self.db_list = null;
});
var fetch_langs = this.rpc("/web/session/get_lang_list", {}).then(function(result) {
self.lang_list = result.lang_list;
});
$.when(fetch_db, fetch_langs).then(function () {self.do_create();});
this.$element.find('#db-create').click(this.do_create);
this.$element.find('#db-drop').click(this.do_drop);
this.$element.find('#db-backup').click(this.do_backup);
this.$element.find('#db-restore').click(this.do_restore);
this.$element.find('#db-change-password').click(this.do_change_password);
this.$element.find('#back-to-login').click(function() {
self.hide();
return $.when(fetch_db, fetch_langs).then(self.do_render);
},
do_render: function() {
var self = this;
self.$element.html(QWeb.render("DatabaseManager", { widget : self }));
self.$element.find(".oe_database_manager_menu").tabs();
self.$element.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
self.$element.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
self.$element.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
self.$element.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
self.$element.find("form[name=change_pwd_form]").validate({
messages: {
old_pwd: "Please enter your previous password",
new_pwd: "Please enter your new password",
confirm_pwd: {
required: "Please confirm your new password",
equalTo: "The confirmation does not match the password"
}
},
submitHandler: self.do_change_password
});
self.$element.find("#back_to_login").click(self.do_exit);
},
destroy: function () {
this.hide();
this.$option_id.empty();
this.$element
.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login')
.unbind('click')
.end()
.empty();
this.$element.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty();
this._super();
},
show: function () {
this.$element.closest(".login")
.addClass("database_block");
},
hide: function () {
this.$element.closest(".login")
.removeClass("database_block")
},
/**
* Converts a .serializeArray() result into a dict. Does not bother folding
* multiple identical keys into an array, last key wins.
@ -383,157 +352,96 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
]
}).html(error.error);
},
do_create: function() {
do_create: function(form) {
var self = this;
self.$option_id.html(QWeb.render("Database.CreateDB", self));
self.$option_id.find("form[name=create_db_form]").validate({
submitHandler: function (form) {
var fields = $(form).serializeArray();
self.rpc("/web/database/create", {'fields': fields}, function(result) {
if (self.db_list) {
self.db_list.push(self.to_object(fields)['db_name']);
self.db_list.sort();
self.getParent().set_db_list(self.db_list);
}
var fields = $(form).serializeArray();
self.rpc("/web/database/create", {'fields': fields}, function(result) {
var form_obj = self.to_object(fields);
self.getParent().do_login( form_obj['db_name'], 'admin', form_obj['create_admin_pwd']);
self.destroy();
});
var form_obj = self.to_object(fields);
self.getParent().do_login(
form_obj['db_name'],
'admin',
form_obj['create_admin_pwd']);
self.destroy();
});
},
do_drop: function(form) {
var self = this;
var $form = $(form),
fields = $form.serializeArray(),
$db_list = $form.find('[name=drop_db]'),
db = $db_list.val();
if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
return;
}
self.rpc("/web/database/drop", {'fields': fields}, function(result) {
if (result.error) {
self.display_error(result);
return;
}
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
self.start();
});
},
do_backup: function(form) {
var self = this;
self.blockUI();
self.session.get_file({
form: form,
success: function () {
self.do_notify(_t("Backed"), _t("Database backed up successfully"));
},
error: openerp.webclient.crashmanager.on_rpc_error,
complete: function() {
self.unblockUI();
}
});
},
do_drop: function() {
do_restore: function(form) {
var self = this;
self.$option_id.html(QWeb.render("DropDB", self));
self.$option_id.find("form[name=drop_db_form]").validate({
submitHandler: function (form) {
var $form = $(form),
fields = $form.serializeArray(),
$db_list = $form.find('[name=drop_db]'),
db = $db_list.val();
self.blockUI();
$(form).ajaxSubmit({
url: '/web/database/restore',
type: 'POST',
resetForm: true,
success: function (body) {
// If empty body, everything went fine
if (!body) { return; }
if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
return;
}
self.rpc("/web/database/drop", {'fields': fields}, function(result) {
if (result.error) {
self.display_error(result);
return;
}
$db_list.find(':selected').remove();
if (self.db_list) {
self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
self.getParent().set_db_list(self.db_list);
}
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
});
}
});
},
do_backup: function() {
var self = this;
self.$option_id
.html(QWeb.render("BackupDB", self))
.find("form[name=backup_db_form]").validate({
submitHandler: function (form) {
self.blockUI();
self.session.get_file({
form: form,
success: function () {
self.do_notify(_t("Backed"),
_t("Database backed up successfully"));
},
error: openerp.webclient.crashmanager.on_rpc_error,
complete: function() {
self.unblockUI();
}
});
}
});
},
do_restore: function() {
var self = this;
self.$option_id.html(QWeb.render("RestoreDB", self));
self.$option_id.find("form[name=restore_db_form]").validate({
submitHandler: function (form) {
self.blockUI();
$(form).ajaxSubmit({
url: '/web/database/restore',
type: 'POST',
resetForm: true,
success: function (body) {
// TODO: ui manipulations
// note: response objects don't work, but we have the
// HTTP body of the response~~
// If empty body, everything went fine
if (!body) { return; }
if (body.indexOf('403 Forbidden') !== -1) {
self.display_error({
title: 'Access Denied',
error: 'Incorrect super-administrator password'
})
} else {
self.display_error({
title: 'Restore Database',
error: 'Could not restore the database'
})
}
},
complete: function() {
self.unblockUI();
self.do_notify(_t("Restored"), _t("Database restored successfully"));
}
});
}
});
},
do_change_password: function() {
var self = this;
self.$option_id.html(QWeb.render("Change_DB_Pwd", self));
self.$option_id.find("form[name=change_pwd_form]").validate({
messages: {
old_pwd: "Please enter your previous password",
new_pwd: "Please enter your new password",
confirm_pwd: {
required: "Please confirm your new password",
equalTo: "The confirmation does not match the password"
if (body.indexOf('403 Forbidden') !== -1) {
self.display_error({
title: 'Access Denied',
error: 'Incorrect super-administrator password'
})
} else {
self.display_error({
title: 'Restore Database',
error: 'Could not restore the database'
})
}
},
submitHandler: function (form) {
self.rpc("/web/database/change_password", {
'fields': $(form).serializeArray()
}, function(result) {
if (result.error) {
self.display_error(result);
return;
}
self.do_notify("Changed Password", "Password has been changed successfully");
});
complete: function() {
self.unblockUI();
self.do_notify(_t("Restored"), _t("Database restored successfully"));
}
});
},
do_change_password: function(form) {
var self = this;
self.rpc("/web/database/change_password", {
'fields': $(form).serializeArray()
}, function(result) {
if (result.error) {
self.display_error(result);
return;
}
self.do_notify("Changed Password", "Password has been changed successfully");
});
},
do_exit: function () {
}
});
openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login# */{
remember_credentials: true,
openerp.web.Login = openerp.web.Widget.extend({
template: "Login",
/**
* @constructs openerp.web.Login
* @extends openerp.web.OldWidget
*
* @param parent
* @param element_id
*/
remember_credentials: true,
init: function(parent) {
this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
@ -550,29 +458,35 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
},
start: function() {
var self = this;
this.database = new openerp.web.Database(this);
this.database.appendTo(this.$element);
this.$element.find('#oe-db-config').click(function() {
self.database.show();
self.$element.find("form").submit(self.on_submit);
self.$element.find('.oe_login_manage_db').click(function() {
self.$element.find('.oe_login_bottom').hide();
self.$element.find('.oe_login_pane').hide();
self.databasemanager = new openerp.web.DatabaseManager(self);
self.databasemanager.appendTo(self.$element);
self.databasemanager.do_exit.add_last(function() {
self.databasemanager.destroy();
self.$element.find('.oe_login_bottom').show();
self.$element.find('.oe_login_pane').show();
self.load_db_list();
})
});
this.$element.find("form").submit(this.on_submit);
this.rpc("/web/database/get_list", {}, function(result) {
self.load_db_list();
},
load_db_list: function () {
var self = this;
self.rpc("/web/database/get_list", {}, function(result) {
self.set_db_list(result.db_list);
},
function(error, event) {
}, function(error, event) {
if (error.data.fault_code === 'AccessDenied') {
event.preventDefault();
}
});
},
set_db_list: function (list) {
this.$element.find("[name=db]").replaceWith(
openerp.web.qweb.render('Login_dblist', {
db_list: list, selected_db: this.selected_db}))
this.$element.find("[name=db]").replaceWith(openerp.web.qweb.render('Login.dblist', { db_list: list, selected_db: this.selected_db}))
},
on_submit: function(ev) {
if(ev) {
@ -598,15 +512,15 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
*/
do_login: function (db, login, password) {
var self = this;
this.$element.removeClass('login_invalid');
this.$element.removeClass('oe_login_invalid');
this.session.on_session_invalid.add({
callback: function () {
self.$element.addClass("login_invalid");
self.$element.addClass("oe_login_invalid");
},
unique: true
});
this.session.session_authenticate(db, login, password).then(function() {
self.$element.removeClass("login_invalid");
self.$element.removeClass("oe_login_invalid");
if (self.has_local_storage) {
if(self.remember_credentials) {
localStorage.setItem('last_db_login_success', db);
@ -624,13 +538,7 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
}
});
openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
/**
* @constructs openerp.web.Menu
* @extends openerp.web.Widget
*
* @param parent
*/
openerp.web.Menu = openerp.web.Widget.extend({
template: 'Menu',
init: function() {
this._super.apply(this, arguments);
@ -774,14 +682,8 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
}
});
openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMenu# */{
openerp.web.UserMenu = openerp.web.Widget.extend({
template: "UserMenu",
/**
* @constructs openerp.web.UserMenu
* @extends openerp.web.Widget
*
* @param parent
*/
init: function(parent) {
this._super(parent);
this.update_promise = $.Deferred().resolve();
@ -910,6 +812,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
dataset.call ('action_get','',function (result){
self.rpc('/web/action/load', {action_id:result}, function(result){
action_manager.do_action(_.extend(result['result'], {
target: 'inline',
res_id: self.session.uid,
res_model: 'res.users',
flags: {
@ -941,7 +844,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
]
}).open();
action_manager.appendTo(this.dialog.$element);
action_manager.render(this.dialog);
action_manager.renderElement(this.dialog);
},
on_menu_about: function() {
var self = this;
@ -968,11 +871,7 @@ openerp.web.UserMenu = openerp.web.Widget.extend(/** @lends openerp.web.UserMen
}
});
openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{
/**
* @constructs openerp.web.WebClient
* @extends openerp.web.Widget
*/
openerp.web.WebClient = openerp.web.Widget.extend({
init: function(parent) {
var self = this;
this._super(parent);
@ -982,7 +881,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
},
start: function() {
var self = this;
this.$element.addClass("openerp openerp2");
this.$element.addClass("openerp openerp-web-client-container");
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
this.$element.addClass("kitten-mode-activated");
this.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) {
@ -1126,16 +1025,15 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
}
});
openerp.web.EmbeddedClient = openerp.web.OldWidget.extend({
openerp.web.EmbeddedClient = openerp.web.Widget.extend({
template: 'EmptyComponent',
init: function(action_id, options) {
this._super();
init: function(parent, action_id, options) {
this._super(parent);
// TODO take the xmlid of a action instead of its id
this.action_id = action_id;
this.options = options || {};
this.am = new openerp.web.ActionManager(this);
},
start: function() {
var self = this;
this.am.appendTo(this.$element.addClass('openerp'));
@ -1152,7 +1050,6 @@ openerp.web.EmbeddedClient = openerp.web.OldWidget.extend({
self.am.do_action(action);
});
}
});
openerp.web.embed = function (origin, dbname, login, key, action, options) {
@ -1168,7 +1065,7 @@ openerp.web.embed = function (origin, dbname, login, key, action, options) {
}
openerp.connection.session_bind(origin).then(function () {
openerp.connection.session_authenticate(dbname, login, key, true).then(function () {
var client = new openerp.web.EmbeddedClient(action, options);
var client = new openerp.web.EmbeddedClient(null, action, options);
client.insertAfter(currentScript);
});
});

View File

@ -0,0 +1,142 @@
/*---------------------------------------------------------
* OpenERP Web core
*--------------------------------------------------------*/
var console;
if (!console) {
console = {log: function () {}};
}
if (!console.debug) {
console.debug = console.log;
}
openerp.web.coresetup = function(openerp) {
/** Configure default qweb */
openerp.web._t = new openerp.web.TranslationDataBase().build_translation_function();
/**
* Lazy translation function, only performs the translation when actually
* printed (e.g. inserted into a template)
*
* Useful when defining translatable strings in code evaluated before the
* translation database is loaded, as class attributes or at the top-level of
* an OpenERP Web module
*
* @param {String} s string to translate
* @returns {Object} lazy translation object
*/
openerp.web._lt = function (s) {
return {toString: function () { return openerp.web._t(s); }}
};
openerp.web.qweb = new QWeb2.Engine();
openerp.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined);
openerp.web.qweb.default_dict = {
'_' : _,
'_t' : openerp.web._t
};
openerp.web.qweb.preprocess_node = function() {
// Note that 'this' is the Qweb Node
switch (this.node.nodeType) {
case 3:
case 4:
// Text and CDATAs
var translation = this.node.parentNode.attributes['t-translation'];
if (translation && translation.value === 'off') {
return;
}
var ts = _.str.trim(this.node.data);
if (ts.length === 0) {
return;
}
var tr = openerp.web._t(ts);
if (tr !== ts) {
this.node.data = tr;
}
break;
case 1:
// Element
var attr, attrs = ['label', 'title', 'alt'];
while (attr = attrs.pop()) {
if (this.attributes[attr]) {
this.attributes[attr] = openerp.web._t(this.attributes[attr]);
}
}
}
};
/** Configure blockui */
if ($.blockUI) {
$.blockUI.defaults.baseZ = 1100;
$.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
}
/** Custom jQuery plugins */
$.fn.getAttributes = function() {
var o = {};
if (this.length) {
for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) {
attr = attrs.item(i)
o[attr.nodeName] = attr.nodeValue;
}
}
return o;
}
/** Jquery extentions */
$.Mutex = (function() {
function Mutex() {
this.def = $.Deferred().resolve();
}
Mutex.prototype.exec = function(action) {
var current = this.def;
var next = this.def = $.Deferred();
return current.pipe(function() {
return $.when(action()).always(function() {
next.resolve();
});
});
};
return Mutex;
})();
/** Setup default connection */
openerp.connection = new openerp.web.Connection();
openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug;
$.async_when = function() {
var async = false;
var def = $.Deferred();
$.when.apply($, arguments).then(function() {
var args = arguments;
var action = function() {
def.resolve.apply(def, args);
};
if (async)
action();
else
setTimeout(action, 0);
}, function() {
var args = arguments;
var action = function() {
def.reject.apply(def, args);
};
if (async)
action();
else
setTimeout(action, 0);
});
async = true;
return def;
};
// special tweak for the web client
var old_async_when = $.async_when;
$.async_when = function() {
if (openerp.connection.synch)
return $.when.apply(this, arguments);
else
return old_async_when.apply(this, arguments);
};
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -207,6 +207,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.$element.html(QWeb.render(this._template, this));
// Head hook
// Selecting records
this.$element.find('.all-record-selector').click(function(){
self.$element.find('.oe-record-selector input').prop('checked',
self.$element.find('.all-record-selector').prop('checked') || false);
@ -215,70 +216,95 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
'selected', [selection.ids, selection.records]);
});
this.$element.find('.oe-list-add')
.click(this.proxy('do_add_record'))
.attr('disabled', grouped && this.options.editable);
this.$element.find('.oe-list-delete')
.attr('disabled', true)
.click(this.proxy('do_delete_selected'));
// Sorting columns
this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) {
e.stopPropagation();
var $this = $(this);
self.dataset.sort($this.data('id'));
if ($this.find('span').length) {
$this.find('span').toggleClass(
'ui-icon-triangle-1-s ui-icon-triangle-1-n');
$this.find('span').toggleClass( 'ui-icon-triangle-1-s ui-icon-triangle-1-n');
} else {
$this.append('<span class="ui-icon ui-icon-triangle-1-n">')
.siblings('.oe-sortable').find('span').remove();
$this.append('<span class="ui-icon ui-icon-triangle-1-n">') .siblings('.oe-sortable').find('span').remove();
}
self.reload_content();
});
this.$element.find('.oe-list-pager')
.delegate('button', 'click', function () {
var $this = $(this);
switch ($this.data('pager-action')) {
case 'first':
self.page = 0; break;
case 'last':
self.page = Math.floor(
self.dataset.size() / self.limit());
break;
case 'next':
self.page += 1; break;
case 'previous':
self.page -= 1; break;
}
self.reload_content();
}).find('.oe-pager-state')
.click(function (e) {
e.stopPropagation();
var $this = $(this);
// Add and delete
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$element.find('.oe_list_buttons').replaceWith(this.$buttons);
}
this.$buttons.find('.oe_list_add')
.click(this.proxy('do_add_record'))
.prop('disabled', grouped && this.options.editable)
.end()
.find('.oe_list_delete')
.click(this.proxy('do_delete_selected'))
.prop('disabled', true);
}
var $select = $('<select>')
.appendTo($this.empty())
.click(function (e) {e.stopPropagation();})
.append('<option value="80">80</option>' +
'<option value="100">100</option>' +
'<option value="200">200</option>' +
'<option value="500">500</option>' +
'<option value="NaN">' + _t("Unlimited") + '</option>')
.change(function () {
var val = parseInt($select.val(), 10);
self._limit = (isNaN(val) ? null : val);
self.page = 0;
self.reload_content();
})
.val(self._limit || 'NaN');
});
if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
// Pager
if (!this.$pager) {
this.$pager = $(QWeb.render("ListView.pager", {'widget':self}));
if (this.options.$buttons) {
this.$pager.appendTo(this.options.$pager);
} else {
this.$element.find('.oe_list_pager').replaceWith(this.$pager);
}
this.$pager
.on('click', 'a[data-pager-action]', function () {
var $this = $(this);
var max_page = Math.floor(self.dataset.size() / self.limit());
switch ($this.data('pager-action')) {
case 'first':
self.page = 0; break;
case 'last':
self.page = max_page - 1;
break;
case 'next':
self.page += 1; break;
case 'previous':
self.page -= 1; break;
}
if (self.page < 0) {
self.page = max_page;
} else if (self.page > max_page) {
self.page = 0;
}
self.reload_content();
}).find('.oe-pager-state')
.click(function (e) {
e.stopPropagation();
var $this = $(this);
var $select = $('<select>')
.appendTo($this.empty())
.click(function (e) {e.stopPropagation();})
.append('<option value="80">80</option>' +
'<option value="100">100</option>' +
'<option value="200">200</option>' +
'<option value="500">500</option>' +
'<option value="NaN">' + _t("Unlimited") + '</option>')
.change(function () {
var val = parseInt($select.val(), 10);
self._limit = (isNaN(val) ? null : val);
self.page = 0;
self.reload_content();
})
.val(self._limit || 'NaN');
});
}
// Sidebar
if (!this.sidebar && this.options.sidebar && this.options.$sidebar) {
this.sidebar = new openerp.web.Sidebar(this);
this.sidebar.appendTo(this.options.$sidebar);
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
}
},
/**
@ -295,28 +321,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.dataset._length = dataset._length;
}
var limit = this.limit(),
total = dataset.size(),
first = (this.page * limit),
last;
if (!limit || (total - first) < limit) {
last = total;
} else {
last = first + limit;
}
this.$element.find('span.oe-pager-state').empty().text(_.str.sprintf(
_t("[%(first_record)d to %(last_record)d] of %(records_count)d"), {
first_record: first + 1,
last_record: last,
records_count: total
}));
var page = this.page + 1,
total = Math.floor(dataset.size() / this.limit()) + 1;
this.$element
.find('button[data-pager-action=first], button[data-pager-action=previous]')
.attr('disabled', this.page === 0)
.end()
.find('button[data-pager-action=last], button[data-pager-action=next]')
.attr('disabled', last === total);
this.$pager.find('.oe-pager-state').text(isNaN(total)
? '-' : _.str.sprintf('%d / %d', page, total));
},
/**
* Sets up the listview's columns: merges view and fields data, move
@ -406,7 +415,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @param {String} [view="page"] the view type to switch to
*/
select_record:function (index, view) {
view = view || index == null ? 'form' : 'page';
view = view || index == null ? 'form' : 'form';
this.dataset.index = index;
_.delay(_.bind(function () {
this.do_switch_view(view);
@ -417,12 +426,24 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
if (this.sidebar) {
this.sidebar.$element.show();
}
if (this.$buttons) {
this.$buttons.show();
}
if (this.$pager) {
this.$pager.show();
}
},
do_hide: function () {
this._super();
if (this.sidebar) {
this.sidebar.$element.hide();
}
if (this.$buttons) {
this.$buttons.hide();
}
if (this.$pager) {
this.$pager.hide();
}
this._super();
},
/**
* Reloads the list view based on the current settings (dataset & al)
@ -478,7 +499,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
reload: function () {
return this.reload_content();
},
do_load_state: function(state, warm) {
var reload = false;
if (state.page && this.page !== state.page) {
@ -543,16 +563,20 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
* @param {Array} records selected record values
*/
do_select: function (ids, records) {
this.$element.find('.oe-list-delete').attr('disabled', !ids.length);
this.$buttons.find('.oe_list_delete').attr('disabled', !ids.length);
if (!ids.length) {
this.dataset.index = 0;
if (this.sidebar) { this.sidebar.do_fold(); }
if (this.sidebar) {
this.sidebar.$element.hide();
}
this.compute_aggregates();
return;
}
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
if (this.sidebar) { this.sidebar.do_unfold(); }
if (this.sidebar) {
this.sidebar.$element.show();
}
this.compute_aggregates(_(records).map(function (record) {
return {count: 1, values: record};
@ -823,7 +847,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis
this.records.bind(event, callback);
}, this);
this.$_element = $('<tbody class="ui-widget-content">')
this.$_element = $('<tbody>')
.appendTo(document.body)
.delegate('th.oe-record-selector', 'click', function (e) {
e.stopPropagation();

View File

@ -235,6 +235,7 @@ openerp.web.list_editable = function (openerp) {
}
self.edition = true;
self.edition_id = record_id;
$new_row.addClass("oe_form_container");
self.edition_form = new openerp.web.ListEditableFormView(self.view, self.dataset, false);
self.edition_form.$element = $new_row;
self.edition_form.editable_list = self;
@ -366,52 +367,56 @@ openerp.web.list_editable = function (openerp) {
});
}
});
if (!openerp.web.list) {
openerp.web.list = {};
}
if (!openerp.web.list.form) {
openerp.web.list.form = {};
}
openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
form_template: 'ListView.row.frame'
});
var form_widgets = openerp.web.form.widgets;
openerp.web.list.form.widgets = form_widgets.extend({
'frame': 'openerp.web.list.form.WidgetFrame'
});
// All form widgets inherit a problematic behavior from
// openerp.web.form.WidgetFrame: the cell itself is removed when invisible
// whether it's @invisible or @attrs[invisible]. In list view, only the
// former should completely remove the cell. We need to override update_dom
// on all widgets since we can't just hit on widget itself (I think)
var list_form_widgets = openerp.web.list.form.widgets;
_(form_widgets.map).each(function (widget_path, key) {
if (key === 'frame') { return; }
var new_path = 'openerp.web.list.form.' + key;
openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
update_dom: function () {
this.$element.children().css('visibility', '');
if (this.modifiers.tree_invisible) {
var old_invisible = this.invisible;
this.invisible = true;
this._super();
this.invisible = old_invisible;
} else if (this.invisible) {
this.$element.children().css('visibility', 'hidden');
} else {
this._super();
}
}
});
list_form_widgets.add(key, new_path);
});
openerp.web.ListEditableFormView = openerp.web.FormView.extend({
form_template: 'ListView.row.form',
init: function() {
this._super.apply(this, arguments);
this.registry = openerp.web.list.form.widgets;
this._super.apply(this, arguments);
this.rendering_engine = new openerp.web.ListEditableRenderingEngine(this);
},
renderElement: function() {}
});
openerp.web.ListEditableRenderingEngine = openerp.web.Class.extend({
init: function(view) {
this.view = view;
},
set_fields_view: function(fields_view) {
this.fvg = fields_view;
},
set_tags_registry: function(tags_registry) {
this.tags_registry = tags_registry;
},
set_fields_registry: function(fields_registry) {
this.fields_registry = fields_registry;
},
render_to: function($element) {
var self = this;
var xml = openerp.web.json_node_to_xml(this.fvg.arch);
var $xml = $(xml);
if (this.view.editable_list.options.selectable)
$("<td>").appendTo($element);
if (this.view.editable_list.options.isClarkGable)
$("<td>").appendTo($element);
$xml.children().each(function(i, el) {
var modifiers = JSON.parse($(el).attr("modifiers") || "{}");
var $td = $("<td>");
if (modifiers.tree_invisible === true)
$td.hide();
var tag_name = el.tagName.toLowerCase();
var key = tag_name;
if (tag_name === "field") {
var name = $(el).attr("name");
key = $(el).attr('widget') || self.fvg.fields[name].type;
}
var obj = self.view.fields_registry.get_object(key);
var w = new (obj)(self.view, openerp.web.xml_to_json(el));
w.appendTo($td);
$td.appendTo($element);
});
$("<td><button class='oe-edit-row-save' type='button'></button></td>").appendTo($element);
},
});
};

View File

@ -1,288 +0,0 @@
openerp.web.page = function (openerp) {
var _t = openerp.web._t,
_lt = openerp.web._lt;
openerp.web.views.add('page', 'openerp.web.PageView');
openerp.web.PageView = openerp.web.FormView.extend({
form_template: "PageView",
display_name: _lt('Page'),
init: function () {
this._super.apply(this, arguments);
this.registry = openerp.web.page.readonly;
},
reload: function () {
if (this.dataset.index == null) {
this.do_prev_view();
return $.Deferred().reject().promise();
}
return this._super();
},
on_loaded: function(data) {
this._super(data);
this.$form_header.find('button.oe_form_button_edit').click(this.on_button_edit);
this.$form_header.find('button.oe_form_button_create').click(this.on_button_create);
this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate);
this.$form_header.find('button.oe_form_button_delete').click(this.on_button_delete);
},
on_button_edit: function() {
return this.do_switch_view('form');
},
on_button_create: function() {
this.dataset.index = null;
return this.do_switch_view('form');
},
on_button_duplicate: function() {
var self = this;
var def = $.Deferred();
$.when(this.has_been_loaded).then(function() {
self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) {
return self.on_created({ result : new_id });
}).then(function() {
return self.do_switch_view('form');
}).then(function() {
def.resolve();
});
});
return def.promise();
},
on_button_delete: function() {
var self = this;
var def = $.Deferred();
$.when(this.has_been_loaded).then(function() {
if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) {
self.dataset.unlink([self.datarecord.id]).then(function() {
self.on_pager_action('next');
def.resolve();
});
} else {
$.async_when().then(function () {
def.reject();
})
}
});
return def.promise();
}
});
/** @namespace */
openerp.web.page = {};
openerp.web.page.WidgetFrameReadonly = openerp.web.form.WidgetFrame.extend({
form_template: 'WidgetFrame.readonly'
});
openerp.web.page.FieldReadonly = openerp.web.form.Field.extend({
});
openerp.web.page.FieldCharReadonly = openerp.web.page.FieldReadonly.extend({
form_template: 'FieldChar.readonly',
init: function(view, node) {
this._super(view, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
},
set_value: function (value) {
this._super.apply(this, arguments);
var show_value = openerp.web.format_value(value, this, '');
if (this.password) {
show_value = new Array(show_value.length + 1).join('*');
}
this.$element.find('div').text(show_value);
return show_value;
}
});
openerp.web.page.FieldFloatReadonly = openerp.web.page.FieldCharReadonly.extend({
init: function (view, node) {
this._super(view, node);
if (node.attrs.digits) {
this.digits = py.eval(node.attrs.digits);
} else {
this.digits = view.fields_view.fields[node.attrs.name].digits;
}
}
});
openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({
form_template: 'FieldURI.readonly',
scheme: null,
format_value: function (value) {
return value;
},
set_value: function (value) {
if (!value) {
this.$element.find('a').text('').attr('href', '#');
return;
}
this.$element.find('a')
.attr('href', this.scheme + ':' + value)
.text(this.format_value(value));
}
});
openerp.web.page.FieldEmailReadonly = openerp.web.page.FieldURIReadonly.extend({
scheme: 'mailto'
});
openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({
set_value: function (value) {
if (!value) {
this.$element.find('a').text('').attr('href', '#');
return;
}
var s = /(\w+):(.+)/.exec(value);
if (!s) {
value = "http://" + value;
}
this.$element.find('a').attr('href', value).text(value);
}
});
openerp.web.page.FieldBooleanReadonly = openerp.web.form.FieldBoolean.extend({
update_dom: function() {
this._super.apply(this, arguments);
this.$element.find('input').prop('disabled', true);
}
});
openerp.web.page.FieldSelectionReadonly = openerp.web.page.FieldReadonly.extend({
form_template: 'FieldChar.readonly',
init: function(view, node) {
// lifted straight from r/w version
var self = this;
this._super(view, node);
this.values = _.clone(this.field.selection);
_.each(this.values, function(v, i) {
if (v[0] === false && v[1] === '') {
self.values.splice(i, 1);
}
});
this.values.unshift([false, '']);
},
set_value: function (value) {
value = value === null ? false : value;
value = value instanceof Array ? value[0] : value;
var option = _(this.values)
.detect(function (record) { return record[0] === value; });
this._super(value);
this.$element.find('div').text(option ? option[1] : this.values[0][1]);
}
});
openerp.web.page.FieldMany2OneReadonly = openerp.web.page.FieldURIReadonly.extend({
set_value: function (value) {
value = value || null;
this.invalid = false;
var self = this;
this.value = value;
self.update_dom();
self.on_value_changed();
var real_set_value = function(rval) {
self.value = rval;
self.$element.find('a')
.unbind('click')
.text(rval ? rval[1] : '')
.click(function () {
self.do_action({
type: 'ir.actions.act_window',
res_model: self.field.relation,
res_id: self.value[0],
context: self.build_context(),
views: [[false, 'page'], [false, 'form']],
target: 'current'
});
return false;
});
};
if (value && !(value instanceof Array)) {
new openerp.web.DataSetStatic(
this, this.field.relation, self.build_context())
.name_get([value], function(data) {
real_set_value(data[0]);
});
} else {
$.async_when().then(function() {real_set_value(value);});
}
},
get_value: function() {
if (!this.value) {
return false;
} else if (this.value instanceof Array) {
return this.value[0];
} else {
return this.value;
}
}
});
openerp.web.page.FieldReferenceReadonly = openerp.web.page.FieldMany2OneReadonly.extend({
set_value: function (value) {
if (!value) {
return this._super(null);
}
var reference = value.split(',');
this.field.relation = reference[0];
var id = parseInt(reference[1], 10);
return this._super(id);
},
get_value: function () {
if (!this.value) {
return null;
}
var id;
if (typeof this.value === 'number') {
// name_get has not run yet
id = this.value;
} else {
id = this.value[0];
}
return _.str.sprintf('%s,%d', this.field.relation, id);
}
});
openerp.web.page.FieldMany2ManyReadonly = openerp.web.form.FieldMany2Many.extend({
force_readonly: true
});
openerp.web.page.FieldOne2ManyReadonly = openerp.web.form.FieldOne2Many.extend({
force_readonly: true
});
openerp.web.page.FieldBinaryImageReaonly = openerp.web.form.FieldBinaryImage.extend({
update_dom: function() {
this._super.apply(this, arguments);
this.$element.find('.oe-binary').hide();
}
});
openerp.web.page.FieldBinaryFileReadonly = openerp.web.form.FieldBinary.extend({
form_template: 'FieldURI.readonly',
start: function() {
this._super.apply(this, arguments);
var self = this;
this.$element.find('a').click(function() {
if (self.value) {
self.on_save_as();
}
return false;
});
},
set_value: function(value) {
this._super.apply(this, arguments);
this.$element.find('a').show(!!value);
if (value) {
var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
this.$element.find('a').text(show_value);
}
}
});
openerp.web.page.readonly = openerp.web.form.widgets.extend({
'frame': 'openerp.web.page.WidgetFrameReadonly',
'char': 'openerp.web.page.FieldCharReadonly',
'id': 'openerp.web.page.FieldCharReadonly',
'email': 'openerp.web.page.FieldEmailReadonly',
'url': 'openerp.web.page.FieldUrlReadonly',
'text': 'openerp.web.page.FieldCharReadonly',
'date': 'openerp.web.page.FieldCharReadonly',
'datetime': 'openerp.web.page.FieldCharReadonly',
'selection' : 'openerp.web.page.FieldSelectionReadonly',
'many2one': 'openerp.web.page.FieldMany2OneReadonly',
'many2many' : 'openerp.web.page.FieldMany2ManyReadonly',
'one2many' : 'openerp.web.page.FieldOne2ManyReadonly',
'one2many_list' : 'openerp.web.page.FieldOne2ManyReadonly',
'reference': 'openerp.web.page.FieldReferenceReadonly',
'boolean': 'openerp.web.page.FieldBooleanReadonly',
'float': 'openerp.web.page.FieldFloatReadonly',
'integer': 'openerp.web.page.FieldCharReadonly',
'float_time': 'openerp.web.page.FieldCharReadonly',
'binary': 'openerp.web.page.FieldBinaryFileReadonly',
'image': 'openerp.web.page.FieldBinaryImageReaonly'
});
};

View File

@ -6,17 +6,7 @@ openerp.web.views = function(session) {
var QWeb = session.web.qweb,
_t = session.web._t;
/**
* Registry for all the client actions key: tag value: widget
*/
session.web.client_actions = new session.web.Registry();
/**
* Registry for all the main views
*/
session.web.views = new session.web.Registry();
session.web.ActionManager = session.web.OldWidget.extend({
session.web.ActionManager = session.web.Widget.extend({
init: function(parent) {
this._super(parent);
this.inner_action = null;
@ -25,9 +15,6 @@ session.web.ActionManager = session.web.OldWidget.extend({
this.dialog_viewmanager = null;
this.client_widget = null;
},
render: function() {
return '<div id="' + this.element_id + '" style="height: 100%;"></div>';
},
dialog_stop: function () {
if (this.dialog) {
this.dialog_viewmanager.destroy();
@ -76,7 +63,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
res_model: state.model,
res_id: state.id,
type: 'ir.actions.act_window',
views: [[false, 'page'], [false, 'form']]
views: [[false, 'form']]
};
action_loaded = this.do_action(action);
} else if (state.sa) {
@ -217,16 +204,8 @@ session.web.ActionManager = session.web.OldWidget.extend({
}
});
session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.ViewManager# */{
session.web.ViewManager = session.web.Widget.extend({
template: "ViewManager",
/**
* @constructs session.web.ViewManager
* @extends session.web.OldWidget
*
* @param parent
* @param dataset
* @param views
*/
init: function(parent, dataset, views, flags) {
this._super(parent);
this.model = dataset ? dataset.model : undefined;
@ -250,19 +229,13 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
this.registry = session.web.views;
this.views_history = [];
},
render: function() {
return session.web.qweb.render(this.template, {
self: this,
prefix: this.element_id,
views: this.views_src});
},
/**
* @returns {jQuery.Deferred} initial view loading promise
*/
start: function() {
this._super();
var self = this;
this.$element.find('.oe_vm_switch button').click(function() {
this.$element.find('.oe_view_manager_switch a').click(function() {
self.on_mode_switch($(this).data('view-type'));
});
var views_ids = {};
@ -271,7 +244,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
deferred : $.Deferred(),
controller : null,
options : _.extend({
sidebar_id : self.element_id + '_sidebar_' + view.view_type,
$buttons : self.$element.find('.oe_view_manager_buttons'),
$sidebar : self.$element.find('.oe_view_manager_sidebar'),
$pager : self.$element.find('.oe_view_manager_pager'),
action : self.action,
action_views_ids : views_ids
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
@ -279,7 +254,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
this.$element.find('.oe_vm_switch').hide();
this.$element.find('.oe_view_manager_switch').hide();
}
// If no default view defined, switch to the first one in sequence
var default_view = this.flags.default_view || this.views_src[0].view_type;
@ -293,9 +268,9 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
* @returns {jQuery.Deferred} new view loading promise
*/
on_mode_switch: function(view_type, no_store) {
var self = this,
view = this.views[view_type],
view_promise;
var self = this;
var view = this.views[view_type];
var view_promise;
if(!view)
return $.Deferred().reject();
@ -307,13 +282,22 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
if (!view.controller) {
// Lazy loading of views
var controllerclass = this.registry.get_object(view_type);
var controller = new controllerclass(this, this.dataset, view.view_id, view.options);
var options = _.clone(view.options);
if (view_type === "form") {
switch (this.action.target) {
case 'new':
case 'inline':
options.initial_mode = 'edit';
break;
}
}
var controller = new controllerclass(this, this.dataset, view.view_id, options);
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
}
controller.do_switch_view.add_last(this.on_mode_switch);
controller.do_prev_view.add_last(this.on_prev_view);
var container = $("#" + this.element_id + '_view_' + view_type);
var container = this.$element.find(".oe_view_manager_view_" + view_type);
view_promise = controller.appendTo(container);
this.views[view_type].controller = controller;
this.views[view_type].deferred.resolve(view_type);
@ -336,9 +320,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
}
this.$element
.find('.oe_vm_switch button').removeAttr('disabled')
.filter('[data-view-type="' + view_type + '"]')
.attr('disabled', true);
.find('.oe_view_manager_switch a').parent().removeClass('active')
this.$element
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
.parent().addClass('active');
$.when(view_promise).then(function () {
_.each(_.keys(self.views), function(view_name) {
@ -373,10 +358,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
if (options.created && current_view === 'form' && previous_view === 'list') {
// APR special case: "If creation mode from list (and only from a list),
// after saving, go to page view (don't come back in list)"
return this.on_mode_switch('page');
return this.on_mode_switch('form');
} else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') {
// APR special case: "If creation from dashboard, we have no previous view
return this.on_mode_switch('page');
return this.on_mode_switch('form');
}
return this.on_mode_switch(previous_view, true);
},
@ -391,12 +376,10 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
if (this.searchview) {
this.searchview.destroy();
}
this.searchview = new session.web.SearchView(
this, this.dataset,
view_id, search_defaults, this.flags.search_view === false);
this.searchview = new session.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false);
this.searchview.on_search.add(this.do_searchview_search);
return this.searchview.appendTo($("#" + this.element_id + "_search"));
return this.searchview.appendTo(this.$element.find(".oe_view_manager_view_search"));
},
do_searchview_search: function(domains, contexts, groupbys) {
var self = this,
@ -452,7 +435,7 @@ session.web.ViewManager = session.web.OldWidget.extend(/** @lends session.web.V
}
});
session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{
session.web.ViewManagerAction = session.web.ViewManager.extend({
template:"ViewManagerAction",
/**
* @constructs session.web.ViewManagerAction
@ -575,6 +558,9 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
});
}
break;
case 'toggle_layout_outline':
current_view.rendering_engine.toggle_layout_debugging();
break;
case 'fields':
this.dataset.call_and_eval(
'fields_get', [false, {}], null, 1).then(function (fields) {
@ -737,85 +723,85 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
}
});
session.web.Sidebar = session.web.OldWidget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
this.items = {};
this.sections = {};
session.web.Sidebar = session.web.Widget.extend({
init: function(parent) {
this._super(parent);
var view = this.getParent();
var view_manager = view.getParent();
var action = view_manager.action;
this.sections = [
{ 'name' : 'print', 'label' : _t('Print'), },
{ 'name' : 'files', 'label' : _t('Attachement'), },
{ 'name' : 'other', 'label' : _t('More'), }
];
this.items = {
'print' : [],
'files' : [],
'other' : [
{ label: _t("Import"), callback: view.on_sidebar_import },
{ label: _t("Export"), callback: view.on_sidebar_export }
]
}
if (this.session.uid === 1) {
var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") };
this.items.other.push(item);
}
},
start: function() {
this._super(this);
var self = this;
this.$element.html(session.web.qweb.render('Sidebar'));
this.$element.find(".toggle-sidebar").click(function(e) {
self.do_toggle();
this._super(this);
this.redraw();
this.$element.on('click','.oe_dropdown_toggle',function(event) {
$(this).parent().find('ul').toggle();
return false;
});
this.$element.on('click','.oe_dropdown_menu li a', function(event) {
var section = $(this).data('section');
var index = $(this).data('index');
$(this).closest('ul').hide();
console.log('click item',section,index);
var item = self.items[section][index];
if (item.callback) {
item.callback.apply(self, [item]);
}
if (item.action) {
self.on_item_action_clicked(item);
}
return false;
});
},
add_default_sections: function() {
var self = this,
view = this.getParent(),
view_manager = view.getParent(),
action = view_manager.action;
if (this.session.uid === 1) {
this.add_section(_t('Customize'), 'customize');
this.add_items('customize', [{
label: _t("Translate"),
callback: view.on_sidebar_translate,
title: _t("Technical translation")
}]);
}
this.add_section(_t('Other Options'), 'other');
this.add_items('other', [
{
label: _t("Import"),
callback: view.on_sidebar_import
}, {
label: _t("Export"),
callback: view.on_sidebar_export
}
]);
redraw: function() {
var self = this;
self.$element.html(QWeb.render('Sidebar', {widget: self}));
this.$element.find('ul').hide();
},
add_default_sections: function() {
var self = this;
},
add_section: function() {
var self = this;
},
add_toolbar: function(toolbar) {
var self = this;
_.each([['print', _t("Reports")], ['action', _t("Actions")], ['relate', _t("Links")]], function(type) {
var items = toolbar[type[0]];
if (items.length) {
_.each(['print','action','relate'], function(type) {
var items = toolbar[type];
if (items) {
for (var i = 0; i < items.length; i++) {
items[i] = {
label: items[i]['name'],
action: items[i],
classname: 'oe_sidebar_' + type[0]
classname: 'oe_sidebar_' + type
}
}
self.add_section(type[1], type[0]);
self.add_items(type[0], items);
self.add_items(type=='print' ? 'print' : 'other', items);
}
});
},
add_section: function(name, code) {
if(!code) code = _.str.underscored(name);
var $section = this.sections[code];
if(!$section) {
var section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
$section = $(session.web.qweb.render("Sidebar.section", {
section_id: section_id,
name: name,
classname: 'oe_sidebar_' + code
}));
$section.appendTo(this.$element.find('div.sidebar-actions'));
this.sections[code] = $section;
}
return $section;
},
/**
* For each item added to the section:
*
* ``label``
* will be used as the item's name in the sidebar
* will be used as the item's name in the sidebar, can be html
*
* ``action``
* descriptor for the action which will be executed, ``action`` and
@ -837,34 +823,10 @@ session.web.Sidebar = session.web.OldWidget.extend({
* @param {Array<{label, action | callback[, classname][, title]}>} items
*/
add_items: function(section_code, items) {
var self = this,
$section = this.add_section(_.str.titleize(section_code.replace('_', ' ')), section_code),
section_id = $section.attr('id');
var self = this;
if (items) {
for (var i = 0; i < items.length; i++) {
items[i].element_id = _.uniqueId(section_id + '_item_');
this.items[items[i].element_id] = items[i];
}
var $items = $(session.web.qweb.render("Sidebar.section.items", {items: items}));
$items.find('a.oe_sidebar_action_a').click(function() {
var item = self.items[$(this).attr('id')];
if (item.callback) {
item.callback.apply(self, [item]);
}
if (item.action) {
self.on_item_action_clicked(item);
}
return false;
});
var $ul = $section.find('ul');
if(!$ul.length) {
$ul = $('<ul/>').appendTo($section);
}
$items.appendTo($ul);
this.items[section_code].push.apply(this.items[section_code],items);
this.redraw();
}
},
on_item_action_clicked: function(item) {
@ -872,11 +834,7 @@ session.web.Sidebar = session.web.OldWidget.extend({
self.getParent().sidebar_context().then(function (context) {
var ids = self.getParent().get_selected_ids();
if (ids.length == 0) {
//TODO: make prettier warning?
openerp.web.dialog($("<div />").text(_t("You must choose at least one record.")), {
title: _t("Warning"),
modal: true
});
session.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
return false;
}
var additional_context = _.extend({
@ -899,15 +857,6 @@ session.web.Sidebar = session.web.OldWidget.extend({
});
});
},
do_fold: function() {
this.$element.addClass('closed-sidebar').removeClass('open-sidebar');
},
do_unfold: function() {
this.$element.addClass('open-sidebar').removeClass('closed-sidebar');
},
do_toggle: function() {
this.$element.toggleClass('open-sidebar closed-sidebar');
}
});
session.web.TranslateDialog = session.web.Dialog.extend({
@ -1026,7 +975,7 @@ session.web.TranslateDialog = session.web.Dialog.extend({
}
});
session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
session.web.View = session.web.Widget.extend({
template: "EmptyComponent",
// name displayed in view switchers
display_name: '',
@ -1066,11 +1015,13 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
* Called after a successful call to fields_view_get.
* Must return a promise.
*/
on_loaded: function(fields_view_get) {},
on_loaded: function(fields_view_get) {
},
set_default_options: function(options) {
this.options = options || {};
_.defaults(this.options, {
// All possible views options should be defaulted here
$sidebar: null,
sidebar_id: null,
sidebar: true,
action: null,
@ -1229,6 +1180,28 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
}
});
session.web.xml_to_json = function(node) {
switch (node.nodeType) {
case 3:
case 4:
return node.data;
break;
case 1:
var attrs = $(node).getAttributes();
_.each(['domain', 'filter_domain', 'context', 'default_get'], function(key) {
if (attrs[key]) {
try {
attrs[key] = JSON.parse(attrs[key]);
} catch(e) { }
}
});
return {
tag: node.tagName.toLowerCase(),
attrs: attrs,
children: _.map(node.childNodes, session.web.xml_to_json)
}
}
}
session.web.json_node_to_xml = function(node, human_readable, indent) {
// For debugging purpose, this function will convert a json node back to xml
// Maybe useful for xml view editor
@ -1267,6 +1240,23 @@ session.web.json_node_to_xml = function(node, human_readable, indent) {
return r + '/>';
}
}
session.web.xml_to_str = function(node) {
if (window.ActiveXObject) {
return node.xml;
} else {
return (new XMLSerializer()).serializeToString(node);
}
}
/**
* Registry for all the client actions key: tag value: widget
*/
session.web.client_actions = new session.web.Registry();
/**
* Registry for all the main views
*/
session.web.views = new session.web.Registry();
};

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
this.model = dataset.model;
this.fields_view = {};
this.view_id = view_id;
this.view_type = 'calendar';
this.has_been_loaded = $.Deferred();
this.creating_event_id = null;
this.dataset_events = [];
@ -40,7 +41,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
},
start: function() {
this._super();
return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': true}, this.on_loaded);
return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': false}, this.on_loaded);
},
destroy: function() {
scheduler.clearAll();
@ -99,21 +100,14 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
}
this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view}));
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.navigator = new openerp.web_calendar.SidebarNavigator(this.sidebar, this);
this.sidebar.responsible = new openerp.web_calendar.SidebarResponsible(this.sidebar, this);
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
this.sidebar.do_unfold();
this.sidebar.do_fold.add_last(this.refresh_scheduler);
this.sidebar.do_unfold.add_last(this.refresh_scheduler);
this.sidebar.do_toggle.add_last(this.refresh_scheduler);
this.init_scheduler();
if (this.options.sidebar) {
this.sidebar = new openerp.web_calendar.Sidebar(this);
this.has_been_loaded.pipe(this.sidebar.appendTo(this.$element));
}
this.init_scheduler();
this.has_been_loaded.resolve();
return this.has_been_loaded.resolve();
},
init_scheduler: function() {
var self = this;
@ -147,17 +141,6 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
scheduler.attachEvent('onViewChange', this.on_view_changed);
this.refresh_scheduler();
if (this.options.sidebar) {
this.mini_calendar = scheduler.renderCalendar({
container: this.sidebar.navigator.element_id,
navigation: true,
date: scheduler._date,
handler: function(date, calendar) {
scheduler.setCurrentView(date, 'day');
}
});
}
},
on_view_changed: function(mode, date) {
this.$element.removeClass('oe_cal_day oe_cal_week oe_cal_month').addClass('oe_cal_' + mode);
@ -176,7 +159,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
},
refresh_minical: function() {
if (this.options.sidebar) {
scheduler.updateCalendar(this.mini_calendar);
scheduler.updateCalendar(this.sidebar.mini_calendar);
}
},
reload_event: function(id) {
@ -235,7 +218,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
this.refresh_scheduler();
this.refresh_minical();
if (!no_filter_reload && this.options.sidebar) {
this.sidebar.responsible.on_events_loaded(sidebar_items);
this.sidebar.filter.on_events_loaded(sidebar_items);
}
},
convert_event: function(evt) {
@ -344,7 +327,7 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
var index = this.dataset.get_id_index(event_id);
if (index !== null) {
this.dataset.index = index;
this.do_switch_view('page');
this.do_switch_view('form');
} else if (scheduler.getState().mode === 'month') {
var event_obj = scheduler.getEvent(event_id);
if (event_obj._length === 1) {
@ -410,18 +393,9 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
var self = this;
$.when(this.has_been_loaded).then(function() {
self.$element.show();
if (self.sidebar) {
self.sidebar.$element.show();
}
self.do_push_state({});
});
},
do_hide: function () {
this._super();
if (this.sidebar) {
this.sidebar.$element.hide();
}
},
get_selected_ids: function() {
// no way to select a record anyway
return [];
@ -466,19 +440,31 @@ openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({
}
});
openerp.web_calendar.SidebarResponsible = openerp.web.OldWidget.extend({
// TODO: fme: in trunk, rename this class to SidebarFilter
openerp.web_calendar.Sidebar = openerp.web.Widget.extend({
template: 'CalendarView.sidebar',
start: function() {
this._super();
this.mini_calendar = scheduler.renderCalendar({
container: this.$element.find('.oe_calendar_mini')[0],
navigation: true,
date: scheduler._date,
handler: function(date, calendar) {
scheduler.setCurrentView(date, 'day');
}
});
this.filter = new openerp.web_calendar.SidebarFilter(this, this.getParent());
this.filter.appendTo(this.$element.find('.oe_calendar_filter'));
}
});
openerp.web_calendar.SidebarFilter = openerp.web.Widget.extend({
init: function(parent, view) {
var $section = parent.add_section(view.color_string, 'responsible');
this.$div = $('<div></div>');
$section.append(this.$div);
this._super(parent, $section.attr('id'));
this._super(parent);
this.view = view;
this.$element.delegate('input:checkbox', 'change', this.on_filter_click);
},
on_events_loaded: function(filters) {
var selected_filters = this.view.selected_filters.slice(0);
this.$div.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
this.$element.find('div.oe_calendar_responsible input').each(function() {
if (_.indexOf(selected_filters, $(this).val()) > -1) {
$(this).click();
@ -505,15 +491,6 @@ openerp.web_calendar.SidebarResponsible = openerp.web.OldWidget.extend({
}
});
openerp.web_calendar.SidebarNavigator = openerp.web.OldWidget.extend({
init: function(parent, view) {
var $section = parent.add_section(_t('Navigator'), 'navigator');
this._super(parent, $section.attr('id'));
this.view = view;
},
on_events_loaded: function(events) {
}
});
};
// DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))

View File

@ -1,5 +1,6 @@
<template>
<t t-name="CalendarView">
<t t-name="CalendarView">
<div class="oe_calendar">
<div id="openerp_scheduler" class="dhx_cal_container" style="height: 600px;">
<div class="dhx_cal_navline">
<div class="dhx_cal_prev_button">&amp;nbsp;</div>
@ -15,6 +16,15 @@
<div class="dhx_cal_data">
</div>
</div>
</div>
</t>
<t t-name="CalendarView.sidebar">
<div class="oe_calendar_sidebar" style="width: 500px">
<div class="oe_calendar_mini oe_left" style="width: 240px"/>
<div class="oe_calendar_filter oe_right" style="width: 240px">
<h3><t t-esc="widget.getParent().color_string"/></h3>
</div>
</div>
</t>
<t t-name="CalendarView.sidebar.responsible">
<div t-foreach="filters" class="oe_calendar_responsible" t-attf-style="background: #{filters_value.color}">

View File

@ -273,9 +273,9 @@ openerp.web.form.DashBoardLegacy = openerp.web.form.DashBoard.extend({
}
});
openerp.web.form.widgets.add('hpaned', 'openerp.web.form.DashBoardLegacy');
openerp.web.form.widgets.add('vpaned', 'openerp.web.form.DashBoardLegacy');
openerp.web.form.widgets.add('board', 'openerp.web.form.DashBoard');
openerp.web.form.tags.add('hpaned', 'openerp.web.form.DashBoardLegacy');
openerp.web.form.tags.add('vpaned', 'openerp.web.form.DashBoardLegacy');
openerp.web.form.tags.add('board', 'openerp.web.form.DashBoard');
/*
* ConfigOverview

View File

@ -3,6 +3,9 @@
visibility: visible !important;
height: 60px !important;
}
.openerp .oe_kanban_groups {
width: 100%;
}
.openerp .oe_kanban_group_header {
position: relative;
}

View File

@ -23,7 +23,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
records : {}
};
this.groups = [];
this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start();
this.form_dialog = new openerp.web.form.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start();
this.form_dialog.on_form_dialog_saved.add_last(this.do_reload);
this.aggregates = {};
this.group_operators = ['avg', 'max', 'min', 'sum', 'count'];
@ -34,9 +34,17 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
this.search_domain = this.search_context = this.search_group_by = null;
this.currently_dragging = {};
this.limit = options.limit || 80;
this.add_group_mutex = new $.Mutex();
},
on_loaded: function(data) {
this.$element.find('button.oe_kanban_button_new').click(this.do_add_record);
this.$buttons = $(QWeb.render("KanbanView.buttons", {'widget': this}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$element.find('.oe_kanban_buttons').replaceWith(this.$buttons);
}
this.$buttons
.on('click','button.oe_kanban_button_new', this.do_add_record);
this.$groups = this.$element.find('.oe_kanban_groups tr');
this.fields_view = data;
this.fields_keys = _.keys(this.fields_view.fields);
@ -138,29 +146,43 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
});
},
do_process_groups: function(groups) {
this.do_clear_groups();
this.dataset.ids = [];
var self = this,
remaining = groups.length - 1,
groups_array = [];
_.each(groups, function (group, index) {
var dataset = new openerp.web.DataSetSearch(self, self.dataset.model, group.context, group.domain);
dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) {
self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
groups_array[index] = new openerp.web_kanban.KanbanGroup(self, records, group, dataset);
if (!remaining--) {
self.dataset.index = self.dataset.size() ? 0 : null;
self.do_add_groups(groups_array);
}
var self = this;
this.add_group_mutex.exec(function() {
var def = $.Deferred();
self.do_clear_groups();
self.dataset.ids = [];
var remaining = groups.length - 1,
groups_array = [];
_.each(groups, function (group, index) {
var dataset = new openerp.web.DataSetSearch(self, self.dataset.model, group.context, group.domain);
dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) {
self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
groups_array[index] = new openerp.web_kanban.KanbanGroup(self, records, group, dataset);
if (!remaining--) {
self.dataset.index = self.dataset.size() ? 0 : null;
def.pipe(self.do_add_groups(groups_array));
}
}).then(null, function() {
def.reject();
});
});
return def;
});
},
do_process_dataset: function(dataset) {
var self = this;
this.do_clear_groups();
this.dataset.read_slice(this.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) {
var kgroup = new openerp.web_kanban.KanbanGroup(self, records, null, self.dataset);
self.do_add_groups([kgroup]);
this.add_group_mutex.exec(function() {
var def = $.Deferred();
self.do_clear_groups();
self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).then(function(records) {
var kgroup = new openerp.web_kanban.KanbanGroup(self, records, null, self.dataset);
self.do_add_groups([kgroup]).then(function() {
def.resolve();
});
}).then(null, function() {
def.reject();
});
return def;
});
},
do_reload: function() {
@ -174,14 +196,16 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
this.$element.find('.oe_kanban_groups_headers, .oe_kanban_groups_records').empty();
},
do_add_groups: function(groups) {
var self = this;
var self = this,
def = $.Deferred().resolve();
_.each(groups, function(group) {
self.groups[group.undefined_title ? 'unshift' : 'push'](group);
});
_.each(this.groups, function(group) {
group.appendTo(self.$element.find('.oe_kanban_groups_headers'));
def.pipe(group.appendTo(self.$element.find('.oe_kanban_groups_headers')));
});
this.on_groups_started();
return def;
},
on_groups_started: function() {
var self = this;
@ -248,9 +272,18 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
},
do_show: function() {
if (this.$buttons) {
this.$buttons.show();
}
this.do_push_state({});
return this._super();
},
do_hide: function () {
if (this.$buttons) {
this.$buttons.hide();
}
return this._super();
},
open_record: function(id) {
if (this.dataset.select_id(id)) {
this.do_switch_view('form');

View File

@ -1,12 +1,8 @@
<template>
<t t-name="KanbanView">
<div class="oe_kanban_header">
<t t-if="widget.options.action_buttons !== false">
<button type="button" class="oe_button oe_kanban_button_new">Create</button>
</t>
</div>
<div class="oe_kanban_buttons"/>
<div class="oe_kanban_view">
<table style="width:100%;" class="oe_kanban_groups">
<table class="oe_kanban_groups">
<tr class="oe_kanban_groups_headers">
</tr>
<tr class="oe_kanban_groups_records">
@ -14,6 +10,11 @@
</table>
</div>
</t>
<div t-name="KanbanView.buttons" class="oe_kanban_buttons">
<t t-if="widget.options.action_buttons !== false">
<button type="button" class="oe_button oe_kanban_button_new">Create</button>
</t>
</div>
<t t-name="KanbanView.group_header">
<td>
<t t-if="widget.view.group_by">

View File

@ -28,7 +28,7 @@ openerp.web_process = function (openerp) {
initialize_process_view: function() {
var self = this;
this.record_id = false;
if(this.active_view == 'page' || this.active_view == 'form') {
if(this.active_view == 'form') {
this.record_id = this.views[this.active_view].controller.datarecord.id;
}

View File

@ -11,6 +11,8 @@ Contents:
.. toctree::
:maxdepth: 2
search-view
getting-started
production
widgets

311
doc/search-view.rst Normal file
View File

@ -0,0 +1,311 @@
Search View
===========
OpenERP Web 6.2 implements a unified facets-based search view instead
of the previous form-like search view (composed of buttons and
multiple fields). The goal for this change is twofold:
* Avoid the common issue of users confusing the search view with a
form view and trying to create their records through it (or entering
all their data, hitting the ``Create`` button expecting their record
to be created and losing everything).
* Improve the looks and behaviors of the view, and the fit within
OpenERP Web's new design.
The faceted search is implemented through a monkey-patched
`VisualSearch <http://documentcloud.github.com/visualsearch/>`_
[#]_. VisualSearch is based on `Backbone
<http://documentcloud.github.com/backbone/>`_ and makes significant
use of Backbone's models and views. As a result, understanding the
implementation of the OpenERP Web 6.2 search view also requires a
basic understanding of Backbone.
.. note::
This document may mention *fetching* data. This is a shortcut for
"returning a deferred to [whatever is being fetched]". Unless
further noted, the function or method may opt to return nothing by
fetching ``null`` (which can easily be done by returning
``$.when(null)``, which simply wraps the ``null`` in a Deferred).
Interaction between the Search View and VisualSearch
----------------------------------------------------
The core data abstraction in VisualSearch is
:js:class:`VS.model.SearchQuery`, a backbone Collection holding
instances of the :js:class:`VS.model.SearchFacet` backbone Model.
Backbone models can hold any number of informal properties interacted
with through the :js:func:`~Backbone.Model.get` and
:js:func:`~Backbone.Model.set` methods. VisualSearch reserves three
such properties for its behavior, these properties *must* be correctly
set on all search facets created programmatically:
``app``
a reference to the VisualSearch instance using this facet. In the
search view, this instance is available as the
:js:attr:`~openerp.web.SearchView.vs` attribute to the searchview
instance.
``category``
the *name* of the facet, displayed in the first section of a facet
view.
``value``
the *displayed value* of the facet, it is directly printed to the
right of the category.
The search view uses additional keys to store state and data it needs
to associate with facet objects:
``field``
the search field instance which created the facet, used when the
search view needs to serialize the facets.
``json``
the "logical" value of the facet, can be absent if the logical and
"printable" values of the facet are the same (e.g. for a basic text
field).
This value may be a complex javascript object such as an array (the
name stands for json-compatible value, it is not a JSON-encoded
string).
.. note::
in order to simplify getting the logical value of a search facet
model, :js:class:`VS.model.SearchFacet` has been extended with a
:js:func:`~VS.model.SearchFacet.value` method
Extensions and patches to VisualSearch
++++++++++++++++++++++++++++++++++++++
.. js:function:: VS.model.SearchFacet.value()
Bundles the logic of selecting between ``json`` and ``value`` in
order to get the logical value of a facet.
.. js:attribute:: VS.options.callbacks.make_facet
Called by :js:class:`VS.ui.SearchBox` when it needs to create a
new search facet *view*. By default this is not supported by
VisualSearch, and requires monkey-patching
:js:func:`VS.ui.SearchBox.renderFacet`.
This patch should not alter any behavior if
:js:attr:`~VS.options.callbacks.make_facet` is not used.
.. js:attribute:: VS.options.callbacks.make_input
Similar to :js:attr:`~VS.options.callbacks.make_facet`, but called
when the :js:class:`~VS.ui.SearchBox` needs to create a search
input view. It requires monkey-patching
:js:func:`VS.ui.SearchBox.renderSearchInput`.
Finally, :js:func:`VS.ui.SearchBox.searchEvent` is monkey-patched to
get rid of its serialize/load round-tripping of facet data: the
additional attributes needed by the search view don't round-trip (at
all) so VisualSearch must not load any data from its (fairly
simplistic) text-serialization format.
.. note::
a second issue is that — as of `commit 3fca87101d`_ — VisualSearch
correctly serializes facet categories containing spaces but is
unable to load them back in. It also does not handle facets with
*empty* categories correctly.
Loading Defaults
----------------
After loading the view data, the SearchView will call
:js:func:`openerp.web.search.Input.facet_for_defaults` on each of its
inputs with the ``defaults`` mapping of key:values (where each key
corresponds to an input). This method should look into the
``defaults`` mapping and fetch the field's default value as a
:js:class:`~VS.models.SearchFacet` if applicable.
The default implementation is to check if there is a default value for
the current input's name (via
:js:attr:`openerp.web.search.Input.attrs.name`) and if there is to
convert this value to a :js:class:`~VS.models.SearchFacet` by calling
:js:func:`openerp.web.search.Input.facet_for`.
There is no built-in (default) implementation of
:js:func:`openerp.web.search.Input.facet_for`. This method should
fetch the :js:class:`~VS.models.SearchFacet` corresponding to the
"raw" value passed as argument.
Providing auto-completion
-------------------------
An important component of the unified search view is the faceted
autocompletion pane. In order to provide good user and developer
experiences, this pane is pluggable (value-wise): each and every
control of the search view can check for (and provide) categorized
auto-completions for a given value being typed by the user.
This is done by implementing
:js:func:`openerp.web.search.Input.complete`: the method is provided
with a value to complete, and should fetch an ``Array`` of completion
values. These completion values will then be provided to the global
autocompletion list, implemented via `jquery-ui autocomplete
<http://jqueryui.com/demos/autocomplete/>`_.
Because the search view uses a custom renderer for its completion, it
was possible to fix some incompatibilities between the attributes of
completion items and VisualSearch's facet model:
Actual completion items
+++++++++++++++++++++++
These are selectable items, and upon selection are turned into actual
search facet objects. They should have all the properties of a search
facet (as described above) and can have one more optional property:
``label``.
When rendering an item in the list, the renderer will first try to use
the ``label`` property if it exists (``label`` can contain HTML and
will be inserted as-is, so it can bold or emphasize some of its
elements), if it does not the ``value`` property will be used.
.. note:: the ``app`` key should not be specified on completion item,
it will be set automatically when the search view creates
the facet from the item.
Section titles
++++++++++++++
A second kind of completion values is the section titles. Section
titles are similar to completion items but only have a ``category``
property. They will be rendered in a different style and can not be
selected in the auto-completion (they will be skipped).
.. note::
Technically, section title items can have any property they want
*as long as they do not have a value property*. A ``value``
property set to ``false``, ``null`` or ``undefined`` is **not**
equivalent to not having a ``value`` property.
If an input *may* fetch more than one completion item, it *should*
prepend a section title (using its own name) to the completion items.
Converting from facet objects
-----------------------------
Ultimately, the point of the search view is to allow searching. In
OpenERP this is done via :ref:`domains <openerpserver:domains>`. On
the other hand, the OpenERP Web 6.2 search view's state is modelled
after a collection of :js:class:`~VS.model.SearchFacet`, and each
field of a search view may have special requirements when it comes to
the domains it produces [#]_.
So there needs to be some way of mapping
:js:class:`~VS.model.SearchFacet` objects to OpenERP search data.
This is done via an input's
:js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context`. Each takes a
:js:class:`~VS.model.SearchFacet` and returns whatever it's supposed
to generate (a domain or a context, respectively). Either can return
``null`` if the current value does not map to a domain or context, and
can throw an :js:class:`~openerp.web.search.Invalid` exception if the
value is not valid at all for the field.
Converting to facet objects
---------------------------
Changes
-------
.. todo:: merge in changelog instead
The displaying of the search view was significantly altered from
OpenERP Web 6.1 to OpenERP Web 6.2.
As a result, while the external API used to interact with the search
view does not change many internal details — including the interaction
between the search view and its widgets — were significantly altered:
Internal operations
+++++++++++++++++++
* :js:func:`openerp.web.SearchView.do_clear` has been removed
* :js:func:`openerp.web.SearchView.do_toggle_filter` has been removed
Widgets API
+++++++++++
* :js:func:`openerp.web.search.Widget.render` has been removed
* :js:func:`openerp.web.search.Widget.make_id` has been removed
* Search field objects are not openerp widgets anymore, their
``start`` is not generally called
* :js:func:`~openerp.web.search.Input.clear` has been removed since
clearing the search view now simply consists of removing all search
facets from VisualSearch
* :js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context` now take a
:js:class:`~VS.model.SearchFacet` as parameter, from which it's
their job to get whatever value they want
* :js:func:`~openerp.web.search.Input.get_groupby` has been added. It returns
an :js:class:`Array` of context-like constructs. By default, it does not do
anything in :js:class:`~openerp.web.search.Field` and it returns the various
contexts of its enabled filters in
:js:class:`~openerp.web.search.FilterGroup`.
Filters
+++++++
* :js:func:`openerp.web.search.Filter.is_enabled` has been removed
* :js:class:`~openerp.web.search.FilterGroup` instances are still
rendered (and started) in the "advanced search" drawer.
Fields
++++++
* ``get_value`` now takes a :js:class:`~VS.model.SearchFacet` (instead
of taking no argument).
A default implementation is provided as
:js:func:`openerp.web.search.Field.get_value` and simply calls
:js:func:`VS.model.SearchFacet.value`.
* The third argument to
:js:func:`~openerp.web.search.Field.make_domain` is now the
:js:class:`~VS.model.SearchFacet` received by
:js:func:`~openerp.web.search.Field.get_domain`, so child classes
have all the information they need to derive the "right" resulting
domain.
Custom filters
++++++++++++++
Instead of being an intrinsic part of the search view, custom filters
are now a special case of filter groups. They are treated specially
still, but much less so than they used to be.
Many To One
+++++++++++
* Because the autocompletion service is now provided by the search
view itself,
:js:func:`openerp.web.search.ManyToOneField.setup_autocomplete` has
been removed.
.. [#] the library code is untouched, all patching is performed in the
Search view's implementation module. Changes to the
VisualSearch code should only update the library to new
revisions or releases.
.. [#] search view fields may also bundle context data to add to the
search context
.. _commit 3fca87101d:
https://github.com/documentcloud/visualsearch/commit/3fca87101d