Project

General

Profile

Revision 08d86103

ID08d861030acd5cdab3131e6caca5193cd8883b6e
Parent 228aad1c
Child bfe25999

Added by Francois POIROTTE over 14 years ago

Mise à jour de Vigiboard pour utiliser mootools/jxlib à la place de jQuery.
Ajout de l'auto-complétion sur les champs du formulaire de recherche.
Ajout d'échapement dans le RootController pour les paramètres passés dans les liens du menu de détails.

git-svn-id: https://vigilo-dev.si.c-s.fr/svn@1406 b22e2e97-25c9-44ff-b637-2e5ceca36478

View differences:

vigiboard/config/app_cfg.py
76 76
# - idcorrevent : identifiant de l'aggregat (alerte correlee)
77 77
# - host : le nom de l'hote concerne par l'alerte
78 78
# - service : le nom du service concerne par l'alerte
79
# - message : le message transmis par Nagios dans l'alerte
79 80
base_config['vigiboard_links.eventdetails'] = {
80 81
    'nagios': ['Nagios host details', 'http://example1.com/%(idcorrevent)d'],
81 82
    'metrology': ['Metrology details', 'http://example2.com/%(idcorrevent)d'],
82 83
    'security': ['Security details', 'http://example3.com/%(idcorrevent)d'],
83 84
    'servicetype': ['Service Type', 'http://example4.com/%(idcorrevent)d'],
85
    'documentation': ['Documentation', 'http://doc.example.com/?q=%(message)s'],
84 86
}
85 87

  
86 88
# URL des tickets, possibilités:
vigiboard/config/middleware.py
44 44
        'vigilo.themes.public', 'vigiboard'))
45 45
    common_static = StaticURLParser(resource_filename(
46 46
        'vigilo.themes.public', 'common'))
47
    app = Cascade([app_static, common_static, app])
47
    local_static = StaticURLParser(resource_filename(
48
                'vigiboard', 'public'))
49
    app = Cascade([app_static, common_static, local_static, app])
48 50

  
49 51
    return app
50 52

  
vigiboard/controllers/root.py
11 11
from sqlalchemy import not_, and_, asc
12 12
from datetime import datetime
13 13
import math
14
import urllib
14 15

  
15 16
from vigiboard.model import DBSession
16 17
from vigiboard.model import Event, EventHistory, CorrEvent, \
......
25 26

  
26 27
__all__ = ('RootController', )
27 28

  
29
def sql_escape_like(s):
30
    return s.replace('%', '\\%').replace('_', '\\_') \
31
                .replace('*', '%').replace('?', '_')
32

  
28 33
class RootController(VigiboardRootController):
29 34
    """
30 35
    Le controller général de vigiboard
......
93 98
            'tt': ''
94 99
        }
95 100
        # Application des filtres si nécessaire
96
        if host :
101
        if host:
97 102
            search['host'] = host
98
            host = host.replace('%', '\\%').replace('_', '\\_') \
99
                    .replace('*', '%').replace('?', '_')
100
            aggregates.add_filter(Event.hostname.ilike('%%%s%%' % host))
103
            host = sql_escape_like(host)
104
            aggregates.add_filter(Host.name.ilike('%%%s%%' % host))
101 105

  
102
        if service :
106
        if service:
103 107
            search['service'] = service
104
            service = service.replace('%', '\\%').replace('_', '\\_') \
105
                    .replace('*', '%').replace('?', '_')
106
            aggregates.add_filter(Event.servicename.ilike('%%%s%%' % service))
108
            service = sql_escape_like(service)
109
            aggregates.add_filter(ServiceLowLevel.servicename.ilike('%%%s%%' % service))
107 110

  
108
        if output :
111
        if output:
109 112
            search['output'] = output
110
            output = output.replace('%', '\\%').replace('_', '\\_') \
111
                    .replace('*', '%').replace('?', '_')
113
            output = sql_escape_like(output)
112 114
            aggregates.add_filter(Event.message.ilike('%%%s%%' % output))
113 115

  
114
        if trouble_ticket :
116
        if trouble_ticket:
115 117
            search['tt'] = trouble_ticket
116
            trouble_ticket = trouble_ticket.replace('%', '\\%') \
117
                    .replace('_', '\\_').replace('*', '%').replace('?', '_')
118
            trouble_ticket = sql_escape_like(trouble_ticket)
118 119
            aggregates.add_filter(CorrEvent.trouble_ticket.ilike(
119 120
                '%%%s%%' % trouble_ticket))
120 121

  
......
151 152
                   refresh_times=self.refresh_times,
152 153
                )
153 154
      
154
    @validate(validators={'idcorrevent':validators.Int(not_empty=True)},
155
    @validate(validators={'idcorrevent': validators.Int(not_empty=True)},
155 156
            error_handler=process_form_errors)
156 157
    @expose('json')
157 158
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
......
207 208
        for edname, edlink in \
208 209
                config['vigiboard_links.eventdetails'].iteritems():
209 210

  
211
            # Rappel:
212
            # event[0] = priorité de l'alerte corrélée.
213
            # event[1] = alerte brute.
210 214
            eventdetails[edname] = edlink[1] % {
211
                    'idcorrevent': idcorrevent,
212
                    'host': event[1].supitem.host.name,
213
                    'service': event[1].supitem.servicename
214
                    }
215
                'idcorrevent': idcorrevent,
216
                'host': urllib.quote(event[1].supitem.host.name),
217
                'service': urllib.quote(event[1].supitem.servicename),
218
                'message': urllib.quote(event[1].message),
219
            }
215 220

  
216 221
        return dict(
217 222
                current_state = StateName.value_to_statename(
......
226 231
                eventdetails = eventdetails,
227 232
            )
228 233

  
229
    @validate(validators={'idcorrevent':validators.Int(not_empty=True)},
234
    @validate(validators={'idcorrevent': validators.Int(not_empty=True)},
230 235
            error_handler=process_form_errors)
231 236
    @expose('vigiboard.html')
232 237
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
......
273 278
                   refresh_times=self.refresh_times,
274 279
                )
275 280

  
276
    @validate(validators={'host':validators.NotEmpty(),
277
        'service':validators.NotEmpty()}, error_handler=process_form_errors)
281
    @validate(validators={'host': validators.NotEmpty(),
282
        'service': validators.NotEmpty()}, error_handler=process_form_errors)
278 283
    @expose('vigiboard.html')
279 284
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
280 285
    def host_service(self, host, service):
......
334 339
    @validate(validators={
335 340
        "id":validators.Regex(r'^[^,]+(,[^,]*)*,?$'),
336 341
#        "trouble_ticket":validators.Regex(r'^[0-9]*$'),
337
        "status":validators.OneOf(['NoChange', 'None', 'Acknowledged',
338
                'AAClosed'])
339
        }, error_handler=process_form_errors)
342
        "status": validators.OneOf([
343
            'NoChange',
344
            'None',
345
            'Acknowledged',
346
            'AAClosed'
347
        ])}, error_handler=process_form_errors)
340 348
    @require(Any(not_anonymous(), msg=l_("You need to be authenticated")))
341 349
    def update(self,**krgv):
342 350
        
......
413 421
        redirect(request.environ.get('HTTP_REFERER', url('/')))
414 422

  
415 423

  
416
    @validate(validators={"plugin_name":validators.OneOf(
424
    @validate(validators={"plugin_name": validators.OneOf(
417 425
        [i for [i, j] in config.get('vigiboard_plugins', [])])},
418 426
                error_handler = process_form_errors)
419 427
    @expose('json')
......
446 454
        session.save()
447 455
        return dict(ret= 'ok')
448 456

  
449
    @validate(validators= {"refresh": validators.Int()},
450
            error_handler = process_form_errors)
457
    @validate(validators={"refresh": validators.Int()},
458
            error_handler=process_form_errors)
451 459
    @expose('json')
452 460
    def set_refresh(self, refresh):
453 461
        """
......
457 465
        session.save()
458 466
        return dict(ret= 'ok')
459 467

  
468
    @expose('json')
469
    def autocomplete_host(self, value):
470
        value = sql_escape_like(value)
471
        hostnames = DBSession.query(
472
                        Host.name.distinct()).filter(
473
                        Host.name.ilike('%' + value + '%')).all()
474
        return dict(results=[h[0] for h in hostnames])
475

  
476
    @expose('json')
477
    def autocomplete_service(self, value):
478
        value = sql_escape_like(value)
479
        services = DBSession.query(
480
                        ServiceLowLevel.servicename.distinct()).filter(
481
                        ServiceLowLevel.servicename.ilike('%' + value + '%')).all()
482
        return dict(results=[s[0] for s in services])
483

  
vigiboard/controllers/vigiboard_plugin/shn.py
8 8
        VigiboardRequestPlugin
9 9
from vigiboard.model import DBSession, CorrEvent
10 10
from pylons.i18n import gettext as _
11
from tg import tmpl_context, url
12
from tw.jquery.ui_dialog import JQueryUIDialog
11
from tg import url
13 12

  
14 13
class PluginSHN(VigiboardRequestPlugin):
15 14

  
......
35 34
        }
36 35
        # XXX Il faudrait échapper l'URL contenue dans baseurl
37 36
        # pour éviter des attaques de type XSS.
38
        res = ('<a href="javascript:vigiboard_shndialog(' + \
39
                '\'%(baseurl)s\',\'%(idcorrevent)d\')" ' + \
40
                'class="SHNLien">%(impacted_hls)d</a>') % dico
37
        res = ('<a href="javascript:vigiboard_hls_dialog(this,' + \
38
                '\'%(baseurl)s\',%(idcorrevent)d)" ' + \
39
                'class="hls_link">%(impacted_hls)d</a>') % dico
41 40
        return res
42 41

  
43 42
    def context(self, context):
44 43
        """Fonction de context"""
45

  
46
        # On ajoute 10 espaces insécables pour éviter un bug de JQueryUIDialog:
47
        # le calcul de la taille de la boîte de dialogue ne tient pas compte
48
        # de l'espace occupé par la croix permettant de fermer le dialogue.
49
        # Du coup, elle se retrouve superposée au titre de la boîte.
50
        tmpl_context.shndialog = JQueryUIDialog(id='SHNDialog',
51
                autoOpen=False, title='%s%s' % (_(u'High-Level Services'),
52
                '&#160;' * 10))
53
        context.append([tmpl_context.shndialog, self.object_name])
44
        context.append([None, self.object_name])
54 45

  
55 46
    def controller(self, *argv, **krgv):
56 47
        """Ajout de fonctionnalités au contrôleur"""
57 48
        idcorrevent = krgv['idcorrevent']
58 49
        correvent = DBSession.query(CorrEvent) \
59 50
                .filter(CorrEvent.idcorrevent == idcorrevent).one()
60
        shns = correvent.high_level_services
51
        services = correvent.high_level_services
61 52

  
62
        return dict(shns=[shn.servicename for shn in shns]) 
53
        return dict(services=[service.servicename for service in services])
63 54

  
vigiboard/controllers/vigiboardrequest.py
7 7
                            StateName
8 8
from vigilo.models.secondary_tables import HOST_GROUP_TABLE, \
9 9
                                            SERVICE_GROUP_TABLE
10
from tg import tmpl_context, url, config
10
from tg import url, config, tmpl_context
11 11
from vigiboard.model import DBSession
12 12
from sqlalchemy import not_, and_, asc, desc, sql
13 13
from sqlalchemy.orm import aliased
14
from tw.jquery.ui_dialog import JQueryUIDialog
15
from vigiboard.widgets.edit_event import EditEventForm , SearchForm
14
from vigiboard.widgets.edit_event import EditEventForm
15
from vigiboard.widgets.search_form import SearchForm
16 16
from vigiboard.controllers.vigiboard_plugin import VigiboardRequestPlugin
17 17
from pylons.i18n import ugettext as _
18 18

  
......
441 441
        # Dialogue d'édition
442 442
        tmpl_context.edit_event_form = EditEventForm('edit_event_form',
443 443
                action=url('/update'))
444
        tmpl_context.edit_eventdialog = JQueryUIDialog(id='Edit_EventsDialog',
445
                autoOpen=False, title=_('Edit Event'))
446 444
    
447 445
        # Dialogue de recherche
448
        tmpl_context.search_form = SearchForm('search_form',
449
                action=url('/'))
450
        tmpl_context.searchdialog = JQueryUIDialog(id='SearchDialog',
451
                autoOpen=False, title=_('Search Event'))
446
        tmpl_context.search_form = SearchForm('search_form', action=url('/'))
452 447
        
453 448
        # Dialogue de détail d'un événement
454
        tmpl_context.historydialog = JQueryUIDialog(id='HistoryDialog',
455
                autoOpen=False, title=_('History'))
456 449

  
457 450
        # Exécution des contexts des plugins
458 451
        for j in self.plugin:
vigiboard/public/css/Autocompleter/Autocompleter.css
1
ul.autocompleter-choices
2
{
3
	position:				absolute;
4
	margin:					0;
5
	padding:				0;
6
	list-style:				none;
7
	border:					1px solid #7c7c7c;
8
	border-left-color:		#c3c3c3;
9
	border-right-color:		#c3c3c3;
10
	border-bottom-color:	#ddd;
11
	background-color:		#fff;
12
	text-align:				left;
13
	font-family:			Verdana, Geneva, Arial, Helvetica, sans-serif;
14
	z-index:				50;
15
	background-color:		#fff;
16
}
17

  
18
ul.autocompleter-choices li
19
{
20
	position:				relative;
21
	margin:					-2px 0 0 0;
22
	padding:				0.2em 1.5em 0.2em 1em;
23
	display:				block;
24
	float:					none !important; 
25
	cursor:					pointer;
26
	font-weight:			normal;
27
	white-space:			nowrap;
28
	font-size:				1em;
29
	line-height:			1.5em;
30
}
31

  
32
ul.autocompleter-choices li.autocompleter-selected
33
{
34
	background-color:		#444;
35
	color:					#fff;
36
}
37

  
38
ul.autocompleter-choices span.autocompleter-queried
39
{
40
	display:				inline;
41
	float:					none;
42
	font-weight:			bold;
43
	margin:					0;
44
	padding:				0;
45
}
46

  
47
ul.autocompleter-choices li.autocompleter-selected span.autocompleter-queried
48
{
49
	color:					#9FCFFF;
50
}
vigiboard/public/js/Autocompleter/Autocompleter.Local.js
1
/**
2
 * Autocompleter.Local
3
 *
4
 * http://digitarald.de/project/autocompleter/
5
 *
6
 * @version		1.1.2
7
 *
8
 * @license		MIT-style license
9
 * @author		Harald Kirschner <mail [at] digitarald.de>
10
 * @copyright	Author
11
 */
12

  
13
Autocompleter.Local = new Class({
14

  
15
	Extends: Autocompleter,
16

  
17
	options: {
18
		minLength: 0,
19
		delay: 200
20
	},
21

  
22
	initialize: function(element, tokens, options) {
23
		this.parent(element, options);
24
		this.tokens = tokens;
25
	},
26

  
27
	query: function() {
28
		this.update(this.filter());
29
	}
30

  
31
});
vigiboard/public/js/Autocompleter/Autocompleter.Request.js
1
/**
2
 * Autocompleter.Request
3
 *
4
 * http://digitarald.de/project/autocompleter/
5
 *
6
 * @version		1.1.2
7
 *
8
 * @license		MIT-style license
9
 * @author		Harald Kirschner <mail [at] digitarald.de>
10
 * @copyright	Author
11
 */
12

  
13
Autocompleter.Request = new Class({
14

  
15
	Extends: Autocompleter,
16

  
17
	options: {/*
18
		indicator: null,
19
		indicatorClass: null,
20
		onRequest: $empty,
21
		onComplete: $empty,*/
22
		postData: {},
23
		ajaxOptions: {},
24
		postVar: 'value'
25

  
26
	},
27

  
28
	query: function(){
29
		var data = $unlink(this.options.postData) || {};
30
		data[this.options.postVar] = this.queryValue;
31
		var indicator = $(this.options.indicator);
32
		if (indicator) indicator.setStyle('display', '');
33
		var cls = this.options.indicatorClass;
34
		if (cls) this.element.addClass(cls);
35
		this.fireEvent('onRequest', [this.element, this.request, data, this.queryValue]);
36
		this.request.send({'data': data});
37
	},
38

  
39
	/**
40
	 * queryResponse - abstract
41
	 *
42
	 * Inherated classes have to extend this function and use this.parent()
43
	 */
44
	queryResponse: function() {
45
		var indicator = $(this.options.indicator);
46
		if (indicator) indicator.setStyle('display', 'none');
47
		var cls = this.options.indicatorClass;
48
		if (cls) this.element.removeClass(cls);
49
		return this.fireEvent('onComplete', [this.element, this.request]);
50
	}
51

  
52
});
53

  
54
Autocompleter.Request.JSON = new Class({
55

  
56
	Extends: Autocompleter.Request,
57

  
58
	initialize: function(el, url, options) {
59
		this.parent(el, options);
60
		this.request = new Request.JSON($merge({
61
			'url': url,
62
			'link': 'cancel'
63
		}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
64
	},
65

  
66
	queryResponse: function(response) {
67
		this.parent();
68
		this.update(response);
69
	}
70

  
71
});
72

  
73
Autocompleter.Request.HTML = new Class({
74

  
75
	Extends: Autocompleter.Request,
76

  
77
	initialize: function(el, url, options) {
78
		this.parent(el, options);
79
		this.request = new Request.HTML($merge({
80
			'url': url,
81
			'link': 'cancel',
82
			'update': this.choices
83
		}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
84
	},
85

  
86
	queryResponse: function(tree, elements) {
87
		this.parent();
88
		if (!elements || !elements.length) {
89
			this.hideChoices();
90
		} else {
91
			this.choices.getChildren(this.options.choicesMatch).each(this.options.injectChoice || function(choice) {
92
				var value = choice.innerHTML;
93
				choice.inputValue = value;
94
				this.addChoiceEvents(choice.set('html', this.markQueryValue(value)));
95
			}, this);
96
			this.showChoices();
97
		}
98

  
99
	}
100

  
101
});
102

  
103
/* compatibility */
104

  
105
Autocompleter.Ajax = {
106
	Base: Autocompleter.Request,
107
	Json: Autocompleter.Request.JSON,
108
	Xhtml: Autocompleter.Request.HTML
109
};
vigiboard/public/js/Autocompleter/Autocompleter.js
1
/**
2
 * Autocompleter
3
 *
4
 * http://digitarald.de/project/autocompleter/
5
 *
6
 * @version		1.1.2
7
 *
8
 * @license		MIT-style license
9
 * @author		Harald Kirschner <mail [at] digitarald.de>
10
 * @copyright	Author
11
 */
12

  
13
var Autocompleter = new Class({
14

  
15
	Implements: [Options, Events],
16

  
17
	options: {/*
18
		onOver: $empty,
19
		onSelect: $empty,
20
		onSelection: $empty,
21
		onShow: $empty,
22
		onHide: $empty,
23
		onBlur: $empty,
24
		onFocus: $empty,*/
25
		minLength: 1,
26
		markQuery: true,
27
		width: 'inherit',
28
		maxChoices: 10,
29
		injectChoice: null,
30
		customChoices: null,
31
		emptyChoices: null,
32
		visibleChoices: true,
33
		className: 'autocompleter-choices',
34
		zIndex: 42,
35
		delay: 400,
36
		observerOptions: {},
37
		fxOptions: {},
38

  
39
		autoSubmit: false,
40
		overflow: false,
41
		overflowMargin: 25,
42
		selectFirst: false,
43
		filter: null,
44
		filterCase: false,
45
		filterSubset: false,
46
		forceSelect: false,
47
		selectMode: true,
48
		choicesMatch: null,
49

  
50
		multiple: false,
51
		separator: ', ',
52
		separatorSplit: /\s*[,;]\s*/,
53
		autoTrim: false,
54
		allowDupes: false,
55

  
56
		cache: true,
57
		relative: false
58
	},
59

  
60
	initialize: function(element, options) {
61
		this.element = $(element);
62
		this.setOptions(options);
63
		this.build();
64
		this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({
65
			'delay': this.options.delay
66
		}, this.options.observerOptions));
67
		this.queryValue = null;
68
		if (this.options.filter) this.filter = this.options.filter.bind(this);
69
		var mode = this.options.selectMode;
70
		this.typeAhead = (mode == 'type-ahead');
71
		this.selectMode = (mode === true) ? 'selection' : mode;
72
		this.cached = [];
73
	},
74

  
75
	/**
76
	 * build - Initialize DOM
77
	 *
78
	 * Builds the html structure for choices and appends the events to the element.
79
	 * Override this function to modify the html generation.
80
	 */
81
	build: function() {
82
		if ($(this.options.customChoices)) {
83
			this.choices = this.options.customChoices;
84
		} else {
85
			this.choices = new Element('ul', {
86
				'class': this.options.className,
87
				'styles': {
88
					'zIndex': this.options.zIndex
89
				}
90
			}).inject(document.body);
91
			this.relative = false;
92
			if (this.options.relative) {
93
				this.choices.inject(this.element, 'after');
94
				this.relative = this.element.getOffsetParent();
95
			}
96
			this.fix = new OverlayFix(this.choices);
97
		}
98
		if (!this.options.separator.test(this.options.separatorSplit)) {
99
			this.options.separatorSplit = this.options.separator;
100
		}
101
		this.fx = (!this.options.fxOptions) ? null : new Fx.Tween(this.choices, $merge({
102
			'property': 'opacity',
103
			'link': 'cancel',
104
			'duration': 200
105
		}, this.options.fxOptions)).addEvent('onStart', Chain.prototype.clearChain).set(0);
106
		this.element.setProperty('autocomplete', 'off')
107
			.addEvent((Browser.Engine.trident || Browser.Engine.webkit) ? 'keydown' : 'keypress', this.onCommand.bind(this))
108
			.addEvent('click', this.onCommand.bind(this, [false]))
109
			.addEvent('focus', this.toggleFocus.create({bind: this, arguments: true, delay: 100}))
110
			.addEvent('blur', this.toggleFocus.create({bind: this, arguments: false, delay: 100}));
111
	},
112

  
113
	destroy: function() {
114
		if (this.fix) this.fix.destroy();
115
		this.choices = this.selected = this.choices.destroy();
116
	},
117

  
118
	toggleFocus: function(state) {
119
		this.focussed = state;
120
		if (!state) this.hideChoices(true);
121
		this.fireEvent((state) ? 'onFocus' : 'onBlur', [this.element]);
122
	},
123

  
124
	onCommand: function(e) {
125
		if (!e && this.focussed) return this.prefetch();
126
		if (e && e.key && !e.shift) {
127
			switch (e.key) {
128
				case 'enter':
129
					if (this.element.value != this.opted) return true;
130
					if (this.selected && this.visible) {
131
						this.choiceSelect(this.selected);
132
						return !!(this.options.autoSubmit);
133
					}
134
					break;
135
				case 'up': case 'down':
136
					if (!this.prefetch() && this.queryValue !== null) {
137
						var up = (e.key == 'up');
138
						this.choiceOver((this.selected || this.choices)[
139
							(this.selected) ? ((up) ? 'getPrevious' : 'getNext') : ((up) ? 'getLast' : 'getFirst')
140
						](this.options.choicesMatch), true);
141
					}
142
					return false;
143
				case 'esc': case 'tab':
144
					this.hideChoices(true);
145
					break;
146
			}
147
		}
148
		return true;
149
	},
150

  
151
	setSelection: function(finish) {
152
		var input = this.selected.inputValue, value = input;
153
		var start = this.queryValue.length, end = input.length;
154
		if (input.substr(0, start).toLowerCase() != this.queryValue.toLowerCase()) start = 0;
155
		if (this.options.multiple) {
156
			var split = this.options.separatorSplit;
157
			value = this.element.value;
158
			start += this.queryIndex;
159
			end += this.queryIndex;
160
			var old = value.substr(this.queryIndex).split(split, 1)[0];
161
			value = value.substr(0, this.queryIndex) + input + value.substr(this.queryIndex + old.length);
162
			if (finish) {
163
				var tokens = value.split(this.options.separatorSplit).filter(function(entry) {
164
					return this.test(entry);
165
				}, /[^\s,]+/);
166
				if (!this.options.allowDupes) tokens = [].combine(tokens);
167
				var sep = this.options.separator;
168
				value = tokens.join(sep) + sep;
169
				end = value.length;
170
			}
171
		}
172
		this.observer.setValue(value);
173
		this.opted = value;
174
		if (finish || this.selectMode == 'pick') start = end;
175
		this.element.selectRange(start, end);
176
		this.fireEvent('onSelection', [this.element, this.selected, value, input]);
177
	},
178

  
179
	showChoices: function() {
180
		var match = this.options.choicesMatch, first = this.choices.getFirst(match);
181
		this.selected = this.selectedValue = null;
182
		if (this.fix) {
183
			var pos = this.element.getCoordinates(this.relative), width = this.options.width || 'auto';
184
			this.choices.setStyles({
185
				'left': pos.left,
186
				'top': pos.bottom,
187
				'width': (width === true || width == 'inherit') ? pos.width : width
188
			});
189
		}
190
		if (!first) return;
191
		if (!this.visible) {
192
			this.visible = true;
193
			this.choices.setStyle('display', '');
194
			if (this.fx) this.fx.start(1);
195
			this.fireEvent('onShow', [this.element, this.choices]);
196
		}
197
		if (this.options.selectFirst || this.typeAhead || first.inputValue == this.queryValue) this.choiceOver(first, this.typeAhead);
198
		var items = this.choices.getChildren(match), max = this.options.maxChoices;
199
		var styles = {'overflowY': 'hidden', 'height': ''};
200
		this.overflown = false;
201
		if (items.length > max) {
202
			var item = items[max - 1];
203
			styles.overflowY = 'scroll';
204
			styles.height = item.getCoordinates(this.choices).bottom;
205
			this.overflown = true;
206
		};
207
		this.choices.setStyles(styles);
208
		this.fix.show();
209
		if (this.options.visibleChoices) {
210
			var scroll = document.getScroll(),
211
			size = document.getSize(),
212
			coords = this.choices.getCoordinates();
213
			if (coords.right > scroll.x + size.x) scroll.x = coords.right - size.x;
214
			if (coords.bottom > scroll.y + size.y) scroll.y = coords.bottom - size.y;
215
			window.scrollTo(Math.min(scroll.x, coords.left), Math.min(scroll.y, coords.top));
216
		}
217
	},
218

  
219
	hideChoices: function(clear) {
220
		if (clear) {
221
			var value = this.element.value;
222
			if (this.options.forceSelect) value = this.opted;
223
			if (this.options.autoTrim) {
224
				value = value.split(this.options.separatorSplit).filter($arguments(0)).join(this.options.separator);
225
			}
226
			this.observer.setValue(value);
227
		}
228
		if (!this.visible) return;
229
		this.visible = false;
230
		if (this.selected) this.selected.removeClass('autocompleter-selected');
231
		this.observer.clear();
232
		var hide = function(){
233
			this.choices.setStyle('display', 'none');
234
			this.fix.hide();
235
		}.bind(this);
236
		if (this.fx) this.fx.start(0).chain(hide);
237
		else hide();
238
		this.fireEvent('onHide', [this.element, this.choices]);
239
	},
240

  
241
	prefetch: function() {
242
		var value = this.element.value, query = value;
243
		if (this.options.multiple) {
244
			var split = this.options.separatorSplit;
245
			var values = value.split(split);
246
			var index = this.element.getSelectedRange().start;
247
			var toIndex = value.substr(0, index).split(split);
248
			var last = toIndex.length - 1;
249
			index -= toIndex[last].length;
250
			query = values[last];
251
		}
252
		if (query.length < this.options.minLength) {
253
			this.hideChoices();
254
		} else {
255
			if (query === this.queryValue || (this.visible && query == this.selectedValue)) {
256
				if (this.visible) return false;
257
				this.showChoices();
258
			} else {
259
				this.queryValue = query;
260
				this.queryIndex = index;
261
				if (!this.fetchCached()) this.query();
262
			}
263
		}
264
		return true;
265
	},
266

  
267
	fetchCached: function() {
268
		return false;
269
		if (!this.options.cache
270
			|| !this.cached
271
			|| !this.cached.length
272
			|| this.cached.length >= this.options.maxChoices
273
			|| this.queryValue) return false;
274
		this.update(this.filter(this.cached));
275
		return true;
276
	},
277

  
278
	update: function(tokens) {
279
		this.choices.empty();
280
		this.cached = tokens;
281
		var type = tokens && $type(tokens);
282
		if (!type || (type == 'array' && !tokens.length) || (type == 'hash' && !tokens.getLength())) {
283
			(this.options.emptyChoices || this.hideChoices).call(this);
284
		} else {
285
			if (this.options.maxChoices < tokens.length && !this.options.overflow) tokens.length = this.options.maxChoices;
286
			tokens.each(this.options.injectChoice || function(token){
287
				var choice = new Element('li', {'html': this.markQueryValue(token)});
288
				choice.inputValue = token;
289
				this.addChoiceEvents(choice).inject(this.choices);
290
			}, this);
291
			this.showChoices();
292
		}
293
	},
294

  
295
	choiceOver: function(choice, selection) {
296
		if (!choice || choice == this.selected) return;
297
		if (this.selected) this.selected.removeClass('autocompleter-selected');
298
		this.selected = choice.addClass('autocompleter-selected');
299
		this.fireEvent('onSelect', [this.element, this.selected, selection]);
300
		if (!this.selectMode) this.opted = this.element.value;
301
		if (!selection) return;
302
		this.selectedValue = this.selected.inputValue;
303
		if (this.overflown) {
304
			var coords = this.selected.getCoordinates(this.choices), margin = this.options.overflowMargin,
305
				top = this.choices.scrollTop, height = this.choices.offsetHeight, bottom = top + height;
306
			if (coords.top - margin < top && top) this.choices.scrollTop = Math.max(coords.top - margin, 0);
307
			else if (coords.bottom + margin > bottom) this.choices.scrollTop = Math.min(coords.bottom - height + margin, bottom);
308
		}
309
		if (this.selectMode) this.setSelection();
310
	},
311

  
312
	choiceSelect: function(choice) {
313
		if (choice) this.choiceOver(choice);
314
		this.setSelection(true);
315
		this.queryValue = false;
316
		this.hideChoices();
317
	},
318

  
319
	filter: function(tokens) {
320
		return (tokens || this.tokens).filter(function(token) {
321
			return this.test(token);
322
		}, new RegExp(((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp(), (this.options.filterCase) ? '' : 'i'));
323
	},
324

  
325
	/**
326
	 * markQueryValue
327
	 *
328
	 * Marks the queried word in the given string with <span class="autocompleter-queried">*</span>
329
	 * Call this i.e. from your custom parseChoices, same for addChoiceEvents
330
	 *
331
	 * @param		{String} Text
332
	 * @return		{String} Text
333
	 */
334
	markQueryValue: function(str) {
335
		return (!this.options.markQuery || !this.queryValue) ? str
336
			: str.replace(new RegExp('(' + ((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp() + ')', (this.options.filterCase) ? '' : 'i'), '<span class="autocompleter-queried">$1</span>');
337
	},
338

  
339
	/**
340
	 * addChoiceEvents
341
	 *
342
	 * Appends the needed event handlers for a choice-entry to the given element.
343
	 *
344
	 * @param		{Element} Choice entry
345
	 * @return		{Element} Choice entry
346
	 */
347
	addChoiceEvents: function(el) {
348
		return el.addEvents({
349
			'mouseover': this.choiceOver.bind(this, [el]),
350
			'click': this.choiceSelect.bind(this, [el])
351
		});
352
	}
353
});
354

  
355
var OverlayFix = new Class({
356

  
357
	initialize: function(el) {
358
		if (Browser.Engine.trident) {
359
			this.element = $(el);
360
			this.relative = this.element.getOffsetParent();
361
			this.fix = new Element('iframe', {
362
				'frameborder': '0',
363
				'scrolling': 'no',
364
				'src': 'javascript:false;',
365
				'styles': {
366
					'position': 'absolute',
367
					'border': 'none',
368
					'display': 'none',
369
					'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'
370
				}
371
			}).inject(this.element, 'after');
372
		}
373
	},
374

  
375
	show: function() {
376
		if (this.fix) {
377
			var coords = this.element.getCoordinates(this.relative);
378
			delete coords.right;
379
			delete coords.bottom;
380
			this.fix.setStyles($extend(coords, {
381
				'display': '',
382
				'zIndex': (this.element.getStyle('zIndex') || 1) - 1
383
			}));
384
		}
385
		return this;
386
	},
387

  
388
	hide: function() {
389
		if (this.fix) this.fix.setStyle('display', 'none');
390
		return this;
391
	},
392

  
393
	destroy: function() {
394
		if (this.fix) this.fix = this.fix.destroy();
395
	}
396

  
397
});
398

  
399
Element.implement({
400

  
401
	getSelectedRange: function() {
402
		if (!Browser.Engine.trident) return {start: this.selectionStart, end: this.selectionEnd};
403
		var pos = {start: 0, end: 0};
404
		var range = this.getDocument().selection.createRange();
405
		if (!range || range.parentElement() != this) return pos;
406
		var dup = range.duplicate();
407
		if (this.type == 'text') {
408
			pos.start = 0 - dup.moveStart('character', -100000);
409
			pos.end = pos.start + range.text.length;
410
		} else {
411
			var value = this.value;
412
			var offset = value.length - value.match(/[\n\r]*$/)[0].length;
413
			dup.moveToElementText(this);
414
			dup.setEndPoint('StartToEnd', range);
415
			pos.end = offset - dup.text.length;
416
			dup.setEndPoint('StartToStart', range);
417
			pos.start = offset - dup.text.length;
418
		}
419
		return pos;
420
	},
421

  
422
	selectRange: function(start, end) {
423
		if (Browser.Engine.trident) {
424
			var diff = this.value.substr(start, end - start).replace(/\r/g, '').length;
425
			start = this.value.substr(0, start).replace(/\r/g, '').length;
426
			var range = this.createTextRange();
427
			range.collapse(true);
428
			range.moveEnd('character', start + diff);
429
			range.moveStart('character', start);
430
			range.select();
431
		} else {
432
			this.focus();
433
			this.setSelectionRange(start, end);
434
		}
435
		return this;
436
	}
437

  
438
});
439

  
440
/* compatibility */
441

  
442
Autocompleter.Base = Autocompleter;
vigiboard/public/js/Autocompleter/Observer.js
1
/**
2
 * Observer - Observe formelements for changes
3
 *
4
 * - Additional code from clientside.cnet.com
5
 *
6
 * @version		1.1
7
 *
8
 * @license		MIT-style license
9
 * @author		Harald Kirschner <mail [at] digitarald.de>
10
 * @copyright	Author
11
 */
12
var Observer = new Class({
13

  
14
	Implements: [Options, Events],
15

  
16
	options: {
17
		periodical: false,
18
		delay: 1000
19
	},
20

  
21
	initialize: function(el, onFired, options){
22
		this.element = $(el) || $$(el);
23
		this.addEvent('onFired', onFired);
24
		this.setOptions(options);
25
		this.bound = this.changed.bind(this);
26
		this.resume();
27
	},
28

  
29
	changed: function() {
30
		var value = this.element.get('value');
31
		if ($equals(this.value, value)) return;
32
		this.clear();
33
		this.value = value;
34
		this.timeout = this.onFired.delay(this.options.delay, this);
35
	},
36

  
37
	setValue: function(value) {
38
		this.value = value;
39
		this.element.set('value', value);
40
		return this.clear();
41
	},
42

  
43
	onFired: function() {
44
		this.fireEvent('onFired', [this.value, this.element]);
45
	},
46

  
47
	clear: function() {
48
		$clear(this.timeout || null);
49
		return this;
50
	},
51

  
52
	pause: function(){
53
		if (this.timer) $clear(this.timer);
54
		else this.element.removeEvent('keyup', this.bound);
55
		return this.clear();
56
	},
57

  
58
	resume: function(){
59
		this.value = this.element.get('value');
60
		if (this.options.periodical) this.timer = this.changed.periodical(this.options.periodical, this);
61
		else this.element.addEvent('keyup', this.bound);
62
		return this;
63
	}
64

  
65
});
66

  
67
var $equals = function(obj1, obj2) {
68
	return (obj1 == obj2 || JSON.encode(obj1) == JSON.encode(obj2));
69
};
vigiboard/public/js/Autocompleter/vigilo.js
1
Autocompleter.Request.VigiloJSON = new Class({
2

  
3
    Extends: Autocompleter.Request,
4

  
5
    options: {
6
        resVar: 'results',
7
    },
8

  
9
    initialize: function(el, url, options) {
10
        this.parent(el, options);
11
        this.request = new Request.JSON($merge({
12
            'url': url,
13
            'link': 'cancel',
14
        }, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
15
    },
16

  
17
    queryResponse: function(response) {
18
        this.parent();
19
        this.update(response[this.options.resVar]);
20
    }
21

  
22
});
23

  
vigiboard/public/js/jxlib.js
1
/******************************************************************************
2
 * MooTools 1.2.2
3
 * Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/).
4
 * MooTools is distributed under an MIT-style license.
5
 ******************************************************************************
6
 * reset.css - Copyright (c) 2006, Yahoo! Inc. All rights reserved.
7
 * Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt
8
 ******************************************************************************
9
 * Jx UI Library, 2.0.1
10
 * Copyright (c) 2006-2008, DM Solutions Group Inc. All rights reserved.
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included
20
 * in all copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 *****************************************************************************/
30
// $Id: common.js 423 2009-05-12 12:37:56Z pagameba $
31
/**
32
 * Class: Jx
33
 * Jx is a global singleton object that contains the entire Jx library
34
 * within it.  All Jx functions, attributes and classes are accessed
35
 * through the global Jx object.  Jx should not create any other
36
 * global variables, if you discover that it does then please report
37
 * it as a bug
38
 *
39
 * License: 
40
 * Copyright (c) 2008, DM Solutions Group Inc.
41
 * 
42
 * This file is licensed under an MIT style license
43
 */
44
 
45
/* firebug console supressor for IE/Safari/Opera */
46
window.addEvent('load', function() {
47
    if (!("console" in window) || !("firebug" in window.console)) {
48
        var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
49
        "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
50

  
51
        window.console = {};
52
        for (var i = 0; i < names.length; ++i) {
53
            window.console[names[i]] = function() {};
54
        }
55
    }
56
});
57
/* inspired by extjs, apparently removes css image flicker and related problems in IE 6 */
58
/* This is already done in mootools Source/Core/Browser.js  KASI*/
59
/*
60
(function() {
61
    var ua = navigator.userAgent.toLowerCase();
62
    var isIE = ua.indexOf("msie") > -1,
63
        isIE7 = ua.indexOf("msie 7") > -1;
64
    if(isIE && !isIE7) {
65
        try {
66
            document.execCommand("BackgroundImageCache", false, true);
67
        } catch(e) {}
68
    }    
69
})();
70
*/
71
Class.Mutators.Family = function(self,name) {
72
    if ($defined(name)){
73
        self.$family = {'name': name};
74
        $[name] = $.object;
75
        return self;
76
    }
77
    else {
78
        this.implement('$family',{'name':self});
79
    }
80
};
81

  
82
/* Setup global namespace
83
 * If jxcore is loaded by jx.js, then the namespace and baseURL are
84
 * already established
85
 */
86
if (typeof Jx == 'undefined') {
87
    var Jx = {};
88
    (function() {
89
        var aScripts = document.getElementsByTagName('SCRIPT');
90
        for (var i=0; i<aScripts.length; i++) {
91
            var s = aScripts[i].src;
92
            var matches = /(.*[jx|js|lib])\/jxlib(.*)/.exec(s);
93
            if (matches && matches[0]) {
94
                /**
95
                 * Property: {String} baseURL
96
                 * This is the URL that Jx was loaded from, it is 
97
                 * automatically calculated from the script tag
98
                 * src property that included Jx.
99
                 *
100
                 * Note that this assumes that you are loading Jx
101
                 * from a js/ or lib/ folder in parallel to the
102
                 * images/ folder that contains the various images
103
                 * needed by Jx components.  If you have a different
104
                 * folder structure, you can define Jx's base
105
                 * by including the following before including
106
                 * the jxlib javascript file:
107
                 *
108
                 * (code)
109
                 * Jx = {
110
                 *    baseURL: 'some/path'
111
                 * }
112
                 * (end)
113
                 */ 
114
                 Jx.aPixel = document.createElement('img', {alt:'',title:''});
115
                 Jx.aPixel.src = matches[1]+'/a_pixel.png';
116
                 Jx.baseURL = Jx.aPixel.src.substring(0,
117
                     Jx.aPixel.src.indexOf('a_pixel.png'));
118
                
119
            }
120
        }
121
       /**
122
        * Determine if we're running in Adobe AIR. If so, determine which sandbox we're in
123
        */
124
        var src = aScripts[0].src;
125
        if (src.contains('app:')){
126
            Jx.isAir = true;
127
        } else {
128
            Jx.isAir = false;
129
        }
130
    })();
131
} 
132

  
133
/**
134
 * Method: applyPNGFilter
135
 *
136
 * Static method that applies the PNG Filter Hack for IE browsers
137
 * when showing 24bit PNG's.  Used automatically for img tags with
138
 * a class of png24.
139
 *
140
 * The filter is applied using a nifty feature of IE that allows javascript to
141
 * be executed as part of a CSS style rule - this ensures that the hack only
142
 * gets applied on IE browsers.
143
 *
144
 * The CSS that triggers this hack is only in the ie6.css files of the various
145
 * themes.
146
 *
147
 * Parameters:
148
 * object {Object} the object (img) to which the filter needs to be applied.
149
 */
150
Jx.applyPNGFilter = function(o)  {
151
   var t=Jx.aPixel.src;
152
   if( o.src != t ) {
153
       var s=o.src;
154
       o.src = t;
155
       o.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+s+"',sizingMethod='scale')";
156
   }
157
};
158

  
159
Jx.imgQueue = [];   //The queue of images to be loaded
160
Jx.imgLoaded = {};  //a hash table of images that have been loaded and cached
161
Jx.imagesLoading = 0; //counter for number of concurrent image loads 
162

  
163
/**
164
 * Method: addToImgQueue
165
 * Request that an image be set to a DOM IMG element src attribute.  This puts 
166
 * the image into a queue and there are private methods to manage that queue
167
 * and limit image loading to 2 at a time.
168
 *
169
 * Parameters:
170
 * obj - {Object} an object containing an element and src
171
 * property, where element is the element to update and src
172
 * is the url to the image.
173
 */
174
Jx.addToImgQueue = function(obj) {
175
    if (Jx.imgLoaded[obj.src]) {
176
        //if this image was already requested (i.e. it's in cache) just set it directly
177
        obj.element.src = obj.src;
178
    } else {
179
        //otherwise stick it in the queue
180
        Jx.imgQueue.push(obj);
181
        Jx.imgLoaded[obj.src] = true;
182
    }
183
    //start the queue management process
184
    Jx.checkImgQueue();
185
};
186

  
187
/**
188
 * Method: checkImgQueue
189
 *
190
 * An internal method that ensures no more than 2 images are loading at a time.
191
 */
192
Jx.checkImgQueue = function() {
193
    while (Jx.imagesLoading < 2 && Jx.imgQueue.length > 0) {
194
        Jx.loadNextImg();
195
    }
196
};
197

  
198
/**
199
 * Method: loadNextImg
200
 *
201
 * An internal method actually populate the DOM element with the image source.
202
 */
203
Jx.loadNextImg = function() {
204
    var obj = Jx.imgQueue.shift();
205
    if (obj) {
206
        ++Jx.imagesLoading;
207
        obj.element.onload = function(){--Jx.imagesLoading; Jx.checkImgQueue();};
208
        obj.element.onerror = function(){--Jx.imagesLoading; Jx.checkImgQueue();};
209
        obj.element.src = obj.src;
210
    }
211
};
212

  
213
/**
214
 * Method: createIframeShim
215
 * Creates a new iframe element that is intended to fill a container
216
 * to mask out other operating system controls (scrollbars, inputs, 
217
 * buttons, etc) when HTML elements are supposed to be above them.
218
 *
219
 * Returns:
220
 * an HTML iframe element that can be inserted into the DOM.
221
 */
222
Jx.createIframeShim = function() {
223
    return new Element('iframe', {
224
        'class':'jxIframeShim',
225
        'scrolling':'no',
226
        'frameborder':0
227
    });
228
};
229
/**
230
 * Method: getNumber
231
 * safely parse a number and return its integer value.  A NaN value 
232
 * returns 0.  CSS size values are also parsed correctly.
233
 *
234
 * Parameters: 
235
 * n - {Mixed} the string or object to parse.
236
 *
237
 * Returns:
238
 * {Integer} the integer value that the parameter represents
239
 */
240
Jx.getNumber = function(n, def) {
241
  var result = n===null||isNaN(parseInt(n,10))?(def||0):parseInt(n,10);
242
  return result;
243
}
244

  
245
/**
246
 * Method: getPageDimensions
247
 * return the dimensions of the browser client area.
248
 *
249
 * Returns:
250
 * {Object} an object containing a width and height property 
251
 * that represent the width and height of the browser client area.
252
 */
253
Jx.getPageDimensions = function() {
254
    return {width: window.getWidth(), height: window.getHeight()};
255
}
256

  
257
/**
258
 * Class: Element
259
 *
260
 * Element is a global object provided by the mootools library.  The
261
 * functions documented here are extensions to the Element object provided
262
 * by Jx to make cross-browser compatibility easier to achieve.  Most of the
263
 * methods are measurement related.
264
 *
265
 * While the code in these methods has been converted to use MooTools methods,
266
 * there may be better MooTools methods to use to accomplish these things.
267
 * Ultimately, it would be nice to eliminate most or all of these and find the
268
 * MooTools equivalent or convince MooTools to add them.
269
 */
270
Element.implement({
271
    /**
272
     * Method: getBoxSizing
273
     * return the box sizing of an element, one of 'content-box' or 
274
     *'border-box'.
275
     *
276
     * Parameters: 
277
     * elem - {Object} the element to get the box sizing of.
278
     *
279
     * Returns:
280
     * {String} the box sizing of the element.
281
     */
282
    getBoxSizing : function() {
283
      var result = 'content-box';
284
      if (Browser.Engine.trident || Browser.Engine.presto) { 
285
          var cm = document["compatMode"];
286
          if (cm == "BackCompat" || cm == "QuirksMode") { 
287
              result = 'border-box'; 
288
          } else {
289
              result = 'content-box'; 
290
        }
291
      } else {
292
          if (arguments.length === 0) {
293
              node = document.documentElement; 
294
          }
295
          var sizing = this.getStyle("-moz-box-sizing");
296
          if (!sizing) { 
297
              sizing = this.getStyle("box-sizing"); 
298
          }
299
          result = (sizing ? sizing : 'content-box');
300
      }
301
      return result;
302
    },
303
    /**
304
     * Method: getContentBoxSize
305
     * return the size of the content area of an element.  This is the size of
306
     * the element less margins, padding, and borders.
307
     *
308
     * Parameters: 
309
     * elem - {Object} the element to get the content size of.
310
     *
311
     * Returns:
312
     * {Object} an object with two properties, width and height, that
313
     * are the size of the content area of the measured element.
314
     */
315
    getContentBoxSize : function() {
316
      var w = this.offsetWidth;
317
      var h = this.offsetHeight;
318
      var padding = this.getPaddingSize();
319
      var border = this.getBorderSize();
320
      w = w - padding.left - padding.right - border.left - border.right;
321
      h = h - padding.bottom - padding.top - border.bottom - border.top;
322
      return {width: w, height: h};
323
    },
324
    /**
325
     * Method: getBorderBoxSize
326
     * return the size of the border area of an element.  This is the size of
327
     * the element less margins.
328
     *
329
     * Parameters: 
330
     * elem - {Object} the element to get the border sizing of.
331
     *
332
     * Returns:
333
     * {Object} an object with two properties, width and height, that
334
     * are the size of the border area of the measured element.
335
     */
336
    getBorderBoxSize: function() {
337
      var w = this.offsetWidth;
338
      var h = this.offsetHeight;
339
      return {width: w, height: h}; 
340
    },
341
    
342
    /**
343
     * Method: getMarginBoxSize
344
     * return the size of the margin area of an element.  This is the size of
345
     * the element plus margins.
346
     *
347
     * Parameters: 
348
     * elem - {Object} the element to get the margin sizing of.
349
     *
350
     * Returns:
351
     * {Object} an object with two properties, width and height, that
352
     * are the size of the margin area of the measured element.
353
     */
354
    getMarginBoxSize: function() {
355
        var margins = this.getMarginSize();
356
        var w = this.offsetWidth + margins.left + margins.right;
357
        var h = this.offsetHeight + margins.top + margins.bottom;
358
        return {width: w, height: h};
359
    },
360
    
361
    /**
362
     * Method: setContentBoxSize
363
     * set either or both of the width and height of an element to
364
     * the provided size.  This function ensures that the content
365
     * area of the element is the requested size and the resulting
366
     * size of the element may be larger depending on padding and
367
     * borders.
368
     *
369
     * Parameters: 
370
     * elem - {Object} the element to set the content area of.
371
     * size - {Object} an object with a width and/or height property that is the size to set
372
     * the content area of the element to.
373
     */
374
    setContentBoxSize : function(size) {
375
        if (this.getBoxSizing() == 'border-box') {
376
            var padding = this.getPaddingSize();
377
            var border = this.getBorderSize();
378
            if (typeof size.width != 'undefined') {
379
                var width = (size.width + padding.left + padding.right + border.left + border.right);
380
                if (width < 0) {
381
                    width = 0;
382
                }
383
                this.style.width = width + 'px';
384
            }
385
            if (typeof size.height != 'undefined') {
386
                var height = (size.height + padding.top + padding.bottom + border.top + border.bottom);
387
                if (height < 0) {
388
                    height = 0;
389
                }
390
                this.style.height = height + 'px';
391
            }
392
        } else {
393
            if (typeof size.width != 'undefined') {
394
                this.style.width = size.width + 'px';
395
            }
396
            if (typeof size.height != 'undefined') {
397
                this.style.height = size.height + 'px';
398
            }
399
        }
400
    },
401
    /**
402
     * Method: setBorderBoxSize
403
     * set either or both of the width and height of an element to
404
     * the provided size.  This function ensures that the border
405
     * size of the element is the requested size and the resulting
406
     * content areaof the element may be larger depending on padding and
407
     * borders.
408
     *
409
     * Parameters: 
410
     * elem - {Object} the element to set the border size of.
411
     * size - {Object} an object with a width and/or height property that is the size to set
412
     * the content area of the element to.
413
     */
414
    setBorderBoxSize : function(size) {
415
      if (this.getBoxSizing() == 'content-box') {
416
        var padding = this.getPaddingSize();
417
        var border = this.getBorderSize();
418
        var margin = this.getMarginSize();
419
        if (typeof size.width != 'undefined') {
420
          var width = (size.width - padding.left - padding.right - border.left - border.right - margin.left - margin.right);
421
          if (width < 0) {
422
            width = 0;
423
          }
424
          this.style.width = width + 'px';
425
        }
426
        if (typeof size.height != 'undefined') {
427
          var height = (size.height - padding.top - padding.bottom - border.top - border.bottom - margin.top - margin.bottom);
428
          if (height < 0) {
429
            height = 0;
430
          }
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff