Revision db6fbc92
vigiboard: implémentation des traductions BabelJS
git-svn-id: https://vigilo-dev.si.c-s.fr/svn@6831 b22e2e97-25c9-44ff-b637-2e5ceca36478
MANIFEST.in | ||
---|---|---|
13 | 13 |
graft vigiboard/public |
14 | 14 |
include *.ini |
15 | 15 |
include jsl.cfg |
16 |
include babeljs.py |
babeljs.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
""" |
|
3 |
Provides a class derived from Babel's compile_catalog |
|
4 |
which automatically creates JavaScript files compatible |
|
5 |
with Babel's JavaScript frontend. |
|
6 |
|
|
7 |
This code was taken from: |
|
8 |
http://svn.python.org/projects/doctools/trunk/setup.py |
|
9 |
|
|
10 |
And generates files for use with: |
|
11 |
http://babel.edgewall.org/browser/trunk/contrib/babel.js |
|
12 |
""" |
|
13 |
try: |
|
14 |
from babel.messages.pofile import read_po |
|
15 |
from babel.messages.frontend import compile_catalog |
|
16 |
try: |
|
17 |
from simplejson import dump |
|
18 |
except ImportError: |
|
19 |
from json import dump |
|
20 |
except ImportError: |
|
21 |
pass |
|
22 |
else: |
|
23 |
import os |
|
24 |
from distutils import log |
|
25 |
|
|
26 |
class compile_catalog_plusjs(compile_catalog): |
|
27 |
""" |
|
28 |
An extended command that writes all message strings that occur in |
|
29 |
JavaScript files to a JavaScript file along with the .mo file. |
|
30 |
|
|
31 |
Unfortunately, babel's setup command isn't built very extensible, so |
|
32 |
most of the run() code is duplicated here. |
|
33 |
""" |
|
34 |
|
|
35 |
def run(self): |
|
36 |
compile_catalog.run(self) |
|
37 |
|
|
38 |
po_files = [] |
|
39 |
js_files = [] |
|
40 |
|
|
41 |
if not self.input_file: |
|
42 |
if self.locale: |
|
43 |
po_files.append((self.locale, |
|
44 |
os.path.join(self.directory, self.locale, |
|
45 |
'LC_MESSAGES', |
|
46 |
self.domain + '.po'))) |
|
47 |
js_files.append(os.path.join(self.directory, self.locale, |
|
48 |
'LC_MESSAGES', |
|
49 |
self.domain + '.js')) |
|
50 |
else: |
|
51 |
for locale in os.listdir(self.directory): |
|
52 |
po_file = os.path.join(self.directory, locale, |
|
53 |
'LC_MESSAGES', self.domain + '.po') |
|
54 |
if os.path.exists(po_file): |
|
55 |
po_files.append((locale, po_file)) |
|
56 |
js_files.append(os.path.join(self.directory, locale, |
|
57 |
'LC_MESSAGES', |
|
58 |
self.domain + '.js')) |
|
59 |
else: |
|
60 |
po_files.append((self.locale, self.input_file)) |
|
61 |
if self.output_file: |
|
62 |
js_files.append(self.output_file) |
|
63 |
else: |
|
64 |
js_files.append(os.path.join(self.directory, self.locale, |
|
65 |
'LC_MESSAGES', |
|
66 |
self.domain + '.js')) |
|
67 |
|
|
68 |
for js_file, (locale, po_file) in zip(js_files, po_files): |
|
69 |
infile = open(po_file, 'r') |
|
70 |
try: |
|
71 |
catalog = read_po(infile, locale) |
|
72 |
finally: |
|
73 |
infile.close() |
|
74 |
|
|
75 |
if catalog.fuzzy and not self.use_fuzzy: |
|
76 |
continue |
|
77 |
|
|
78 |
log.info('writing JavaScript strings in catalog %r to %r', |
|
79 |
po_file, js_file) |
|
80 |
|
|
81 |
jscatalog = {} |
|
82 |
for message in catalog: |
|
83 |
# Si le message n'a pas encore été traduit, |
|
84 |
# on ne l'ajoute pas. Le message d'origine |
|
85 |
# (non traduit) sera renvoyé. |
|
86 |
if not message.string: |
|
87 |
continue |
|
88 |
|
|
89 |
# On n'ajoute le message au fichier de traduction JS |
|
90 |
# auto-généré que si le message est utilisé dans du |
|
91 |
# code JavaScript. |
|
92 |
if any(x[0].endswith('.js') for x in message.locations): |
|
93 |
msgid = message.id |
|
94 |
if isinstance(msgid, (list, tuple)): |
|
95 |
msgid = msgid[0] |
|
96 |
jscatalog[msgid] = message.string |
|
97 |
|
|
98 |
outfile = open(js_file, 'wb') |
|
99 |
try: |
|
100 |
outfile.write('babel.Translations.load('); |
|
101 |
dump(dict( |
|
102 |
messages=jscatalog, |
|
103 |
plural_expr=catalog.plural_expr, |
|
104 |
locale=str(catalog.locale) |
|
105 |
), outfile) |
|
106 |
outfile.write(').install();') |
|
107 |
finally: |
|
108 |
outfile.close() |
|
109 |
|
setup.py | ||
---|---|---|
20 | 20 |
|
21 | 21 |
sysconfdir = os.getenv("SYSCONFDIR", "/etc") |
22 | 22 |
|
23 |
cmdclass = {} |
|
24 |
try: |
|
25 |
from babeljs import compile_catalog_plusjs |
|
26 |
except ImportError: |
|
27 |
pass |
|
28 |
else: |
|
29 |
cmdclass['compile_catalog'] = compile_catalog_plusjs |
|
30 |
|
|
23 | 31 |
setup( |
24 | 32 |
name='vigilo-vigiboard', |
25 | 33 |
version='2.0.0', |
... | ... | |
45 | 53 |
package_data={ |
46 | 54 |
'vigiboard': [ |
47 | 55 |
'i18n/*/LC_MESSAGES/*.mo', |
56 |
'i18n/*/LC_MESSAGES/*.js', |
|
57 |
'templates/*/*', |
|
58 |
'public/js/*.js', |
|
48 | 59 |
], |
49 | 60 |
}, |
50 | 61 |
message_extractors={ |
51 | 62 |
'vigiboard': [ |
52 | 63 |
('**.py', 'python', None), |
64 |
('**/public/js/*.js', 'javascript', None), |
|
53 | 65 |
], |
54 | 66 |
}, |
55 | 67 |
|
... | ... | |
79 | 91 |
'masked_events = vigiboard.controllers.plugins.masked_events:PluginMaskedEvents', |
80 | 92 |
] |
81 | 93 |
}, |
94 |
cmdclass=cmdclass, |
|
82 | 95 |
data_files=[ |
83 | 96 |
(os.path.join(sysconfdir, 'vigilo/vigiboard/'), [ |
84 | 97 |
'deployment/vigiboard.conf', |
vigiboard/controllers/root.py | ||
---|---|---|
23 | 23 |
from datetime import datetime |
24 | 24 |
from time import mktime |
25 | 25 |
|
26 |
from pkg_resources import resource_filename |
|
27 |
|
|
26 | 28 |
from tg.exceptions import HTTPNotFound |
27 | 29 |
from tg import expose, validate, require, flash, url, \ |
28 | 30 |
tmpl_context, request, config, session, redirect |
29 | 31 |
from webhelpers import paginate |
30 | 32 |
from tw.forms import validators |
31 |
from pylons.i18n import ugettext as _, lazy_ugettext as l_ |
|
33 |
from pylons.i18n import ugettext as _, lazy_ugettext as l_, get_lang
|
|
32 | 34 |
from sqlalchemy import asc |
33 | 35 |
from sqlalchemy.sql import func |
34 | 36 |
from sqlalchemy.orm import aliased |
... | ... | |
208 | 210 |
) |
209 | 211 |
|
210 | 212 |
|
213 |
@expose() |
|
214 |
def i18n(self): |
|
215 |
import gettext |
|
216 |
import pylons |
|
217 |
import os.path |
|
218 |
|
|
219 |
# Repris de pylons.i18n.translation:_get_translator. |
|
220 |
conf = pylons.config.current_conf() |
|
221 |
try: |
|
222 |
rootdir = conf['pylons.paths']['root'] |
|
223 |
except KeyError: |
|
224 |
rootdir = conf['pylons.paths'].get('root_path') |
|
225 |
localedir = os.path.join(rootdir, 'i18n') |
|
226 |
|
|
227 |
lang = get_lang() |
|
228 |
|
|
229 |
# Localise le fichier *.mo actuellement chargé |
|
230 |
# et génère le chemin jusqu'au *.js correspondant. |
|
231 |
filename = gettext.find(conf['pylons.package'], localedir, |
|
232 |
languages=lang) |
|
233 |
js = filename[:-3] + '.js' |
|
234 |
|
|
235 |
themes_filename = gettext.find( |
|
236 |
'vigilo-themes', |
|
237 |
resource_filename('vigilo.themes.i18n', ''), |
|
238 |
languages=lang) |
|
239 |
themes_js = themes_filename[:-3] + '.js' |
|
240 |
|
|
241 |
# Récupère et envoie le contenu du fichier de traduction *.js. |
|
242 |
fhandle = open(js, 'r') |
|
243 |
translations = fhandle.read() |
|
244 |
fhandle.close() |
|
245 |
|
|
246 |
fhandle = open(themes_js, 'r') |
|
247 |
translations += fhandle.read() |
|
248 |
fhandle.close() |
|
249 |
return translations |
|
250 |
|
|
251 |
|
|
211 | 252 |
class MaskedEventsSchema(schema.Schema): |
212 | 253 |
"""Schéma de validation de la méthode masked_events.""" |
213 | 254 |
idcorrevent = validators.Int(not_empty=True) |
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 |
}); |
vigiboard/public/js/autocompleter-vigilo.js | ||
---|---|---|
1 |
/** |
|
2 |
* Autocompleter.Request.VigiloJSON |
|
3 |
* |
|
4 |
*/ |
|
5 |
Autocompleter.Request.VigiloJSON = new Class({ |
|
6 |
|
|
7 |
Extends: Autocompleter.Request, |
|
8 |
|
|
9 |
options: { |
|
10 |
resVar: 'results' |
|
11 |
}, |
|
12 |
|
|
13 |
initialize: function(el, url, options) { |
|
14 |
this.parent(el, options); |
|
15 |
this.request = new Request.JSON($merge({ |
|
16 |
'url': url, |
|
17 |
'link': 'cancel' |
|
18 |
}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this)); |
|
19 |
}, |
|
20 |
|
|
21 |
queryResponse: function(response) { |
|
22 |
this.parent(); |
|
23 |
this.update(response[this.options.resVar]); |
|
24 |
} |
|
25 |
|
|
26 |
}); |
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 |
} |
|
431 |
this.style.height = height + 'px'; |
|
432 |
} |
|
433 |
} else { |
|
434 |
if (typeof size.width != 'undefined' && size.width >= 0) { |
|
435 |
this.style.width = size.width + 'px'; |
|
436 |
} |
|
437 |
if (typeof size.height != 'undefined' && size.height >= 0) { |
|
438 |
this.style.height = size.height + 'px'; |
|
439 |
} |
|
440 |
} |
|
441 |
}, |
|
442 |
/** |
|
443 |
* Method: getPaddingSize |
|
444 |
* returns the padding for each edge of an element |
|
445 |
* |
|
446 |
* Parameters: |
|
447 |
* elem - {Object} The element to get the padding for. |
|
448 |
* |
|
449 |
* Returns: |
|
450 |
* {Object} an object with properties left, top, right and bottom |
|
451 |
* that contain the associated padding values. |
|
452 |
*/ |
|
453 |
getPaddingSize : function () { |
|
454 |
var l = Jx.getNumber(this.getStyle('padding-left')); |
|
455 |
var t = Jx.getNumber(this.getStyle('padding-top')); |
|
456 |
var r = Jx.getNumber(this.getStyle('padding-right')); |
|
457 |
var b = Jx.getNumber(this.getStyle('padding-bottom')); |
|
458 |
return {left:l, top:t, right: r, bottom: b}; |
|
459 |
}, |
|
460 |
/** |
|
461 |
* Method: getBorderSize |
|
462 |
* returns the border size for each edge of an element |
|
463 |
* |
|
464 |
* Parameters: |
|
465 |
* elem - {Object} The element to get the borders for. |
|
466 |
* |
|
467 |
* Returns: |
|
468 |
* {Object} an object with properties left, top, right and bottom |
|
469 |
* that contain the associated border values. |
|
470 |
*/ |
|
471 |
getBorderSize : function() { |
|
472 |
var l = Jx.getNumber(this.getStyle('border-left-width')); |
|
473 |
var t = Jx.getNumber(this.getStyle('border-top-width')); |
|
474 |
var r = Jx.getNumber(this.getStyle('border-right-width')); |
|
475 |
var b = Jx.getNumber(this.getStyle('border-bottom-width')); |
|
476 |
return {left:l, top:t, right: r, bottom: b}; |
|
477 |
}, |
|
478 |
/** |
|
479 |
* Method: getMarginSize |
|
480 |
* returns the margin size for each edge of an element |
|
481 |
* |
|
482 |
* Parameters: |
|
483 |
* elem - {Object} The element to get the margins for. |
|
484 |
* |
|
485 |
* Returns: |
|
486 |
*: {Object} an object with properties left, top, right and bottom |
|
487 |
* that contain the associated margin values. |
|
488 |
*/ |
|
489 |
getMarginSize : function() { |
|
490 |
var l = Jx.getNumber(this.getStyle('margin-left')); |
|
491 |
var t = Jx.getNumber(this.getStyle('margin-top')); |
|
492 |
var r = Jx.getNumber(this.getStyle('margin-right')); |
|
493 |
var b = Jx.getNumber(this.getStyle('margin-bottom')); |
|
494 |
return {left:l, top:t, right: r, bottom: b}; |
|
495 |
}, |
|
496 |
|
|
497 |
/** |
|
498 |
* Method: descendantOf |
|
499 |
* determines if the element is a descendent of the reference node. |
|
500 |
* |
|
501 |
* Parameters: |
|
502 |
* node - {HTMLElement} the reference node |
|
503 |
* |
|
504 |
* Returns: |
|
505 |
* {Boolean} true if the element is a descendent, false otherwise. |
|
506 |
*/ |
|
507 |
descendantOf: function(node) { |
|
508 |
var parent = $(this.parentNode); |
|
509 |
while (parent != node && parent && parent.parentNode && parent.parentNode != parent) { |
|
510 |
parent = $(parent.parentNode); |
|
511 |
} |
|
512 |
return parent == node; |
|
513 |
}, |
|
514 |
|
|
515 |
/** |
|
516 |
* Method: findElement |
Also available in: Unified diff