Merge lp:~robbyoconnor/sahana-eden/surveytool into lp:sahana-eden

Proposed by Robert O'Connor
Status: Merged
Merged at revision: 1168
Proposed branch: lp:~robbyoconnor/sahana-eden/surveytool
Merge into: lp:sahana-eden
Diff against target: 14613 lines (+14129/-292)
27 files modified
controllers/budget.py (+2/-2)
controllers/survey.py (+460/-0)
models/000_config.py (+295/-288)
models/survey.py (+142/-0)
modules/validators.py (+1/-1)
modules/widgets.py (+1/-1)
static/scripts/survey/jquery-ui-1.8.4.custom.js (+11241/-0)
static/scripts/survey/jquery.bsmselect.js (+481/-0)
static/scripts/survey/jquery.selectboxes.js (+533/-0)
static/scripts/survey/jquery.selectboxes.min.js (+14/-0)
static/styles/survey/jquery.bsmselect.css (+64/-0)
views/question_create.html (+21/-0)
views/survey/index.html (+16/-0)
views/survey/layout.html (+98/-0)
views/survey/layout_popup.html (+102/-0)
views/survey/question_options_create.html (+117/-0)
views/survey/question_options_update.html (+111/-0)
views/survey/question_popup.html (+26/-0)
views/survey/question_update.html (+21/-0)
views/survey/questions.html (+82/-0)
views/survey/section_layout.html (+184/-0)
views/survey/table.html (+26/-0)
views/survey/table_create.html (+21/-0)
views/survey/table_list.html (+3/-0)
views/survey/table_update.html (+21/-0)
views/survey/template_create.html (+23/-0)
views/survey/template_update.html (+23/-0)
To merge this branch: bzr merge lp:~robbyoconnor/sahana-eden/surveytool
Reviewer Review Type Date Requested Status
Fran Boon Pending
Review via email: mp+34371@code.launchpad.net

Commit message

merge survey tool into trunk. Incredibly buggy...people are advised to *NOT* use this code in a production instance of sahana as it may or may not work properly!

Description of the change

Fran,

I'm allowing you to (finally) merge in the survey tool. I've got a full plate right now and won't have the time to make the changes necessary.

There are bugs:

All on the "Add Questions" page (this page is very buggy).

* the CSS on the multiselect widget is busted and acts weird with long Questions
* It doesn't update properly
* It doesn't seem to reload the questions correctly -- probably on account of a bad query

Sections have yet to be implemented but should be done using subheadings. I want to do this, but if somebody gets to it first, oh well.

You may also want to peek at http://code.launchpad.net/~robbyoconnor/gsoc2010 and see if maybe that's in a better state than this one? I'm pretty sure that was buggy as well -- I don't remember what state that branch is in. I feel that this branch is probably best to merge. Code may be a mess right now.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'VERSION' (properties changed: +x to -x)
2=== modified file 'controllers/budget.py'
3--- controllers/budget.py 2010-08-28 09:44:22 +0000
4+++ controllers/budget.py 2010-09-02 01:18:42 +0000
5@@ -153,7 +153,7 @@
6 "Module's Home Page"
7
8 module_name = deployment_settings.modules[module].name_nice
9-
10+
11 return dict(module_name=module_name)
12
13 def parameters():
14@@ -1418,7 +1418,7 @@
15 budget = int(request.args(0))
16 except TypeError, ValueError:
17 session.error = T("Need to specify a Budget!")
18- redirect(URL(r=request, f="budget"))
19+ redirect(URL(r=reqest, f="budget"))
20
21 tables = [db.budget_budget_staff, db.budget_budget_bundle]
22 authorised = shn_has_permission("update", tables[0]) and shn_has_permission("update", tables[1])
23
24=== modified file 'controllers/default.py' (properties changed: +x to -x)
25=== modified file 'controllers/pr.py' (properties changed: +x to -x)
26=== added file 'controllers/survey.py'
27--- controllers/survey.py 1970-01-01 00:00:00 +0000
28+++ controllers/survey.py 2010-09-02 01:18:42 +0000
29@@ -0,0 +1,460 @@
30+# -*- coding: utf-8 -*-
31+
32+"""
33+ Survey Module -- a tool to create surveys.
34+
35+ @author: Robert O'Connor
36+"""
37+from gluon.html import *
38+from gluon.sqlhtml import SQLFORM
39+
40+module = "survey"
41+
42+
43+# Will populate later on.
44+response.menu_options = [
45+ [T("Template"), False, URL(r=request, f="template"),[
46+ [T("List"), False, URL(r=request, f="template")],
47+ [T("Add"), False, URL(r=request, f="template", args="create")]
48+ ]],
49+ [T("Series"), False, URL(r=request, f="series"),[
50+ [T("List"), False, URL(r=request, f="series")],
51+ [T("Add"), False, URL(r=request, f="series", args="create")]
52+ ]]]
53+def template_link():
54+ response.s3.prep = response.s3.prep = lambda jr: jr.representation in ("xml", "json") and True or False
55+ return shn_rest_controller("survey", "template_link")
56+
57+def index():
58+ "Module's Home Page"
59+ module_name = deployment_settings.modules[module].name_nice
60+ return dict(module_name=module_name)
61+
62+def template():
63+ """ RESTlike CRUD controller """
64+ resource = "template"
65+ def _prep(jr):
66+ crud.settings.create_next = URL(r = request, c="survey", f="questions")
67+ crud.settings.update_next = URL(r = request, c="survey", f="questions")
68+ return True
69+ response.s3.prep = _prep
70+
71+ tablename = "%s_%s" % (module, resource)
72+ table = db[tablename]
73+ table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
74+ table.name.requires = IS_NOT_EMPTY()
75+ table.name.label = T("Survey Name")
76+ table.name.comment = SPAN("*", _class="req")
77+ table.description.label = T("Description")
78+
79+
80+ # CRUD Strings
81+ s3.crud_strings[tablename] = Storage(
82+ title_create = T("Add Survey Template"),
83+ title_display = T('Survey Template Details'),
84+ title_list = T("List Survey Templates"),
85+ title_update = T('Edit Survey Template'),
86+ subtitle_create = T('Add New Survey Template'),
87+ subtitle_list = T('Survey Templates'),
88+ label_list_button = T("List Survey Templates"),
89+ label_create_button = T("Add Survey Template"),
90+ msg_record_created = T('Survey Template added'),
91+ msg_record_modified = T('Survey Template updated'),
92+ msg_record_deleted = T('Survey Template deleted'),
93+ msg_list_empty = T('No Survey Template currently registered'))
94+
95+ output = shn_rest_controller(module,resource,listadd=False)
96+ return transform_buttons(output,next=True,cancel=True)
97+
98+def questions():
99+ """
100+ At this stage, the user the following workflow will be implemented:
101+
102+ - User adds questions via the drop down or clicks "Add Question" to add a new one.
103+ """
104+ table = db["survey_questions"]
105+ record = request.args(0)
106+ template = db(db.survey_template.id == session.rcvars.survey_template).select().first()
107+ if not record:
108+ questions_query = (db.survey_template_link.survey_questions_id == db.survey_questions.id) & (template.id == db.survey_template_link.survey_template_id)
109+ record = db(questions_query).select(db.survey_questions.id).first()
110+ if record:
111+ redirect(URL(r=request,f="questions",args=[record.id]))
112+ questions_form = SQLFORM(table,record,deletable=True,keepvalues=True)
113+ all_questions = db().select(db.survey_question.ALL)
114+ output = dict(all_questions=all_questions)
115+ # Let's avoid blowing up -- this loads questions
116+ try:
117+ query = (template.id == db.survey_template_link.survey_template_id)
118+ contained_questions = db(query).select(db.survey_question.id)
119+ if len(contained_questions) > 0:
120+ output.update(contained_questions=contained_questions)
121+ else:
122+ output.update(contained_questions=contained_questions)
123+ except:
124+ output.update(contained_questions=[])
125+ pass # this means we didn't pass an id, e.g., making a new section!
126+ if questions_form.accepts(request.vars,session,keepvalues=True):
127+ questions = request.post_vars.questions
128+ if questions:
129+ for question in questions:
130+ if not has_dupe_questions(template.id,question):
131+ db.survey_template_link.insert(survey_template_id=session.rcvars.survey_template,survey_questions_id=questions_form.vars.id,
132+ survey_question_id=question)
133+ elif questions_form.errors:
134+ response.flash= T("Please correct all errors.")
135+ output.update(form=questions_form)
136+ return output
137+def table():
138+ if not "series_id" in request.vars:
139+ response.error = "You must provide both a series id to proceed."
140+ return dict() # empty dict!
141+
142+ # store the necessary information -- we're safe at this point.
143+ series_id = request.vars.series_id
144+
145+ # first solely check the series exists.
146+ series = db(db.survey_series.id == series_id).select().first()
147+ if not series:
148+ response.error = T("A survey series with id %s does not exist. Please go back and create one.") % (series_id)
149+ return dict()
150+ # query for the template to get the table name
151+ template = db(db.survey_template.id == series.survey_template_id).select().first()
152+
153+
154+ # everything is good at this point!
155+ table = get_table_for_template(template.id)
156+ resource = "template_%s" % (template.id)
157+ table.uuid.requires = IS_NOT_IN_DB(db, "%s.uuid" % template.table_name)
158+ table.id.represent = lambda id: A(id,_href=URL(r=request,f="table",args=[id,"update"], vars={"series_id":request.vars.series_id}))
159+ # CRUD Strings
160+ s3.crud_strings[template.table_name] = Storage(
161+ title_create = T("Add Survey Answer"),
162+ title_display = T("Survey Answer Details"),
163+ title_list = T("List Survey Answers"),
164+ title_update = T("Edit Survey Answer"),
165+ subtitle_create = T("Add New Survey Answer"),
166+ subtitle_list = T("Survey Answer"),
167+ label_list_button = T("List Survey Answers"),
168+ label_create_button = T("Add Survey Answer"),
169+ msg_record_created = T("Survey Answer added"),
170+ msg_record_modified = T("Survey Answer updated"),
171+ msg_record_deleted = T("Survey Answer deleted"),
172+ msg_list_empty = T("No Survey Answers currently registered"))
173+ response.s3.filter = (table.series_id == series_id)
174+ output = shn_rest_controller("survey", resource,listadd=False)
175+ authorised = shn_has_permission("create", table)
176+ if authorised:
177+ output.update(add_btn=A(Tstr("Add Survey Answer"), _href=URL(r=request,f="table", args=["create"], vars={"series_id":request.vars.series_id}),
178+ _class="action-btn"))
179+ else:
180+ output.update(add_btn="")
181+ return output
182+
183+def series():
184+ """ RESTlike CRUD controller """
185+ resource = "series"
186+ tablename = "%s_%s" % (module, resource)
187+ table = db[tablename]
188+ table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
189+ table.name.requires = IS_NOT_EMPTY()
190+ table.name.label = T("Survey Series Name")
191+ table.name.comment = SPAN("*", _class="req")
192+ table.description.label = T("Description")
193+ table.survey_template_id.label = T("Survey Template")
194+ table.survey_template_id.requires = IS_ONE_OF(db, "survey_template.id", "%(name)s")
195+ table.survey_template_id.represent = lambda id: (id and [db(db.survey_template.id==id).select()[0].name] or [""])[0]
196+ table.survey_template_id.comment = SPAN("*", _class="req")
197+ table.from_date.label = T("Start of Period")
198+ table.from_date.requires = IS_NOT_EMPTY()
199+ table.from_date.comment = SPAN("*", _class="req")
200+ table.to_date.label = T("End of Period")
201+ table.to_date.requires = IS_NOT_EMPTY()
202+ table.to_date.comment = SPAN("*", _class="req")
203+
204+ # CRUD Strings
205+ s3.crud_strings[tablename] = Storage(
206+ title_create = T("Add Survey Series"),
207+ title_display = T("Survey Series Details"),
208+ title_list = T("List Survey Series"),
209+ title_update = T("Edit Survey Series"),
210+ subtitle_create = T("Add New Survey Series"),
211+ subtitle_list = T("Survey Series"),
212+ label_list_button = T("List Survey Series"),
213+ label_create_button = T("Add Survey Series"),
214+ msg_record_created = T("Survey Series added"),
215+ msg_record_modified = T("Survey Series updated"),
216+ msg_record_deleted = T("Survey Series deleted"),
217+ msg_list_empty = T("No Survey Series currently registered"))
218+ def _postp(jr, output):
219+ shn_action_buttons(jr, deletable=False)
220+ return output
221+ response.s3.postp = _postp
222+
223+ output = shn_rest_controller(module, resource,listadd=False)
224+
225+ return output
226+
227+#def section():
228+# """ RESTlike CRUD controller """
229+# resource = "section"
230+# tablename = "%s_%s" % (module, resource)
231+# table = db[tablename]
232+# table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
233+# table.name.requires = IS_NOT_EMPTY()
234+# table.name.comment = SPAN("*", _class="req")
235+# table.name.label = T("Survey Section Display Name")
236+# table.description.label = T("Description")
237+#
238+#
239+# # CRUD Strings
240+# s3.crud_strings[tablename] = Storage(
241+# title_create = T("Add Survey Section"),
242+# title_display = T("Survey Section Details"),
243+# title_list = T("List Survey Sections"),
244+# title_update = T("Edit Survey Section"),
245+# subtitle_create = T("Add New Survey Section"),
246+# subtitle_list = T("Survey Section"),
247+# label_list_button = T("List Survey Sections"),
248+# label_create_button = T("Add Survey Section"),
249+# msg_record_created = T("Survey Section added"),
250+# msg_record_modified = T("Survey Section updated"),
251+# msg_record_deleted = T("Survey Section deleted"),
252+# msg_list_empty = T("No Survey Sections currently registered"))
253+# output = shn_rest_controller(module, resource,listadd=False)
254+#
255+# return transform_buttons(output,save=True,cancel=True)
256+
257+def question():
258+ # Question data, e.g., name,description, etc.
259+ resource = "question"
260+ tablename = "%s_%s" % (module, resource)
261+ table = db[tablename]
262+ table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
263+ table.name.requires = IS_NOT_EMPTY()
264+ table.name.label = T("Survey Question Display Name")
265+ table.name.comment = SPAN("*", _class="req")
266+ table.description.label = T("Description")
267+# table.tf_ta_columns.label = T("Number of Columns")
268+# table.ta_rows.label = T("Number of Rows")
269+# table.aggregation_type.writable = False
270+# table.aggregation_type.readable = False
271+
272+ question_types = {
273+# 1:T("Multiple Choice (Only One Answer)"),
274+# 2:T("Multiple Choice (Multiple Answers)"),
275+# 3:T("Matrix of Choices (Only one answer)"),
276+# 4:T("Matrix of Choices (Multiple Answers)"),
277+# 5:T("Rating Scale"),
278+ 6:T("Text"),
279+# 7:T("Multiple Text Fields"),
280+# 8:T("Matrix of Text Fields"),
281+ 9:T("Long Text"),
282+ 10:T("Number"),
283+ 11:T("Date"),
284+# 12:T("Image"),
285+# 13:T("Descriptive Text (e.g., Prose, etc)"),
286+# 14:T("Location"),
287+# 15:T("Organisation"),
288+# 16:T("Person"),
289+## 16:T("Custom Database Resource (e.g., anything defined as a resource in Sahana)")
290+ }
291+
292+ table.question_type.requires = IS_IN_SET(question_types)
293+ table.question_type.comment = SPAN("*", _class="req")
294+ # CRUD Strings
295+ s3.crud_strings[tablename] = Storage(
296+ title_create = T("Add Survey Question"),
297+ title_display = T("Survey Question Details"),
298+ title_list = T("List Survey Questions"),
299+ title_update = T("Edit Survey Question"),
300+ subtitle_create = T("Add New Survey Question"),
301+ subtitle_list = T("Survey Question'"),
302+ label_list_button = T("List Survey Questions"),
303+ label_create_button = T("Add Survey Question"),
304+ msg_record_created = T("Survey Question added"),
305+ msg_record_modified = T("Survey Question updated"),
306+ msg_record_deleted = T("Survey Question deleted"),
307+ msg_list_empty = T("No Survey Questions currently registered"))
308+ output = shn_rest_controller(module, resource,listadd=False)
309+
310+ return transform_buttons(output,cancel=True,save=True)
311+
312+#def question_options():
313+# resource = "question"
314+# tablename = "%s_%s" % (module, resource)
315+# table = db[tablename]
316+# table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
317+# table.tf_ta_columns.label = T("Number of Columns")
318+# table.ta_rows.label = T("Number of Rows")
319+## table.answer_choices.label = T("Answer Choices (One Per Line)")
320+# table.aggregation_type.writable = False
321+# table.aggregation_type.readable = False
322+## table.row_choices.label = T("Row Choices (One Per Line)")
323+## table.column_choices.label = T("Column Choices (One Per Line")
324+## table.tf_choices.label = T("Text before each Text Field (One per line)")
325+# output = shn_rest_controller(module, resource,listadd=False)
326+# output.update(question_type=question_type)
327+# return transform_buttons(output,prev=True,finish=True,cancel=True)
328+
329+def add_buttons(form, save = None, prev = None, next = None, finish = None,cancel=None):
330+ """
331+ Utility Function to reduce code duplication as this deals with:
332+
333+ 1) removing the save button
334+ 2) adding the following: Cancel, Next, Previous and Finish(shown on the last step *ONLY*)
335+ """
336+ form[0][-1][1][0] = "" # remove the original Save Button
337+ if save:
338+ form[-1][-1][1].append(INPUT(_type="submit",_name = "save",_value=T("Save"), _id="save"))
339+
340+ if cancel:
341+ form[-1][-1][1].append(INPUT(_type="button",_name = "cancel",_value=T("Cancel"), _id="cancel"))
342+
343+ if prev:
344+ form[-1][-1][1].append(INPUT(_type="submit",_name = "prev",_value=T("Previous"), _id="prev"))
345+
346+ if next:
347+ form[-1][-1][1].append(INPUT(_type="submit", _value=T("Next"),_name="next",_id="next"))
348+
349+ if finish:
350+ form[-1][-1][1].append(INPUT(_type="submit", _value=T("Finish"),_name="finish",_id="finish"))
351+ return form
352+
353+def transform_buttons(output,save = None, prev = None, next = None, finish = None,cancel=None):
354+ # fails when output is not HTML (e.g., JSON)
355+ if isinstance(output, dict):
356+ form = output.get("form",None)
357+ if form:
358+ add_buttons(form,save,prev,next,finish,cancel)
359+ return output
360+
361+def has_dupe_questions(template_id,question_id):
362+ question_query = (db.survey_template_link.survey_template_id == template_id) \
363+ & (question_id == db.survey_template_link.survey_question_id)
364+ questions = db(question_query).select(db.survey_question.ALL)
365+ if len(questions) > 1:
366+
367+ return True
368+ else:
369+ return False
370+
371+def prune_questions(questions_id, questions,all_questions):
372+ if not questions_id:
373+ return # do nothing
374+ if not questions:
375+ return # nothing to act on.
376+ for question in all_questions:
377+ if not question in questions:
378+ question_query = (db.survey_template_link.survey_questions_id == questions_id) \
379+ & (question.id == db.survey_template_link.survey_question_id)
380+ db(question_query).delete()
381+ db.commit()
382+ return questions
383+
384+def get_contained_questions(questions_id):
385+ question_query = (db.survey_template_link.survey_questions_id == questions_id) & \
386+ (db.survey_question.id == db.survey_template_link.survey_question_id) & \
387+ (db.survey_template.id == db.survey_template_link.survey_template_id)
388+ contained_questions = db(question_query).select(db.survey_question.ALL)
389+ return contained_questions
390+
391+def get_table_for_template(template_id):
392+ """ Returns the table for the given template and if it doesn't exist -- creates and returns that"""
393+
394+ # get the template first -- we need to get the table name
395+ template = db(db.survey_template.id == template_id).select().first()
396+
397+ tbl = None
398+
399+ if template: # avoid blow ups!
400+ fields = [Field("series_id", db.survey_series, writable=False, readable=False)
401+ ] # A list of Fields representing the questions
402+
403+ questions = db((db.survey_template_link.survey_template_id == template_id) & \
404+ (db.survey_question.id == db.survey_template_link.survey_question_id)).select(db.survey_question.ALL)
405+ # for each question, depending on its type create a Field
406+ for question in questions:
407+ question_type = question.question_type
408+
409+ if question_type == 6: # Single TF -- simple for now -- will introduce more types later.
410+ fields.append(Field("question_%s" % (question.id), label=question.name))
411+
412+
413+ elif question_type == 9:
414+ fields.append(Field("question_%s" % (question.id), "text", label=question.name))
415+
416+ elif question_type == 10:
417+ fields.append(Field("question_%s" % (question.id), "integer", label=question.name))
418+
419+ elif question_type == 11:
420+ fields.append(Field("question_%s" % (question.id), "date", label=question.name))
421+
422+ tbl = db.define_table("survey_template_%s" % (template_id), uuidstamp, deletion_status, authorstamp,
423+ *fields, migrate=True)
424+ # now add the table name to the template record so we can reference it later.
425+ db(db.survey_template.id == template_id).update(table_name="survey_template_%s" % (template.id))
426+ db.commit()
427+
428+ # set up onaccept for this table.
429+ def _onaccept(form):
430+ db(tbl.id == form.vars.id).update(series_id=request.vars.series_id)
431+ db.commit()
432+
433+ s3xrc.model.configure(tbl,
434+ onaccept=lambda form: _onaccept(form))
435+ # finally we return the newly created or existing table.
436+ return tbl
437+
438+def shn_action_buttons(jr, deletable=True):
439+
440+ """ Provide the usual Action Buttons for Column views. Designed to be called from a postp """
441+
442+ if jr.component:
443+ args = [jr.component_name, "[id]"]
444+ else:
445+ args = ["[id]"]
446+ if auth.is_logged_in():
447+ # Provide the ability to delete records in bulk
448+ if deletable:
449+ response.s3.actions = [
450+ dict(label=str(UPDATE), _class="action-btn", url=str(URL(r=request, args = args + ["update"]))),
451+ dict(label=str(DELETE), _class="action-btn", url=str(URL(r=request, args = args + ["delete"])))
452+ ]
453+ else:
454+ url = URL(r=request,f="table",vars= {"series_id":args})
455+ response.s3.actions = [
456+ dict(label=str(UPDATE), _class="action-btn", url=str(URL(r=request, args = args + ["update"]))),
457+ dict(label="Answer", _class="action-btn", url=str( URL(r=request,f="table",args="create", vars= {"series_id":"[id]"}))),
458+ dict(label="Results", _class="action-btn", url=str( URL(r=request,f="table",vars= {"series_id":"[id]"}))) ]
459+ else:
460+ response.s3.actions = [
461+ dict(label=str(READ), _class="action-btn", url=str(URL(r=request, args = args)))
462+ ]
463+
464+ return
465+#def get_options_for_questions(template_id):
466+# questions = db((db.survey_template_link.survey_template_id == template_id) & \
467+# (db.survey_question.id == db.survey_template_link.survey_question_id)).select(db.survey_question.ALL)
468+# opt_map = {}
469+# for question in questions:
470+# question_type = question.question_type
471+# if question_type == 6: # Single TF -- simple for now -- will introduce more types later.
472+# opt_map[question.id] = {"allow_comments":question.allow_comments,\
473+# "comment_display_label":question.comment_display_label,\
474+# "required":question.required}
475+#
476+# elif question_type == 9:
477+# opt_map[question.id] = { "allow_comments":question.allow_comments,\
478+# "comment_display_label":question.comment_display_label,\
479+# "required":question.required}
480+# elif question_type == 10:
481+# opt_map[question.id] = {"allow_comments":question.allow_comments,\
482+# "comment_display_label":question.comment_display_label,\
483+# "required":question.required}
484+#
485+# elif question_type == 11:
486+# opt_map[question.id] = {"allow_comments":question.allow_comments,\
487+# "comment_display_label":question.comment_display_label,\
488+# "required":question.required}
489+# return opt_map
490\ No newline at end of file
491
492=== modified file 'models/000_config.py'
493--- models/000_config.py 2010-09-02 00:15:55 +0000
494+++ models/000_config.py 2010-09-02 01:18:42 +0000
495@@ -1,288 +1,295 @@
496-# -*- coding: utf-8 -*-
497-
498-"""
499- Deployment settings
500- All settings which are typically edited for a deployment should be done here
501- Deployers shouldn't typically need to edit any other files.
502-"""
503-
504-def Tstr(text):
505- """
506- Convenience function for non-Web2Py modules
507- (latest web2py no longer needs this)
508- """
509- return str(T(text))
510-
511-s3cfg = local_import("s3cfg")
512-deployment_settings = s3cfg.S3Config(T)
513-
514-# Database settings
515-deployment_settings.database.db_type = "sqlite"
516-deployment_settings.database.host = "localhost"
517-deployment_settings.database.port = "" # use default
518-deployment_settings.database.database = "sahana"
519-deployment_settings.database.username = "sahana"
520-deployment_settings.database.password = "password"
521-deployment_settings.database.pool_size = 30
522-
523-# Authentication settings
524-# This setting should be changed _before_ registering the 1st user
525-deployment_settings.auth.hmac_key = "akeytochange"
526-# These settings should be changed _after_ the 1st (admin) user is
527-# registered in order to secure the deployment
528-deployment_settings.auth.registration_requires_verification = False
529-deployment_settings.auth.registration_requires_approval = False
530-deployment_settings.auth.openid = False
531-
532-# Base settings
533-# Set this to the Public URL of the instance
534-deployment_settings.base.public_url = "http://127.0.0.1:8000"
535-
536-# Switch to "False" in Production for a Performance gain
537-# (need to set to "True" again when Table definitions are changed)
538-deployment_settings.base.migrate = True
539-
540-# Enable/disable pre-population of the database.
541-# Set to False during first run for manual DB migration in case this
542-# is explicitly required for a code upgrade, otherwise leave at True
543-# NOTE: the web UI will not be accessible while the DB is empty,
544-# instead run:
545-# python web2py.py -S eden -M
546-# to create the db structure, then exit and re-import the data.
547-deployment_settings.base.prepopulate = True
548-
549-# Email settings
550-# Outbound server
551-deployment_settings.mail.server = "127.0.0.1:25"
552-# Useful for Windows Laptops:
553-#deployment_settings.mail.server = "smtp.gmail.com:587"
554-#deployment_settings.mail.login = "username:password"
555-# From Address
556-deployment_settings.mail.sender = "'Sahana' <sahana@your.org>"
557-# Address to which mails get sent to approve new users
558-deployment_settings.mail.approver = "useradmin@your.org"
559-
560-# L10n settings
561-# Uncomment this if the deployment is just in a few countries
562-# (used in the GIS Location Selector & maybe in future: Messaging)
563-#deployment_settings.L10n.countries = ["PK"]
564-# Languages used in the deployment (used for Language Toolbar & GIS Locations)
565-# http://www.loc.gov/standards/iso639-2/php/code_list.php
566-deployment_settings.L10n.languages = {
567- "en":T("English"),
568- "es":T("Spanish"),
569- "pa":T("Punjabi"),
570- "ps":T("Pashto"),
571- "sd":T("Sindhi"),
572- "ur":T("Urdu"),
573- "zh-tw":T("Chinese (Taiwan)"),
574- "seraiki":T("Seraiki"),
575- "balochi":T("Balochi"),
576-}
577-# Default language for Language Toolbar (& GIS Locations in future)
578-deployment_settings.L10n.default_language = "en"
579-# Display the language toolbar
580-deployment_settings.L10n.display_toolbar = True
581-# Default timezone for users
582-deployment_settings.L10n.utc_offset = "UTC +0000"
583-
584-# GIS (Map) settings
585-# Provide a tool to select locations via a map on all forms with location_id
586-deployment_settings.gis.map_selector = True
587-# Display Resources recorded to Admin-Level Locations on the map
588-deployment_settings.gis.display_L0 = False
589-# Currently unused
590-#deployment_settings.gis.display_L1 = True
591-# Allow non-MapAdmins to edit Admin locations?
592-# (defaults to True, if not set)
593-deployment_settings.gis.edit_L0 = False
594-deployment_settings.gis.edit_L1 = True
595-#deployment_settings.gis.edit_L2 = True
596-deployment_settings.gis.locations_hierarchy = {
597- "L0":T("Country"),
598- "L1":T("Province"),
599- "L2":T("District"),
600- "L3":T("Town"),
601- "L4":T("Village")
602-}
603-# Do we have a spatial DB available? (currently unused. Will support PostGIS & Spatialite.)
604-deployment_settings.gis.spatialdb = False
605-# GeoServer (currently unused. Will allow REST control of GeoServer.)
606-deployment_settings.gis.geoserver_url = "http://localhost/geoserver"
607-deployment_settings.gis.geoserver_username = "admin"
608-deployment_settings.gis.geoserver_password = "password"
609-
610-
611-# Security Policy settings
612-# Lock-down access to Map Editing
613-#deployment_settings.security.map = True
614-# Currently unused
615-#deployment_settings.security.policy = 2 # Editor
616-
617-# Comment/uncomment modules here to disable/enable them
618-# Modules menu is defined in 01_menu.py
619-from gluon.storage import Storage
620-deployment_settings.modules = Storage(
621- default = Storage(
622- name_nice = Tstr("Home"),
623- access = None, # All Users (inc Anonymous) can see this module in the default menu & access the controller
624- module_type = 0 # This item is always 1st in the menu
625- ),
626- admin = Storage(
627- name_nice = Tstr("Administration"),
628- description = Tstr("Site Administration"),
629- access = "|1|", # Only Administrators can see this module in the default menu & access the controller
630- module_type = 0 # This item is handled separately in the menu
631- ),
632- gis = Storage(
633- name_nice = Tstr("Map"),
634- description = Tstr("Situation Awareness & Geospatial Analysis"),
635- module_type = 1, # 1st item in the menu
636- resources = Storage(
637- gis_location = {'importer' : True}
638- )
639- ),
640- mpr = Storage(
641- name_nice = Tstr("Missing Persons"),
642- description = Tstr("Helps to report and search for Missing Persons"),
643- module_type = 2,
644- ),
645- rms = Storage(
646- name_nice = Tstr("Requests"),
647- description = Tstr("Tracks requests for aid and matches them against donors who have pledged aid"),
648- module_type = 3,
649- resources = Storage(
650- rms_req = {'importer' : True},
651- )
652- ),
653- hms = Storage(
654- name_nice = Tstr("Hospitals"),
655- description = Tstr("Helps to monitor status of hospitals"),
656- module_type = 4,
657- resources = Storage(
658- hms_hospital = {'importer' : True}
659- )
660- ),
661- vol = Storage(
662- name_nice = Tstr("Volunteers"),
663- description = Tstr("Manage volunteers by capturing their skills, availability and allocation"),
664- module_type = 5,
665- ),
666- logs = Storage(
667- name_nice = Tstr("Logistics Management"),
668- description = Tstr("Managing, Storing and Distributing Relief Items"),
669- module_type = 10
670- ),
671- project = Storage(
672- name_nice = Tstr("Project Management"),
673- description = Tstr("Project Activities"),
674- module_type = 10
675- ),
676- msg = Storage(
677- name_nice = Tstr("Messaging"),
678- description = Tstr("Sends & Receives Alerts via Email & SMS"),
679- module_type = 10,
680- ),
681- flood = Storage(
682- name_nice = Tstr("Flood Alerts"),
683- description = Tstr("Flood Alerts show water levels in various parts of the country"),
684- module_type = 10
685- ),
686- sitrep = Storage(
687- name_nice = Tstr("Assessments"),
688- description = Tstr("Assessments are structured reports done by Professional Organisations - data includes WFP Assessments"),
689- module_type = 10
690- ),
691- rat = Storage(
692- name_nice = Tstr("Rapid Assessments"),
693- description = Tstr("Assessments are structured reports done by Professional Organisations"),
694- module_type = 10
695- ),
696- pr = Storage(
697- name_nice = Tstr("Person Registry"),
698- description = Tstr("Central point to record details on People"),
699- module_type = 10,
700- resources = Storage(
701- pr_address = {'importer' : True},
702- pr_pe_contact = {'importer' : True},
703- pr_presence = {'importer' : True},
704- pr_identity = {'importer' : True},
705- pr_person = {'importer' : True},
706- pr_group = {'importer' : True},
707- pr_group_membership = {'importer' : True},
708- )
709- ),
710- dvi = Storage(
711- name_nice = Tstr("Disaster Victim Identification"),
712- description = Tstr("Disaster Victim Identification"),
713- module_type = 10,
714- resources = Storage(
715- dvi_recreq = {'importer' : True},
716- )
717- ),
718- #dvr = Storage(
719- # name_nice = Tstr("Disaster Victim Registry"),
720- # description = Tstr("Traces internally displaced people (IDPs) and their needs"),
721- # module_type = 10
722- # ),
723- budget = Storage(
724- name_nice = Tstr("Budgeting Module"),
725- description = Tstr("Allows a Budget to be drawn up"),
726- module_type = 10,
727- resources = Storage(
728- budget_item = {'importer' : True},
729- budget_kit = {'importer' : True},
730- budget_bundle = {'importer' : True},
731- )
732- ),
733- cr = Storage(
734- name_nice = Tstr("Shelter Registry"),
735- description = Tstr("Tracks the location, distibution, capacity and breakdown of victims in Shelters"),
736- module_type = 10,
737- resources = Storage(
738- cr_shelter = {'importer' : True }
739- )
740- ),
741- delphi = Storage(
742- name_nice = Tstr("Delphi Decision Maker"),
743- description = Tstr("Supports the decision making of large groups of Crisis Management Experts by helping the groups create ranked list."),
744- module_type = 10,
745- ),
746- doc = Storage(
747- name_nice = Tstr("Documents and Photos"),
748- description = Tstr("A library of digital resources, such as photos, documents and reports"),
749- module_type = 10,
750- ),
751- irs = Storage(
752- name_nice = Tstr("Incident Reporting"),
753- description = Tstr("Incident Reporting System"),
754- module_type = 10
755- ),
756- org = Storage(
757- name_nice = Tstr("Organization Registry"),
758- description = Tstr('Lists "who is doing what & where". Allows relief agencies to coordinate their activities'),
759- module_type = 10,
760- resources = Storage(
761- org_organisation = {'importer' : True},
762- org_office = {'importer' : True},
763- org_project = {'importer' : True},
764- org_staff = {'importer' : True},
765- org_task = {'importer' : True}
766- )
767- ),
768- ticket = Storage(
769- name_nice = Tstr("Ticketing Module"),
770- description = Tstr("Master Message Log to process incoming reports & requests"),
771- module_type = 10,
772- ),
773- importer = Storage(
774- name_nice = "Spreadsheet Importer",
775- description = "Used to import data from spreadsheets into the database",
776- module_type = 10,
777- )
778- #lms = Storage(
779- # name_nice = Tstr("Logistics Management System"),
780- # description = Tstr("An intake system, a warehouse management system, commodity tracking, supply chain management, procurement and other asset and resource management capabilities."),
781- # module_type = 10
782- # ),
783-)
784\ No newline at end of file
785+
786+# -*- coding: utf-8 -*-
787+
788+"""
789+ Deployment settings
790+ All settings which are typically edited for a deployment should be done here
791+ Deployers shouldn't typically need to edit any other files.
792+"""
793+
794+def Tstr(text):
795+ """
796+ Convenience function for non-Web2Py modules
797+ (latest web2py no longer needs this)
798+ """
799+ return str(T(text))
800+
801+s3cfg = local_import("s3cfg")
802+deployment_settings = s3cfg.S3Config(T)
803+
804+# Database settings
805+deployment_settings.database.db_type = "sqlite"
806+deployment_settings.database.host = "localhost"
807+deployment_settings.database.port = "" # use default
808+deployment_settings.database.database = "sahana"
809+deployment_settings.database.username = "sahana"
810+deployment_settings.database.password = "password"
811+deployment_settings.database.pool_size = 30
812+
813+# Authentication settings
814+# This setting should be changed _before_ registering the 1st user
815+deployment_settings.auth.hmac_key = "akeytochange"
816+# These settings should be changed _after_ the 1st (admin) user is
817+# registered in order to secure the deployment
818+deployment_settings.auth.registration_requires_verification = False
819+deployment_settings.auth.registration_requires_approval = False
820+deployment_settings.auth.openid = False
821+
822+# Base settings
823+# Set this to the Public URL of the instance
824+deployment_settings.base.public_url = "http://127.0.0.1:8000"
825+
826+# Switch to "False" in Production for a Performance gain
827+# (need to set to "True" again when Table definitions are changed)
828+deployment_settings.base.migrate = True
829+
830+# Enable/disable pre-population of the database.
831+# Set to False during first run for manual DB migration in case this
832+# is explicitly required for a code upgrade, otherwise leave at True
833+# NOTE: the web UI will not be accessible while the DB is empty,
834+# instead run:
835+# python web2py.py -S eden -M
836+# to create the db structure, then exit and re-import the data.
837+deployment_settings.base.prepopulate = True
838+
839+# Email settings
840+# Outbound server
841+deployment_settings.mail.server = "127.0.0.1:25"
842+# Useful for Windows Laptops:
843+#deployment_settings.mail.server = "smtp.gmail.com:587"
844+#deployment_settings.mail.login = "username:password"
845+# From Address
846+deployment_settings.mail.sender = "'Sahana' <sahana@your.org>"
847+# Address to which mails get sent to approve new users
848+deployment_settings.mail.approver = "useradmin@your.org"
849+
850+# L10n settings
851+# Uncomment this if the deployment is just in a few countries
852+# (used in the GIS Location Selector & maybe in future: Messaging)
853+#deployment_settings.L10n.countries = ["PK"]
854+# Languages used in the deployment (used for Language Toolbar & GIS Locations)
855+# http://www.loc.gov/standards/iso639-2/php/code_list.php
856+deployment_settings.L10n.languages = {
857+ "en":T("English"),
858+ "es":T("Spanish"),
859+ "pa":T("Punjabi"),
860+ "ps":T("Pashto"),
861+ "sd":T("Sindhi"),
862+ "ur":T("Urdu"),
863+ "zh-tw":T("Chinese (Taiwan)"),
864+ "seraiki":T("Seraiki"),
865+ "balochi":T("Balochi"),
866+}
867+# Default language for Language Toolbar (& GIS Locations in future)
868+deployment_settings.L10n.default_language = "en"
869+# Display the language toolbar
870+deployment_settings.L10n.display_toolbar = True
871+# Default timezone for users
872+deployment_settings.L10n.utc_offset = "UTC +0000"
873+
874+# GIS (Map) settings
875+# Provide a tool to select locations via a map on all forms with location_id
876+deployment_settings.gis.map_selector = True
877+# Display Resources recorded to Admin-Level Locations on the map
878+deployment_settings.gis.display_L0 = False
879+# Currently unused
880+#deployment_settings.gis.display_L1 = True
881+# Allow non-MapAdmins to edit Admin locations?
882+# (defaults to True, if not set)
883+deployment_settings.gis.edit_L0 = False
884+deployment_settings.gis.edit_L1 = True
885+#deployment_settings.gis.edit_L2 = True
886+deployment_settings.gis.locations_hierarchy = {
887+ "L0":T("Country"),
888+ "L1":T("Province"),
889+ "L2":T("District"),
890+ "L3":T("Town"),
891+ "L4":T("Village")
892+}
893+# Do we have a spatial DB available? (currently unused. Will support PostGIS & Spatialite.)
894+deployment_settings.gis.spatialdb = False
895+# GeoServer (currently unused. Will allow REST control of GeoServer.)
896+deployment_settings.gis.geoserver_url = "http://localhost/geoserver"
897+deployment_settings.gis.geoserver_username = "admin"
898+deployment_settings.gis.geoserver_password = "password"
899+
900+
901+# Security Policy settings
902+# Lock-down access to Map Editing
903+#deployment_settings.security.map = True
904+# Currently unused
905+#deployment_settings.security.policy = 2 # Editor
906+
907+# Comment/uncomment modules here to disable/enable them
908+# Modules menu is defined in 01_menu.py
909+from gluon.storage import Storage
910+deployment_settings.modules = Storage(
911+ default = Storage(
912+ name_nice = Tstr("Home"),
913+ access = None, # All Users (inc Anonymous) can see this module in the default menu & access the controller
914+ module_type = 0 # This item is always 1st in the menu
915+ ),
916+ admin = Storage(
917+ name_nice = Tstr("Administration"),
918+ description = Tstr("Site Administration"),
919+ access = "|1|", # Only Administrators can see this module in the default menu & access the controller
920+ module_type = 0 # This item is handled separately in the menu
921+ ),
922+ gis = Storage(
923+ name_nice = Tstr("Map"),
924+ description = Tstr("Situation Awareness & Geospatial Analysis"),
925+ module_type = 1, # 1st item in the menu
926+ resources = Storage(
927+ gis_location = {'importer' : True}
928+ )
929+ ),
930+ mpr = Storage(
931+ name_nice = Tstr("Missing Persons"),
932+ description = Tstr("Helps to report and search for Missing Persons"),
933+ module_type = 2,
934+ ),
935+ rms = Storage(
936+ name_nice = Tstr("Requests"),
937+ description = Tstr("Tracks requests for aid and matches them against donors who have pledged aid"),
938+ module_type = 3,
939+ resources = Storage(
940+ rms_req = {'importer' : True},
941+ )
942+ ),
943+ hms = Storage(
944+ name_nice = Tstr("Hospitals"),
945+ description = Tstr("Helps to monitor status of hospitals"),
946+ module_type = 4,
947+ resources = Storage(
948+ hms_hospital = {'importer' : True}
949+ )
950+ ),
951+ vol = Storage(
952+ name_nice = Tstr("Volunteers"),
953+ description = Tstr("Manage volunteers by capturing their skills, availability and allocation"),
954+ module_type = 5,
955+ ),
956+ logs = Storage(
957+ name_nice = Tstr("Logistics Management"),
958+ description = Tstr("Managing, Storing and Distributing Relief Items"),
959+ module_type = 10
960+ ),
961+ project = Storage(
962+ name_nice = Tstr("Project Management"),
963+ description = Tstr("Project Activities"),
964+ module_type = 10
965+ ),
966+ msg = Storage(
967+ name_nice = Tstr("Messaging"),
968+ description = Tstr("Sends & Receives Alerts via Email & SMS"),
969+ module_type = 10,
970+ ),
971+ flood = Storage(
972+ name_nice = Tstr("Flood Alerts"),
973+ description = Tstr("Flood Alerts show water levels in various parts of the country"),
974+ module_type = 10
975+ ),
976+ sitrep = Storage(
977+ name_nice = Tstr("Assessments"),
978+ description = Tstr("Assessments are structured reports done by Professional Organisations - data includes WFP Assessments"),
979+ module_type = 10
980+ ),
981+ rat = Storage(
982+ name_nice = Tstr("Rapid Assessments"),
983+ description = Tstr("Assessments are structured reports done by Professional Organisations"),
984+ module_type = 10
985+ ),
986+ pr = Storage(
987+ name_nice = Tstr("Person Registry"),
988+ description = Tstr("Central point to record details on People"),
989+ module_type = 10,
990+ resources = Storage(
991+ pr_address = {'importer' : True},
992+ pr_pe_contact = {'importer' : True},
993+ pr_presence = {'importer' : True},
994+ pr_identity = {'importer' : True},
995+ pr_person = {'importer' : True},
996+ pr_group = {'importer' : True},
997+ pr_group_membership = {'importer' : True},
998+ )
999+ ),
1000+ dvi = Storage(
1001+ name_nice = Tstr("Disaster Victim Identification"),
1002+ description = Tstr("Disaster Victim Identification"),
1003+ module_type = 10,
1004+ resources = Storage(
1005+ dvi_recreq = {'importer' : True},
1006+ )
1007+ ),
1008+ #dvr = Storage(
1009+ # name_nice = Tstr("Disaster Victim Registry"),
1010+ # description = Tstr("Traces internally displaced people (IDPs) and their needs"),
1011+ # module_type = 10
1012+ # ),
1013+ budget = Storage(
1014+ name_nice = Tstr("Budgeting Module"),
1015+ description = Tstr("Allows a Budget to be drawn up"),
1016+ module_type = 10,
1017+ resources = Storage(
1018+ budget_item = {'importer' : True},
1019+ budget_kit = {'importer' : True},
1020+ budget_bundle = {'importer' : True},
1021+ )
1022+ ),
1023+ cr = Storage(
1024+ name_nice = Tstr("Shelter Registry"),
1025+ description = Tstr("Tracks the location, distibution, capacity and breakdown of victims in Shelters"),
1026+ module_type = 10,
1027+ resources = Storage(
1028+ cr_shelter = {'importer' : True }
1029+ )
1030+ ),
1031+ delphi = Storage(
1032+ name_nice = Tstr("Delphi Decision Maker"),
1033+ description = Tstr("Supports the decision making of large groups of Crisis Management Experts by helping the groups create ranked list."),
1034+ module_type = 10,
1035+ ),
1036+ doc = Storage(
1037+ name_nice = Tstr("Documents and Photos"),
1038+ description = Tstr("A library of digital resources, such as photos, documents and reports"),
1039+ module_type = 10,
1040+ ),
1041+ irs = Storage(
1042+ name_nice = Tstr("Incident Reporting"),
1043+ description = Tstr("Incident Reporting System"),
1044+ module_type = 10
1045+ ),
1046+ org = Storage(
1047+ name_nice = Tstr("Organization Registry"),
1048+ description = Tstr('Lists "who is doing what & where". Allows relief agencies to coordinate their activities'),
1049+ module_type = 10,
1050+ resources = Storage(
1051+ org_organisation = {'importer' : True},
1052+ org_office = {'importer' : True},
1053+ org_project = {'importer' : True},
1054+ org_staff = {'importer' : True},
1055+ org_task = {'importer' : True}
1056+ )
1057+ ),
1058+ ticket = Storage(
1059+ name_nice = Tstr("Ticketing Module"),
1060+ description = Tstr("Master Message Log to process incoming reports & requests"),
1061+ module_type = 10,
1062+ ),
1063+ importer = Storage(
1064+ name_nice = "Spreadsheet Importer",
1065+ description = "Used to import data from spreadsheets into the database",
1066+ module_type = 10,
1067+ ),
1068+ survey = Storage(
1069+ name_nice = "Survey Module",
1070+ description = "Create, enter, and manage surveys.",
1071+ module_type = 10,
1072+ )
1073+
1074+ #lms = Storage(
1075+ # name_nice = Tstr("Logistics Management System"),
1076+ # description = Tstr("An intake system, a warehouse management system, commodity tracking, supply chain management, procurement and other asset and resource management capabilities."),
1077+ # module_type = 10
1078+ # ),
1079+)
1080
1081=== modified file 'models/msg.py' (properties changed: +x to -x)
1082=== added file 'models/survey.py'
1083--- models/survey.py 1970-01-01 00:00:00 +0000
1084+++ models/survey.py 2010-09-02 01:18:42 +0000
1085@@ -0,0 +1,142 @@
1086+# -*- coding: utf-8 -*-
1087+
1088+"""
1089+ Survey Module
1090+
1091+ @author: Robert O'Connor
1092+"""
1093+
1094+module = "survey"
1095+
1096+from gluon.sqlhtml import *
1097+
1098+
1099+ADD_LOCATION = T("Add Location")
1100+repr_select = lambda l: len(l.name) > 48 and "%s..." % l.name[:44] or l.name
1101+location_id = db.Table(None, "location_id",
1102+ FieldS3("location_id", db.gis_location, sortby="name",
1103+ requires = IS_NULL_OR(IS_ONE_OF(db, "gis_location.id", repr_select, sort=True)),
1104+ represent = lambda id: shn_gis_location_represent(id),
1105+ label = T("Location"),
1106+ comment = DIV(SPAN("* ", _class="req"),A(ADD_LOCATION,
1107+ _class="colorbox",
1108+ _href=URL(r=request, c="gis", f="location", args="create", vars=dict(format="popup")),
1109+ _target="top",
1110+ _title=ADD_LOCATION),
1111+ DIV( _class="tooltip",
1112+ _title=Tstr("Location") + "|" + Tstr("The Location of this Site, which can be general (for Reporting) or precise (for displaying on a Map)."))),
1113+ ondelete = "RESTRICT"))
1114+
1115+s3xrc.model.configure(db.gis_location,
1116+ onvalidation=lambda form: gis.wkt_centroid(form),
1117+ onaccept=gis.update_location_tree())
1118+
1119+
1120+if deployment_settings.has_module(module):
1121+
1122+ #Reusable table
1123+ name_desc = db.Table(db,timestamp, uuidstamp, deletion_status, authorstamp,
1124+ Field("name", "string", default="", length=120),
1125+ Field("description", "text", default="",length=500))
1126+
1127+ #Survey Template
1128+ resource = "template"
1129+ tablename = module + "_" + resource
1130+ template = db.define_table(tablename,name_desc,
1131+ Field("table_name","string",readable=False,writable=False),
1132+ Field("locked","boolean",readable=False,writable=False),
1133+ person_id,
1134+ organisation_id)
1135+
1136+ # Survey Series
1137+ resource = "series"
1138+ tablename = module + "_" + resource
1139+ series = db.define_table(tablename,name_desc,
1140+ Field("survey_template_id", db.survey_template),
1141+ Field("from_date", "date", default=None),
1142+ Field("to_date","date",default=None),
1143+ location_id)
1144+
1145+ # Survey Section
1146+ resource = "questions"
1147+ tablename = module +"_" + resource
1148+ section = db.define_table(tablename,uuidstamp,deletion_status,authorstamp)
1149+
1150+# # Question options e.g., Row choices, Column Choices, Layout Configuration data, etc...
1151+# resource = "question_options"
1152+# tablename = module +"_" + resource
1153+# question_options = db.define_table(tablename,uuidstamp,deletion_status,authorstamp,
1154+## # Field("display_option","integer"),
1155+### Field("answer_choices","text",length=700),
1156+### Field("row_choices","text"), # row choices
1157+### Field("column_choices","text"), # column choices
1158+## Field("tf_choices","text"), # text before the text fields.
1159+# Field("tf_ta_columns","integer"), # number of columns for TF/TA
1160+# Field("ta_rows","integer"), # number of rows for text areas
1161+### Field("number_of_options","integer"),
1162+# Field("allow_comments","boolean"), # whether or not to allow comments
1163+# Field("comment_display_label"), # the label for the comment field
1164+# Field("required","boolean"), # marks the question as required
1165+## Field("validate","boolean"), # whether or not to enable validation
1166+### Field("validation_options","integer"), # pre-set validation regexps and such.
1167+# Field("aggregation_type","string"))
1168+
1169+ # Survey Question
1170+ resource = "question"
1171+ tablename = module +"_" + resource
1172+ question = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
1173+ Field("name", "string", default="", length=120),
1174+ Field("question_type","integer"),
1175+ Field("description", "text", default="",length=500))
1176+# Field("options_id",db.survey_question_options),
1177+# Field("tf_ta_columns","integer"), # number of columns for TF/TA
1178+# Field("ta_rows","integer"), # number of rows for text areas
1179+# Field("allow_comments","boolean"), # whether or not to allow comments
1180+# Field("comment_display_label"), # the label for the comment field
1181+# Field("required","boolean"), # marks the question as required
1182+# Field("aggregation_type","string"))
1183+
1184+
1185+# #Survey Instance
1186+# resource = "instance"
1187+# tablename = module +"_" + resource
1188+# instance = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
1189+# Field("survey_series_id",db.survey_series))
1190+
1191+# # Survey Answer
1192+# resource = "answer"
1193+# tablename = module +"_" + resource
1194+# answer = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
1195+# Field("survey_instance_id",db.survey_instance),
1196+# Field("question_id",db.survey_question),
1197+# Field("answer_value","text",length=600),
1198+# Field("answer_image","upload"), # store the image if "Image" is selected.
1199+# Field("answer_location",db.gis_location),
1200+# Field("answer_person",db.pr_person),
1201+# Field("answer_organisation",db.org_organisation))
1202+
1203+ # Link table
1204+ resource = "template_link"
1205+ tablename = module +"_" + resource
1206+ link_table = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
1207+ Field("survey_question_id",db.survey_question),
1208+ Field("survey_template_id", db.survey_template),
1209+ Field("survey_questions_id", db.survey_questions))
1210+ link_table.survey_question_id.requires =IS_NULL_OR(IS_ONE_OF(db, "survey_question.id", "%(name)s"))
1211+
1212+
1213+# def question_options_onaccept(form):
1214+# if form.vars.id and session.rcvars.survey_question:
1215+# table = db.survey_question_options
1216+# opts = db(table.id == form.vars.id).update(question_id=session.rcvars.survey_question)
1217+# db.commit()
1218+#
1219+# s3xrc.model.configure(db.survey_question_options,
1220+# onaccept=lambda form: question_options_onaccept(form))
1221+
1222+# def question_onaccept(form):
1223+# if form.vars.id and session.rcvars.survey_section and session.rcvars.survey_template:
1224+# db.survey_template_link_table.insert(survey_section_id=session.rcvars.survey_section, survey_template_id=session.rcvars.survey_template)
1225+# db.commit()
1226+# s3xrc.model.configure(db.survey_question,
1227+# onaccept=lambda form: question_onaccept(form))
1228\ No newline at end of file
1229
1230=== modified file 'modules/validators.py'
1231--- modules/validators.py 2010-08-15 21:47:42 +0000
1232+++ modules/validators.py 2010-09-02 01:18:42 +0000
1233@@ -489,4 +489,4 @@
1234
1235 def formatter(self, value):
1236 # Always format with trailing UTC offset
1237- return value.strftime(str(self.format)) + " +0000"
1238+ return value.strftime(str(self.format)) + " +0000"
1239\ No newline at end of file
1240
1241=== modified file 'modules/widgets.py'
1242--- modules/widgets.py 2010-08-25 11:30:41 +0000
1243+++ modules/widgets.py 2010-09-02 01:18:42 +0000
1244@@ -619,4 +619,4 @@
1245 else:
1246 return_value = return_value
1247
1248- return return_value
1249+ return return_value
1250\ No newline at end of file
1251
1252=== added directory 'static/img/tango'
1253=== added file 'static/img/tango/down.png'
1254Binary files static/img/tango/down.png 1970-01-01 00:00:00 +0000 and static/img/tango/down.png 2010-09-02 01:18:42 +0000 differ
1255=== added file 'static/img/tango/minus.png'
1256Binary files static/img/tango/minus.png 1970-01-01 00:00:00 +0000 and static/img/tango/minus.png 2010-09-02 01:18:42 +0000 differ
1257=== added file 'static/img/tango/plus.png'
1258Binary files static/img/tango/plus.png 1970-01-01 00:00:00 +0000 and static/img/tango/plus.png 2010-09-02 01:18:42 +0000 differ
1259=== added file 'static/img/tango/up.png'
1260Binary files static/img/tango/up.png 1970-01-01 00:00:00 +0000 and static/img/tango/up.png 2010-09-02 01:18:42 +0000 differ
1261=== modified file 'static/scripts/S3/S3.js' (properties changed: +x to -x)
1262=== added directory 'static/scripts/survey'
1263=== added file 'static/scripts/survey/jquery-ui-1.8.4.custom.js'
1264--- static/scripts/survey/jquery-ui-1.8.4.custom.js 1970-01-01 00:00:00 +0000
1265+++ static/scripts/survey/jquery-ui-1.8.4.custom.js 2010-09-02 01:18:42 +0000
1266@@ -0,0 +1,11241 @@
1267+/*!
1268+ * jQuery UI 1.8.4
1269+ *
1270+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1271+ * Dual licensed under the MIT or GPL Version 2 licenses.
1272+ * http://jquery.org/license
1273+ *
1274+ * http://docs.jquery.com/UI
1275+ */
1276+(function( $, undefined ) {
1277+
1278+// prevent duplicate loading
1279+// this is only a problem because we proxy existing functions
1280+// and we don't want to double proxy them
1281+$.ui = $.ui || {};
1282+if ( $.ui.version ) {
1283+ return;
1284+}
1285+
1286+//Helper functions and ui object
1287+$.extend( $.ui, {
1288+ version: "1.8.4",
1289+
1290+ // $.ui.plugin is deprecated. Use the proxy pattern instead.
1291+ plugin: {
1292+ add: function( module, option, set ) {
1293+ var proto = $.ui[ module ].prototype;
1294+ for ( var i in set ) {
1295+ proto.plugins[ i ] = proto.plugins[ i ] || [];
1296+ proto.plugins[ i ].push( [ option, set[ i ] ] );
1297+ }
1298+ },
1299+ call: function( instance, name, args ) {
1300+ var set = instance.plugins[ name ];
1301+ if ( !set || !instance.element[ 0 ].parentNode ) {
1302+ return;
1303+ }
1304+
1305+ for ( var i = 0; i < set.length; i++ ) {
1306+ if ( instance.options[ set[ i ][ 0 ] ] ) {
1307+ set[ i ][ 1 ].apply( instance.element, args );
1308+ }
1309+ }
1310+ }
1311+ },
1312+
1313+ contains: function( a, b ) {
1314+ return document.compareDocumentPosition ?
1315+ a.compareDocumentPosition( b ) & 16 :
1316+ a !== b && a.contains( b );
1317+ },
1318+
1319+ hasScroll: function( el, a ) {
1320+
1321+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
1322+ if ( $( el ).css( "overflow" ) === "hidden") {
1323+ return false;
1324+ }
1325+
1326+ var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
1327+ has = false;
1328+
1329+ if ( el[ scroll ] > 0 ) {
1330+ return true;
1331+ }
1332+
1333+ // TODO: determine which cases actually cause this to happen
1334+ // if the element doesn't have the scroll set, see if it's possible to
1335+ // set the scroll
1336+ el[ scroll ] = 1;
1337+ has = ( el[ scroll ] > 0 );
1338+ el[ scroll ] = 0;
1339+ return has;
1340+ },
1341+
1342+ isOverAxis: function( x, reference, size ) {
1343+ //Determines when x coordinate is over "b" element axis
1344+ return ( x > reference ) && ( x < ( reference + size ) );
1345+ },
1346+
1347+ isOver: function( y, x, top, left, height, width ) {
1348+ //Determines when x, y coordinates is over "b" element
1349+ return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
1350+ },
1351+
1352+ keyCode: {
1353+ ALT: 18,
1354+ BACKSPACE: 8,
1355+ CAPS_LOCK: 20,
1356+ COMMA: 188,
1357+ COMMAND: 91,
1358+ COMMAND_LEFT: 91, // COMMAND
1359+ COMMAND_RIGHT: 93,
1360+ CONTROL: 17,
1361+ DELETE: 46,
1362+ DOWN: 40,
1363+ END: 35,
1364+ ENTER: 13,
1365+ ESCAPE: 27,
1366+ HOME: 36,
1367+ INSERT: 45,
1368+ LEFT: 37,
1369+ MENU: 93, // COMMAND_RIGHT
1370+ NUMPAD_ADD: 107,
1371+ NUMPAD_DECIMAL: 110,
1372+ NUMPAD_DIVIDE: 111,
1373+ NUMPAD_ENTER: 108,
1374+ NUMPAD_MULTIPLY: 106,
1375+ NUMPAD_SUBTRACT: 109,
1376+ PAGE_DOWN: 34,
1377+ PAGE_UP: 33,
1378+ PERIOD: 190,
1379+ RIGHT: 39,
1380+ SHIFT: 16,
1381+ SPACE: 32,
1382+ TAB: 9,
1383+ UP: 38,
1384+ WINDOWS: 91 // COMMAND
1385+ }
1386+});
1387+
1388+//jQuery plugins
1389+$.fn.extend({
1390+ _focus: $.fn.focus,
1391+ focus: function( delay, fn ) {
1392+ return typeof delay === "number" ?
1393+ this.each(function() {
1394+ var elem = this;
1395+ setTimeout(function() {
1396+ $( elem ).focus();
1397+ if ( fn ) {
1398+ fn.call( elem );
1399+ }
1400+ }, delay );
1401+ }) :
1402+ this._focus.apply( this, arguments );
1403+ },
1404+
1405+ enableSelection: function() {
1406+ return this
1407+ .attr( "unselectable", "off" )
1408+ .css( "MozUserSelect", "" );
1409+ },
1410+
1411+ disableSelection: function() {
1412+ return this
1413+ .attr( "unselectable", "on" )
1414+ .css( "MozUserSelect", "none" );
1415+ },
1416+
1417+ scrollParent: function() {
1418+ var scrollParent;
1419+ if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
1420+ scrollParent = this.parents().filter(function() {
1421+ return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
1422+ }).eq(0);
1423+ } else {
1424+ scrollParent = this.parents().filter(function() {
1425+ return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
1426+ }).eq(0);
1427+ }
1428+
1429+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
1430+ },
1431+
1432+ zIndex: function( zIndex ) {
1433+ if ( zIndex !== undefined ) {
1434+ return this.css( "zIndex", zIndex );
1435+ }
1436+
1437+ if ( this.length ) {
1438+ var elem = $( this[ 0 ] ), position, value;
1439+ while ( elem.length && elem[ 0 ] !== document ) {
1440+ // Ignore z-index if position is set to a value where z-index is ignored by the browser
1441+ // This makes behavior of this function consistent across browsers
1442+ // WebKit always returns auto if the element is positioned
1443+ position = elem.css( "position" );
1444+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
1445+ // IE returns 0 when zIndex is not specified
1446+ // other browsers return a string
1447+ // we ignore the case of nested elements with an explicit value of 0
1448+ // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
1449+ value = parseInt( elem.css( "zIndex" ) );
1450+ if ( !isNaN( value ) && value != 0 ) {
1451+ return value;
1452+ }
1453+ }
1454+ elem = elem.parent();
1455+ }
1456+ }
1457+
1458+ return 0;
1459+ }
1460+});
1461+
1462+$.each( [ "Width", "Height" ], function( i, name ) {
1463+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
1464+ type = name.toLowerCase(),
1465+ orig = {
1466+ innerWidth: $.fn.innerWidth,
1467+ innerHeight: $.fn.innerHeight,
1468+ outerWidth: $.fn.outerWidth,
1469+ outerHeight: $.fn.outerHeight
1470+ };
1471+
1472+ function reduce( elem, size, border, margin ) {
1473+ $.each( side, function() {
1474+ size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
1475+ if ( border ) {
1476+ size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
1477+ }
1478+ if ( margin ) {
1479+ size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
1480+ }
1481+ });
1482+ return size;
1483+ }
1484+
1485+ $.fn[ "inner" + name ] = function( size ) {
1486+ if ( size === undefined ) {
1487+ return orig[ "inner" + name ].call( this );
1488+ }
1489+
1490+ return this.each(function() {
1491+ $.style( this, type, reduce( this, size ) + "px" );
1492+ });
1493+ };
1494+
1495+ $.fn[ "outer" + name] = function( size, margin ) {
1496+ if ( typeof size !== "number" ) {
1497+ return orig[ "outer" + name ].call( this, size );
1498+ }
1499+
1500+ return this.each(function() {
1501+ $.style( this, type, reduce( this, size, true, margin ) + "px" );
1502+ });
1503+ };
1504+});
1505+
1506+//Additional selectors
1507+function visible( element ) {
1508+ return !$( element ).parents().andSelf().filter(function() {
1509+ return $.curCSS( this, "visibility" ) === "hidden" ||
1510+ $.expr.filters.hidden( this );
1511+ }).length;
1512+}
1513+
1514+$.extend( $.expr[ ":" ], {
1515+ data: function( elem, i, match ) {
1516+ return !!$.data( elem, match[ 3 ] );
1517+ },
1518+
1519+ focusable: function( element ) {
1520+ var nodeName = element.nodeName.toLowerCase(),
1521+ tabIndex = $.attr( element, "tabindex" );
1522+ if ( "area" === nodeName ) {
1523+ var map = element.parentNode,
1524+ mapName = map.name,
1525+ img;
1526+ if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
1527+ return false;
1528+ }
1529+ img = $( "img[usemap=#" + mapName + "]" )[0];
1530+ return !!img && visible( img );
1531+ }
1532+ return ( /input|select|textarea|button|object/.test( nodeName )
1533+ ? !element.disabled
1534+ : "a" == nodeName
1535+ ? element.href || !isNaN( tabIndex )
1536+ : !isNaN( tabIndex ))
1537+ // the element and all of its ancestors must be visible
1538+ && visible( element );
1539+ },
1540+
1541+ tabbable: function( element ) {
1542+ var tabIndex = $.attr( element, "tabindex" );
1543+ return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" );
1544+ }
1545+});
1546+
1547+})( jQuery );
1548+/*!
1549+ * jQuery UI Widget 1.8.4
1550+ *
1551+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1552+ * Dual licensed under the MIT or GPL Version 2 licenses.
1553+ * http://jquery.org/license
1554+ *
1555+ * http://docs.jquery.com/UI/Widget
1556+ */
1557+(function( $, undefined ) {
1558+
1559+var _remove = $.fn.remove;
1560+
1561+$.fn.remove = function( selector, keepData ) {
1562+ return this.each(function() {
1563+ if ( !keepData ) {
1564+ if ( !selector || $.filter( selector, [ this ] ).length ) {
1565+ $( "*", this ).add( [ this ] ).each(function() {
1566+ $( this ).triggerHandler( "remove" );
1567+ });
1568+ }
1569+ }
1570+ return _remove.call( $(this), selector, keepData );
1571+ });
1572+};
1573+
1574+$.widget = function( name, base, prototype ) {
1575+ var namespace = name.split( "." )[ 0 ],
1576+ fullName;
1577+ name = name.split( "." )[ 1 ];
1578+ fullName = namespace + "-" + name;
1579+
1580+ if ( !prototype ) {
1581+ prototype = base;
1582+ base = $.Widget;
1583+ }
1584+
1585+ // create selector for plugin
1586+ $.expr[ ":" ][ fullName ] = function( elem ) {
1587+ return !!$.data( elem, name );
1588+ };
1589+
1590+ $[ namespace ] = $[ namespace ] || {};
1591+ $[ namespace ][ name ] = function( options, element ) {
1592+ // allow instantiation without initializing for simple inheritance
1593+ if ( arguments.length ) {
1594+ this._createWidget( options, element );
1595+ }
1596+ };
1597+
1598+ var basePrototype = new base();
1599+ // we need to make the options hash a property directly on the new instance
1600+ // otherwise we'll modify the options hash on the prototype that we're
1601+ // inheriting from
1602+// $.each( basePrototype, function( key, val ) {
1603+// if ( $.isPlainObject(val) ) {
1604+// basePrototype[ key ] = $.extend( {}, val );
1605+// }
1606+// });
1607+ basePrototype.options = $.extend( true, {}, basePrototype.options );
1608+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
1609+ namespace: namespace,
1610+ widgetName: name,
1611+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
1612+ widgetBaseClass: fullName
1613+ }, prototype );
1614+
1615+ $.widget.bridge( name, $[ namespace ][ name ] );
1616+};
1617+
1618+$.widget.bridge = function( name, object ) {
1619+ $.fn[ name ] = function( options ) {
1620+ var isMethodCall = typeof options === "string",
1621+ args = Array.prototype.slice.call( arguments, 1 ),
1622+ returnValue = this;
1623+
1624+ // allow multiple hashes to be passed on init
1625+ options = !isMethodCall && args.length ?
1626+ $.extend.apply( null, [ true, options ].concat(args) ) :
1627+ options;
1628+
1629+ // prevent calls to internal methods
1630+ if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
1631+ return returnValue;
1632+ }
1633+
1634+ if ( isMethodCall ) {
1635+ this.each(function() {
1636+ var instance = $.data( this, name ),
1637+ methodValue = instance && $.isFunction( instance[options] ) ?
1638+ instance[ options ].apply( instance, args ) :
1639+ instance;
1640+ if ( methodValue !== instance && methodValue !== undefined ) {
1641+ returnValue = methodValue;
1642+ return false;
1643+ }
1644+ });
1645+ } else {
1646+ this.each(function() {
1647+ var instance = $.data( this, name );
1648+ if ( instance ) {
1649+ if ( options ) {
1650+ instance.option( options );
1651+ }
1652+ instance._init();
1653+ } else {
1654+ $.data( this, name, new object( options, this ) );
1655+ }
1656+ });
1657+ }
1658+
1659+ return returnValue;
1660+ };
1661+};
1662+
1663+$.Widget = function( options, element ) {
1664+ // allow instantiation without initializing for simple inheritance
1665+ if ( arguments.length ) {
1666+ this._createWidget( options, element );
1667+ }
1668+};
1669+
1670+$.Widget.prototype = {
1671+ widgetName: "widget",
1672+ widgetEventPrefix: "",
1673+ options: {
1674+ disabled: false
1675+ },
1676+ _createWidget: function( options, element ) {
1677+ // $.widget.bridge stores the plugin instance, but we do it anyway
1678+ // so that it's stored even before the _create function runs
1679+ $.data( element, this.widgetName, this );
1680+ this.element = $( element );
1681+ this.options = $.extend( true, {},
1682+ this.options,
1683+ $.metadata && $.metadata.get( element )[ this.widgetName ],
1684+ options );
1685+
1686+ var self = this;
1687+ this.element.bind( "remove." + this.widgetName, function() {
1688+ self.destroy();
1689+ });
1690+
1691+ this._create();
1692+ this._init();
1693+ },
1694+ _create: function() {},
1695+ _init: function() {},
1696+
1697+ destroy: function() {
1698+ this.element
1699+ .unbind( "." + this.widgetName )
1700+ .removeData( this.widgetName );
1701+ this.widget()
1702+ .unbind( "." + this.widgetName )
1703+ .removeAttr( "aria-disabled" )
1704+ .removeClass(
1705+ this.widgetBaseClass + "-disabled " +
1706+ "ui-state-disabled" );
1707+ },
1708+
1709+ widget: function() {
1710+ return this.element;
1711+ },
1712+
1713+ option: function( key, value ) {
1714+ var options = key,
1715+ self = this;
1716+
1717+ if ( arguments.length === 0 ) {
1718+ // don't return a reference to the internal hash
1719+ return $.extend( {}, self.options );
1720+ }
1721+
1722+ if (typeof key === "string" ) {
1723+ if ( value === undefined ) {
1724+ return this.options[ key ];
1725+ }
1726+ options = {};
1727+ options[ key ] = value;
1728+ }
1729+
1730+ $.each( options, function( key, value ) {
1731+ self._setOption( key, value );
1732+ });
1733+
1734+ return self;
1735+ },
1736+ _setOption: function( key, value ) {
1737+ this.options[ key ] = value;
1738+
1739+ if ( key === "disabled" ) {
1740+ this.widget()
1741+ [ value ? "addClass" : "removeClass"](
1742+ this.widgetBaseClass + "-disabled" + " " +
1743+ "ui-state-disabled" )
1744+ .attr( "aria-disabled", value );
1745+ }
1746+
1747+ return this;
1748+ },
1749+
1750+ enable: function() {
1751+ return this._setOption( "disabled", false );
1752+ },
1753+ disable: function() {
1754+ return this._setOption( "disabled", true );
1755+ },
1756+
1757+ _trigger: function( type, event, data ) {
1758+ var callback = this.options[ type ];
1759+
1760+ event = $.Event( event );
1761+ event.type = ( type === this.widgetEventPrefix ?
1762+ type :
1763+ this.widgetEventPrefix + type ).toLowerCase();
1764+ data = data || {};
1765+
1766+ // copy original event properties over to the new event
1767+ // this would happen if we could call $.event.fix instead of $.Event
1768+ // but we don't have a way to force an event to be fixed multiple times
1769+ if ( event.originalEvent ) {
1770+ for ( var i = $.event.props.length, prop; i; ) {
1771+ prop = $.event.props[ --i ];
1772+ event[ prop ] = event.originalEvent[ prop ];
1773+ }
1774+ }
1775+
1776+ this.element.trigger( event, data );
1777+
1778+ return !( $.isFunction(callback) &&
1779+ callback.call( this.element[0], event, data ) === false ||
1780+ event.isDefaultPrevented() );
1781+ }
1782+};
1783+
1784+})( jQuery );
1785+/*!
1786+ * jQuery UI Mouse 1.8.4
1787+ *
1788+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1789+ * Dual licensed under the MIT or GPL Version 2 licenses.
1790+ * http://jquery.org/license
1791+ *
1792+ * http://docs.jquery.com/UI/Mouse
1793+ *
1794+ * Depends:
1795+ * jquery.ui.widget.js
1796+ */
1797+(function( $, undefined ) {
1798+
1799+$.widget("ui.mouse", {
1800+ options: {
1801+ cancel: ':input,option',
1802+ distance: 1,
1803+ delay: 0
1804+ },
1805+ _mouseInit: function() {
1806+ var self = this;
1807+
1808+ this.element
1809+ .bind('mousedown.'+this.widgetName, function(event) {
1810+ return self._mouseDown(event);
1811+ })
1812+ .bind('click.'+this.widgetName, function(event) {
1813+ if(self._preventClickEvent) {
1814+ self._preventClickEvent = false;
1815+ event.stopImmediatePropagation();
1816+ return false;
1817+ }
1818+ });
1819+
1820+ this.started = false;
1821+ },
1822+
1823+ // TODO: make sure destroying one instance of mouse doesn't mess with
1824+ // other instances of mouse
1825+ _mouseDestroy: function() {
1826+ this.element.unbind('.'+this.widgetName);
1827+ },
1828+
1829+ _mouseDown: function(event) {
1830+ // don't let more than one widget handle mouseStart
1831+ // TODO: figure out why we have to use originalEvent
1832+ event.originalEvent = event.originalEvent || {};
1833+ if (event.originalEvent.mouseHandled) { return; }
1834+
1835+ // we may have missed mouseup (out of window)
1836+ (this._mouseStarted && this._mouseUp(event));
1837+
1838+ this._mouseDownEvent = event;
1839+
1840+ var self = this,
1841+ btnIsLeft = (event.which == 1),
1842+ elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
1843+ if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
1844+ return true;
1845+ }
1846+
1847+ this.mouseDelayMet = !this.options.delay;
1848+ if (!this.mouseDelayMet) {
1849+ this._mouseDelayTimer = setTimeout(function() {
1850+ self.mouseDelayMet = true;
1851+ }, this.options.delay);
1852+ }
1853+
1854+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
1855+ this._mouseStarted = (this._mouseStart(event) !== false);
1856+ if (!this._mouseStarted) {
1857+ event.preventDefault();
1858+ return true;
1859+ }
1860+ }
1861+
1862+ // these delegates are required to keep context
1863+ this._mouseMoveDelegate = function(event) {
1864+ return self._mouseMove(event);
1865+ };
1866+ this._mouseUpDelegate = function(event) {
1867+ return self._mouseUp(event);
1868+ };
1869+ $(document)
1870+ .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
1871+ .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
1872+
1873+ // preventDefault() is used to prevent the selection of text here -
1874+ // however, in Safari, this causes select boxes not to be selectable
1875+ // anymore, so this fix is needed
1876+ ($.browser.safari || event.preventDefault());
1877+
1878+ event.originalEvent.mouseHandled = true;
1879+ return true;
1880+ },
1881+
1882+ _mouseMove: function(event) {
1883+ // IE mouseup check - mouseup happened when mouse was out of window
1884+ if ($.browser.msie && !event.button) {
1885+ return this._mouseUp(event);
1886+ }
1887+
1888+ if (this._mouseStarted) {
1889+ this._mouseDrag(event);
1890+ return event.preventDefault();
1891+ }
1892+
1893+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
1894+ this._mouseStarted =
1895+ (this._mouseStart(this._mouseDownEvent, event) !== false);
1896+ (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
1897+ }
1898+
1899+ return !this._mouseStarted;
1900+ },
1901+
1902+ _mouseUp: function(event) {
1903+ $(document)
1904+ .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
1905+ .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
1906+
1907+ if (this._mouseStarted) {
1908+ this._mouseStarted = false;
1909+ this._preventClickEvent = (event.target == this._mouseDownEvent.target);
1910+ this._mouseStop(event);
1911+ }
1912+
1913+ return false;
1914+ },
1915+
1916+ _mouseDistanceMet: function(event) {
1917+ return (Math.max(
1918+ Math.abs(this._mouseDownEvent.pageX - event.pageX),
1919+ Math.abs(this._mouseDownEvent.pageY - event.pageY)
1920+ ) >= this.options.distance
1921+ );
1922+ },
1923+
1924+ _mouseDelayMet: function(event) {
1925+ return this.mouseDelayMet;
1926+ },
1927+
1928+ // These are placeholder methods, to be overriden by extending plugin
1929+ _mouseStart: function(event) {},
1930+ _mouseDrag: function(event) {},
1931+ _mouseStop: function(event) {},
1932+ _mouseCapture: function(event) { return true; }
1933+});
1934+
1935+})(jQuery);
1936+/*
1937+ * jQuery UI Position 1.8.4
1938+ *
1939+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1940+ * Dual licensed under the MIT or GPL Version 2 licenses.
1941+ * http://jquery.org/license
1942+ *
1943+ * http://docs.jquery.com/UI/Position
1944+ */
1945+(function( $, undefined ) {
1946+
1947+$.ui = $.ui || {};
1948+
1949+var horizontalPositions = /left|center|right/,
1950+ horizontalDefault = "center",
1951+ verticalPositions = /top|center|bottom/,
1952+ verticalDefault = "center",
1953+ _position = $.fn.position,
1954+ _offset = $.fn.offset;
1955+
1956+$.fn.position = function( options ) {
1957+ if ( !options || !options.of ) {
1958+ return _position.apply( this, arguments );
1959+ }
1960+
1961+ // make a copy, we don't want to modify arguments
1962+ options = $.extend( {}, options );
1963+
1964+ var target = $( options.of ),
1965+ collision = ( options.collision || "flip" ).split( " " ),
1966+ offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
1967+ targetWidth,
1968+ targetHeight,
1969+ basePosition;
1970+
1971+ if ( options.of.nodeType === 9 ) {
1972+ targetWidth = target.width();
1973+ targetHeight = target.height();
1974+ basePosition = { top: 0, left: 0 };
1975+ } else if ( options.of.scrollTo && options.of.document ) {
1976+ targetWidth = target.width();
1977+ targetHeight = target.height();
1978+ basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
1979+ } else if ( options.of.preventDefault ) {
1980+ // force left top to allow flipping
1981+ options.at = "left top";
1982+ targetWidth = targetHeight = 0;
1983+ basePosition = { top: options.of.pageY, left: options.of.pageX };
1984+ } else {
1985+ targetWidth = target.outerWidth();
1986+ targetHeight = target.outerHeight();
1987+ basePosition = target.offset();
1988+ }
1989+
1990+ // force my and at to have valid horizontal and veritcal positions
1991+ // if a value is missing or invalid, it will be converted to center
1992+ $.each( [ "my", "at" ], function() {
1993+ var pos = ( options[this] || "" ).split( " " );
1994+ if ( pos.length === 1) {
1995+ pos = horizontalPositions.test( pos[0] ) ?
1996+ pos.concat( [verticalDefault] ) :
1997+ verticalPositions.test( pos[0] ) ?
1998+ [ horizontalDefault ].concat( pos ) :
1999+ [ horizontalDefault, verticalDefault ];
2000+ }
2001+ pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : horizontalDefault;
2002+ pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : verticalDefault;
2003+ options[ this ] = pos;
2004+ });
2005+
2006+ // normalize collision option
2007+ if ( collision.length === 1 ) {
2008+ collision[ 1 ] = collision[ 0 ];
2009+ }
2010+
2011+ // normalize offset option
2012+ offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
2013+ if ( offset.length === 1 ) {
2014+ offset[ 1 ] = offset[ 0 ];
2015+ }
2016+ offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
2017+
2018+ if ( options.at[0] === "right" ) {
2019+ basePosition.left += targetWidth;
2020+ } else if (options.at[0] === horizontalDefault ) {
2021+ basePosition.left += targetWidth / 2;
2022+ }
2023+
2024+ if ( options.at[1] === "bottom" ) {
2025+ basePosition.top += targetHeight;
2026+ } else if ( options.at[1] === verticalDefault ) {
2027+ basePosition.top += targetHeight / 2;
2028+ }
2029+
2030+ basePosition.left += offset[ 0 ];
2031+ basePosition.top += offset[ 1 ];
2032+
2033+ return this.each(function() {
2034+ var elem = $( this ),
2035+ elemWidth = elem.outerWidth(),
2036+ elemHeight = elem.outerHeight(),
2037+ position = $.extend( {}, basePosition );
2038+
2039+ if ( options.my[0] === "right" ) {
2040+ position.left -= elemWidth;
2041+ } else if ( options.my[0] === horizontalDefault ) {
2042+ position.left -= elemWidth / 2;
2043+ }
2044+
2045+ if ( options.my[1] === "bottom" ) {
2046+ position.top -= elemHeight;
2047+ } else if ( options.my[1] === verticalDefault ) {
2048+ position.top -= elemHeight / 2;
2049+ }
2050+
2051+ // prevent fractions (see #5280)
2052+ position.left = parseInt( position.left );
2053+ position.top = parseInt( position.top );
2054+
2055+ $.each( [ "left", "top" ], function( i, dir ) {
2056+ if ( $.ui.position[ collision[i] ] ) {
2057+ $.ui.position[ collision[i] ][ dir ]( position, {
2058+ targetWidth: targetWidth,
2059+ targetHeight: targetHeight,
2060+ elemWidth: elemWidth,
2061+ elemHeight: elemHeight,
2062+ offset: offset,
2063+ my: options.my,
2064+ at: options.at
2065+ });
2066+ }
2067+ });
2068+
2069+ if ( $.fn.bgiframe ) {
2070+ elem.bgiframe();
2071+ }
2072+ elem.offset( $.extend( position, { using: options.using } ) );
2073+ });
2074+};
2075+
2076+$.ui.position = {
2077+ fit: {
2078+ left: function( position, data ) {
2079+ var win = $( window ),
2080+ over = position.left + data.elemWidth - win.width() - win.scrollLeft();
2081+ position.left = over > 0 ? position.left - over : Math.max( 0, position.left );
2082+ },
2083+ top: function( position, data ) {
2084+ var win = $( window ),
2085+ over = position.top + data.elemHeight - win.height() - win.scrollTop();
2086+ position.top = over > 0 ? position.top - over : Math.max( 0, position.top );
2087+ }
2088+ },
2089+
2090+ flip: {
2091+ left: function( position, data ) {
2092+ if ( data.at[0] === "center" ) {
2093+ return;
2094+ }
2095+ var win = $( window ),
2096+ over = position.left + data.elemWidth - win.width() - win.scrollLeft(),
2097+ myOffset = data.my[ 0 ] === "left" ?
2098+ -data.elemWidth :
2099+ data.my[ 0 ] === "right" ?
2100+ data.elemWidth :
2101+ 0,
2102+ offset = -2 * data.offset[ 0 ];
2103+ position.left += position.left < 0 ?
2104+ myOffset + data.targetWidth + offset :
2105+ over > 0 ?
2106+ myOffset - data.targetWidth + offset :
2107+ 0;
2108+ },
2109+ top: function( position, data ) {
2110+ if ( data.at[1] === "center" ) {
2111+ return;
2112+ }
2113+ var win = $( window ),
2114+ over = position.top + data.elemHeight - win.height() - win.scrollTop(),
2115+ myOffset = data.my[ 1 ] === "top" ?
2116+ -data.elemHeight :
2117+ data.my[ 1 ] === "bottom" ?
2118+ data.elemHeight :
2119+ 0,
2120+ atOffset = data.at[ 1 ] === "top" ?
2121+ data.targetHeight :
2122+ -data.targetHeight,
2123+ offset = -2 * data.offset[ 1 ];
2124+ position.top += position.top < 0 ?
2125+ myOffset + data.targetHeight + offset :
2126+ over > 0 ?
2127+ myOffset + atOffset + offset :
2128+ 0;
2129+ }
2130+ }
2131+};
2132+
2133+// offset setter from jQuery 1.4
2134+if ( !$.offset.setOffset ) {
2135+ $.offset.setOffset = function( elem, options ) {
2136+ // set position first, in-case top/left are set even on static elem
2137+ if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
2138+ elem.style.position = "relative";
2139+ }
2140+ var curElem = $( elem ),
2141+ curOffset = curElem.offset(),
2142+ curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
2143+ curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
2144+ props = {
2145+ top: (options.top - curOffset.top) + curTop,
2146+ left: (options.left - curOffset.left) + curLeft
2147+ };
2148+
2149+ if ( 'using' in options ) {
2150+ options.using.call( elem, props );
2151+ } else {
2152+ curElem.css( props );
2153+ }
2154+ };
2155+
2156+ $.fn.offset = function( options ) {
2157+ var elem = this[ 0 ];
2158+ if ( !elem || !elem.ownerDocument ) { return null; }
2159+ if ( options ) {
2160+ return this.each(function() {
2161+ $.offset.setOffset( this, options );
2162+ });
2163+ }
2164+ return _offset.call( this );
2165+ };
2166+}
2167+
2168+}( jQuery ));
2169+/*
2170+ * jQuery UI Draggable 1.8.4
2171+ *
2172+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
2173+ * Dual licensed under the MIT or GPL Version 2 licenses.
2174+ * http://jquery.org/license
2175+ *
2176+ * http://docs.jquery.com/UI/Draggables
2177+ *
2178+ * Depends:
2179+ * jquery.ui.core.js
2180+ * jquery.ui.mouse.js
2181+ * jquery.ui.widget.js
2182+ */
2183+(function( $, undefined ) {
2184+
2185+$.widget("ui.draggable", $.ui.mouse, {
2186+ widgetEventPrefix: "drag",
2187+ options: {
2188+ addClasses: true,
2189+ appendTo: "parent",
2190+ axis: false,
2191+ connectToSortable: false,
2192+ containment: false,
2193+ cursor: "auto",
2194+ cursorAt: false,
2195+ grid: false,
2196+ handle: false,
2197+ helper: "original",
2198+ iframeFix: false,
2199+ opacity: false,
2200+ refreshPositions: false,
2201+ revert: false,
2202+ revertDuration: 500,
2203+ scope: "default",
2204+ scroll: true,
2205+ scrollSensitivity: 20,
2206+ scrollSpeed: 20,
2207+ snap: false,
2208+ snapMode: "both",
2209+ snapTolerance: 20,
2210+ stack: false,
2211+ zIndex: false
2212+ },
2213+ _create: function() {
2214+
2215+ if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
2216+ this.element[0].style.position = 'relative';
2217+
2218+ (this.options.addClasses && this.element.addClass("ui-draggable"));
2219+ (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
2220+
2221+ this._mouseInit();
2222+
2223+ },
2224+
2225+ destroy: function() {
2226+ if(!this.element.data('draggable')) return;
2227+ this.element
2228+ .removeData("draggable")
2229+ .unbind(".draggable")
2230+ .removeClass("ui-draggable"
2231+ + " ui-draggable-dragging"
2232+ + " ui-draggable-disabled");
2233+ this._mouseDestroy();
2234+
2235+ return this;
2236+ },
2237+
2238+ _mouseCapture: function(event) {
2239+
2240+ var o = this.options;
2241+
2242+ // among others, prevent a drag on a resizable-handle
2243+ if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
2244+ return false;
2245+
2246+ //Quit if we're not on a valid handle
2247+ this.handle = this._getHandle(event);
2248+ if (!this.handle)
2249+ return false;
2250+
2251+ return true;
2252+
2253+ },
2254+
2255+ _mouseStart: function(event) {
2256+
2257+ var o = this.options;
2258+
2259+ //Create and append the visible helper
2260+ this.helper = this._createHelper(event);
2261+
2262+ //Cache the helper size
2263+ this._cacheHelperProportions();
2264+
2265+ //If ddmanager is used for droppables, set the global draggable
2266+ if($.ui.ddmanager)
2267+ $.ui.ddmanager.current = this;
2268+
2269+ /*
2270+ * - Position generation -
2271+ * This block generates everything position related - it's the core of draggables.
2272+ */
2273+
2274+ //Cache the margins of the original element
2275+ this._cacheMargins();
2276+
2277+ //Store the helper's css position
2278+ this.cssPosition = this.helper.css("position");
2279+ this.scrollParent = this.helper.scrollParent();
2280+
2281+ //The element's absolute position on the page minus margins
2282+ this.offset = this.positionAbs = this.element.offset();
2283+ this.offset = {
2284+ top: this.offset.top - this.margins.top,
2285+ left: this.offset.left - this.margins.left
2286+ };
2287+
2288+ $.extend(this.offset, {
2289+ click: { //Where the click happened, relative to the element
2290+ left: event.pageX - this.offset.left,
2291+ top: event.pageY - this.offset.top
2292+ },
2293+ parent: this._getParentOffset(),
2294+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
2295+ });
2296+
2297+ //Generate the original position
2298+ this.originalPosition = this.position = this._generatePosition(event);
2299+ this.originalPageX = event.pageX;
2300+ this.originalPageY = event.pageY;
2301+
2302+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
2303+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
2304+
2305+ //Set a containment if given in the options
2306+ if(o.containment)
2307+ this._setContainment();
2308+
2309+ //Trigger event + callbacks
2310+ if(this._trigger("start", event) === false) {
2311+ this._clear();
2312+ return false;
2313+ }
2314+
2315+ //Recache the helper size
2316+ this._cacheHelperProportions();
2317+
2318+ //Prepare the droppable offsets
2319+ if ($.ui.ddmanager && !o.dropBehaviour)
2320+ $.ui.ddmanager.prepareOffsets(this, event);
2321+
2322+ this.helper.addClass("ui-draggable-dragging");
2323+ this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
2324+ return true;
2325+ },
2326+
2327+ _mouseDrag: function(event, noPropagation) {
2328+
2329+ //Compute the helpers position
2330+ this.position = this._generatePosition(event);
2331+ this.positionAbs = this._convertPositionTo("absolute");
2332+
2333+ //Call plugins and callbacks and use the resulting position if something is returned
2334+ if (!noPropagation) {
2335+ var ui = this._uiHash();
2336+ if(this._trigger('drag', event, ui) === false) {
2337+ this._mouseUp({});
2338+ return false;
2339+ }
2340+ this.position = ui.position;
2341+ }
2342+
2343+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
2344+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
2345+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
2346+
2347+ return false;
2348+ },
2349+
2350+ _mouseStop: function(event) {
2351+
2352+ //If we are using droppables, inform the manager about the drop
2353+ var dropped = false;
2354+ if ($.ui.ddmanager && !this.options.dropBehaviour)
2355+ dropped = $.ui.ddmanager.drop(this, event);
2356+
2357+ //if a drop comes from outside (a sortable)
2358+ if(this.dropped) {
2359+ dropped = this.dropped;
2360+ this.dropped = false;
2361+ }
2362+
2363+ //if the original element is removed, don't bother to continue
2364+ if(!this.element[0] || !this.element[0].parentNode)
2365+ return false;
2366+
2367+ if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
2368+ var self = this;
2369+ $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
2370+ if(self._trigger("stop", event) !== false) {
2371+ self._clear();
2372+ }
2373+ });
2374+ } else {
2375+ if(this._trigger("stop", event) !== false) {
2376+ this._clear();
2377+ }
2378+ }
2379+
2380+ return false;
2381+ },
2382+
2383+ cancel: function() {
2384+
2385+ if(this.helper.is(".ui-draggable-dragging")) {
2386+ this._mouseUp({});
2387+ } else {
2388+ this._clear();
2389+ }
2390+
2391+ return this;
2392+
2393+ },
2394+
2395+ _getHandle: function(event) {
2396+
2397+ var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
2398+ $(this.options.handle, this.element)
2399+ .find("*")
2400+ .andSelf()
2401+ .each(function() {
2402+ if(this == event.target) handle = true;
2403+ });
2404+
2405+ return handle;
2406+
2407+ },
2408+
2409+ _createHelper: function(event) {
2410+
2411+ var o = this.options;
2412+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
2413+
2414+ if(!helper.parents('body').length)
2415+ helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
2416+
2417+ if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
2418+ helper.css("position", "absolute");
2419+
2420+ return helper;
2421+
2422+ },
2423+
2424+ _adjustOffsetFromHelper: function(obj) {
2425+ if (typeof obj == 'string') {
2426+ obj = obj.split(' ');
2427+ }
2428+ if ($.isArray(obj)) {
2429+ obj = {left: +obj[0], top: +obj[1] || 0};
2430+ }
2431+ if ('left' in obj) {
2432+ this.offset.click.left = obj.left + this.margins.left;
2433+ }
2434+ if ('right' in obj) {
2435+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
2436+ }
2437+ if ('top' in obj) {
2438+ this.offset.click.top = obj.top + this.margins.top;
2439+ }
2440+ if ('bottom' in obj) {
2441+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
2442+ }
2443+ },
2444+
2445+ _getParentOffset: function() {
2446+
2447+ //Get the offsetParent and cache its position
2448+ this.offsetParent = this.helper.offsetParent();
2449+ var po = this.offsetParent.offset();
2450+
2451+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
2452+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
2453+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
2454+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
2455+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
2456+ po.left += this.scrollParent.scrollLeft();
2457+ po.top += this.scrollParent.scrollTop();
2458+ }
2459+
2460+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
2461+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
2462+ po = { top: 0, left: 0 };
2463+
2464+ return {
2465+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
2466+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
2467+ };
2468+
2469+ },
2470+
2471+ _getRelativeOffset: function() {
2472+
2473+ if(this.cssPosition == "relative") {
2474+ var p = this.element.position();
2475+ return {
2476+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
2477+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
2478+ };
2479+ } else {
2480+ return { top: 0, left: 0 };
2481+ }
2482+
2483+ },
2484+
2485+ _cacheMargins: function() {
2486+ this.margins = {
2487+ left: (parseInt(this.element.css("marginLeft"),10) || 0),
2488+ top: (parseInt(this.element.css("marginTop"),10) || 0)
2489+ };
2490+ },
2491+
2492+ _cacheHelperProportions: function() {
2493+ this.helperProportions = {
2494+ width: this.helper.outerWidth(),
2495+ height: this.helper.outerHeight()
2496+ };
2497+ },
2498+
2499+ _setContainment: function() {
2500+
2501+ var o = this.options;
2502+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
2503+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
2504+ 0 - this.offset.relative.left - this.offset.parent.left,
2505+ 0 - this.offset.relative.top - this.offset.parent.top,
2506+ $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
2507+ ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
2508+ ];
2509+
2510+ if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
2511+ var ce = $(o.containment)[0]; if(!ce) return;
2512+ var co = $(o.containment).offset();
2513+ var over = ($(ce).css("overflow") != 'hidden');
2514+
2515+ this.containment = [
2516+ co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
2517+ co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
2518+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
2519+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
2520+ ];
2521+ } else if(o.containment.constructor == Array) {
2522+ this.containment = o.containment;
2523+ }
2524+
2525+ },
2526+
2527+ _convertPositionTo: function(d, pos) {
2528+
2529+ if(!pos) pos = this.position;
2530+ var mod = d == "absolute" ? 1 : -1;
2531+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
2532+
2533+ return {
2534+ top: (
2535+ pos.top // The absolute mouse position
2536+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
2537+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
2538+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
2539+ ),
2540+ left: (
2541+ pos.left // The absolute mouse position
2542+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
2543+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
2544+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
2545+ )
2546+ };
2547+
2548+ },
2549+
2550+ _generatePosition: function(event) {
2551+
2552+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
2553+ var pageX = event.pageX;
2554+ var pageY = event.pageY;
2555+
2556+ /*
2557+ * - Position constraining -
2558+ * Constrain the position to a mix of grid, containment.
2559+ */
2560+
2561+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
2562+
2563+ if(this.containment) {
2564+ if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
2565+ if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
2566+ if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
2567+ if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
2568+ }
2569+
2570+ if(o.grid) {
2571+ var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
2572+ pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
2573+
2574+ var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
2575+ pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
2576+ }
2577+
2578+ }
2579+
2580+ return {
2581+ top: (
2582+ pageY // The absolute mouse position
2583+ - this.offset.click.top // Click offset (relative to the element)
2584+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
2585+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
2586+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
2587+ ),
2588+ left: (
2589+ pageX // The absolute mouse position
2590+ - this.offset.click.left // Click offset (relative to the element)
2591+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
2592+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
2593+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
2594+ )
2595+ };
2596+
2597+ },
2598+
2599+ _clear: function() {
2600+ this.helper.removeClass("ui-draggable-dragging");
2601+ if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
2602+ //if($.ui.ddmanager) $.ui.ddmanager.current = null;
2603+ this.helper = null;
2604+ this.cancelHelperRemoval = false;
2605+ },
2606+
2607+ // From now on bulk stuff - mainly helpers
2608+
2609+ _trigger: function(type, event, ui) {
2610+ ui = ui || this._uiHash();
2611+ $.ui.plugin.call(this, type, [event, ui]);
2612+ if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
2613+ return $.Widget.prototype._trigger.call(this, type, event, ui);
2614+ },
2615+
2616+ plugins: {},
2617+
2618+ _uiHash: function(event) {
2619+ return {
2620+ helper: this.helper,
2621+ position: this.position,
2622+ originalPosition: this.originalPosition,
2623+ offset: this.positionAbs
2624+ };
2625+ }
2626+
2627+});
2628+
2629+$.extend($.ui.draggable, {
2630+ version: "1.8.4"
2631+});
2632+
2633+$.ui.plugin.add("draggable", "connectToSortable", {
2634+ start: function(event, ui) {
2635+
2636+ var inst = $(this).data("draggable"), o = inst.options,
2637+ uiSortable = $.extend({}, ui, { item: inst.element });
2638+ inst.sortables = [];
2639+ $(o.connectToSortable).each(function() {
2640+ var sortable = $.data(this, 'sortable');
2641+ if (sortable && !sortable.options.disabled) {
2642+ inst.sortables.push({
2643+ instance: sortable,
2644+ shouldRevert: sortable.options.revert
2645+ });
2646+ sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
2647+ sortable._trigger("activate", event, uiSortable);
2648+ }
2649+ });
2650+
2651+ },
2652+ stop: function(event, ui) {
2653+
2654+ //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
2655+ var inst = $(this).data("draggable"),
2656+ uiSortable = $.extend({}, ui, { item: inst.element });
2657+
2658+ $.each(inst.sortables, function() {
2659+ if(this.instance.isOver) {
2660+
2661+ this.instance.isOver = 0;
2662+
2663+ inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
2664+ this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
2665+
2666+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
2667+ if(this.shouldRevert) this.instance.options.revert = true;
2668+
2669+ //Trigger the stop of the sortable
2670+ this.instance._mouseStop(event);
2671+
2672+ this.instance.options.helper = this.instance.options._helper;
2673+
2674+ //If the helper has been the original item, restore properties in the sortable
2675+ if(inst.options.helper == 'original')
2676+ this.instance.currentItem.css({ top: 'auto', left: 'auto' });
2677+
2678+ } else {
2679+ this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
2680+ this.instance._trigger("deactivate", event, uiSortable);
2681+ }
2682+
2683+ });
2684+
2685+ },
2686+ drag: function(event, ui) {
2687+
2688+ var inst = $(this).data("draggable"), self = this;
2689+
2690+ var checkPos = function(o) {
2691+ var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
2692+ var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
2693+ var itemHeight = o.height, itemWidth = o.width;
2694+ var itemTop = o.top, itemLeft = o.left;
2695+
2696+ return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
2697+ };
2698+
2699+ $.each(inst.sortables, function(i) {
2700+
2701+ //Copy over some variables to allow calling the sortable's native _intersectsWith
2702+ this.instance.positionAbs = inst.positionAbs;
2703+ this.instance.helperProportions = inst.helperProportions;
2704+ this.instance.offset.click = inst.offset.click;
2705+
2706+ if(this.instance._intersectsWith(this.instance.containerCache)) {
2707+
2708+ //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
2709+ if(!this.instance.isOver) {
2710+
2711+ this.instance.isOver = 1;
2712+ //Now we fake the start of dragging for the sortable instance,
2713+ //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
2714+ //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
2715+ this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
2716+ this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
2717+ this.instance.options.helper = function() { return ui.helper[0]; };
2718+
2719+ event.target = this.instance.currentItem[0];
2720+ this.instance._mouseCapture(event, true);
2721+ this.instance._mouseStart(event, true, true);
2722+
2723+ //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
2724+ this.instance.offset.click.top = inst.offset.click.top;
2725+ this.instance.offset.click.left = inst.offset.click.left;
2726+ this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
2727+ this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
2728+
2729+ inst._trigger("toSortable", event);
2730+ inst.dropped = this.instance.element; //draggable revert needs that
2731+ //hack so receive/update callbacks work (mostly)
2732+ inst.currentItem = inst.element;
2733+ this.instance.fromOutside = inst;
2734+
2735+ }
2736+
2737+ //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
2738+ if(this.instance.currentItem) this.instance._mouseDrag(event);
2739+
2740+ } else {
2741+
2742+ //If it doesn't intersect with the sortable, and it intersected before,
2743+ //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
2744+ if(this.instance.isOver) {
2745+
2746+ this.instance.isOver = 0;
2747+ this.instance.cancelHelperRemoval = true;
2748+
2749+ //Prevent reverting on this forced stop
2750+ this.instance.options.revert = false;
2751+
2752+ // The out event needs to be triggered independently
2753+ this.instance._trigger('out', event, this.instance._uiHash(this.instance));
2754+
2755+ this.instance._mouseStop(event, true);
2756+ this.instance.options.helper = this.instance.options._helper;
2757+
2758+ //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
2759+ this.instance.currentItem.remove();
2760+ if(this.instance.placeholder) this.instance.placeholder.remove();
2761+
2762+ inst._trigger("fromSortable", event);
2763+ inst.dropped = false; //draggable revert needs that
2764+ }
2765+
2766+ };
2767+
2768+ });
2769+
2770+ }
2771+});
2772+
2773+$.ui.plugin.add("draggable", "cursor", {
2774+ start: function(event, ui) {
2775+ var t = $('body'), o = $(this).data('draggable').options;
2776+ if (t.css("cursor")) o._cursor = t.css("cursor");
2777+ t.css("cursor", o.cursor);
2778+ },
2779+ stop: function(event, ui) {
2780+ var o = $(this).data('draggable').options;
2781+ if (o._cursor) $('body').css("cursor", o._cursor);
2782+ }
2783+});
2784+
2785+$.ui.plugin.add("draggable", "iframeFix", {
2786+ start: function(event, ui) {
2787+ var o = $(this).data('draggable').options;
2788+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
2789+ $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
2790+ .css({
2791+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
2792+ position: "absolute", opacity: "0.001", zIndex: 1000
2793+ })
2794+ .css($(this).offset())
2795+ .appendTo("body");
2796+ });
2797+ },
2798+ stop: function(event, ui) {
2799+ $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
2800+ }
2801+});
2802+
2803+$.ui.plugin.add("draggable", "opacity", {
2804+ start: function(event, ui) {
2805+ var t = $(ui.helper), o = $(this).data('draggable').options;
2806+ if(t.css("opacity")) o._opacity = t.css("opacity");
2807+ t.css('opacity', o.opacity);
2808+ },
2809+ stop: function(event, ui) {
2810+ var o = $(this).data('draggable').options;
2811+ if(o._opacity) $(ui.helper).css('opacity', o._opacity);
2812+ }
2813+});
2814+
2815+$.ui.plugin.add("draggable", "scroll", {
2816+ start: function(event, ui) {
2817+ var i = $(this).data("draggable");
2818+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
2819+ },
2820+ drag: function(event, ui) {
2821+
2822+ var i = $(this).data("draggable"), o = i.options, scrolled = false;
2823+
2824+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
2825+
2826+ if(!o.axis || o.axis != 'x') {
2827+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
2828+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
2829+ else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
2830+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
2831+ }
2832+
2833+ if(!o.axis || o.axis != 'y') {
2834+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
2835+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
2836+ else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
2837+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
2838+ }
2839+
2840+ } else {
2841+
2842+ if(!o.axis || o.axis != 'x') {
2843+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
2844+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2845+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
2846+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2847+ }
2848+
2849+ if(!o.axis || o.axis != 'y') {
2850+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
2851+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2852+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
2853+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2854+ }
2855+
2856+ }
2857+
2858+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
2859+ $.ui.ddmanager.prepareOffsets(i, event);
2860+
2861+ }
2862+});
2863+
2864+$.ui.plugin.add("draggable", "snap", {
2865+ start: function(event, ui) {
2866+
2867+ var i = $(this).data("draggable"), o = i.options;
2868+ i.snapElements = [];
2869+
2870+ $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
2871+ var $t = $(this); var $o = $t.offset();
2872+ if(this != i.element[0]) i.snapElements.push({
2873+ item: this,
2874+ width: $t.outerWidth(), height: $t.outerHeight(),
2875+ top: $o.top, left: $o.left
2876+ });
2877+ });
2878+
2879+ },
2880+ drag: function(event, ui) {
2881+
2882+ var inst = $(this).data("draggable"), o = inst.options;
2883+ var d = o.snapTolerance;
2884+
2885+ var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2886+ y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2887+
2888+ for (var i = inst.snapElements.length - 1; i >= 0; i--){
2889+
2890+ var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
2891+ t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
2892+
2893+ //Yes, I know, this is insane ;)
2894+ if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
2895+ if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2896+ inst.snapElements[i].snapping = false;
2897+ continue;
2898+ }
2899+
2900+ if(o.snapMode != 'inner') {
2901+ var ts = Math.abs(t - y2) <= d;
2902+ var bs = Math.abs(b - y1) <= d;
2903+ var ls = Math.abs(l - x2) <= d;
2904+ var rs = Math.abs(r - x1) <= d;
2905+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2906+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
2907+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
2908+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
2909+ }
2910+
2911+ var first = (ts || bs || ls || rs);
2912+
2913+ if(o.snapMode != 'outer') {
2914+ var ts = Math.abs(t - y1) <= d;
2915+ var bs = Math.abs(b - y2) <= d;
2916+ var ls = Math.abs(l - x1) <= d;
2917+ var rs = Math.abs(r - x2) <= d;
2918+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
2919+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2920+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
2921+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
2922+ }
2923+
2924+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
2925+ (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2926+ inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2927+
2928+ };
2929+
2930+ }
2931+});
2932+
2933+$.ui.plugin.add("draggable", "stack", {
2934+ start: function(event, ui) {
2935+
2936+ var o = $(this).data("draggable").options;
2937+
2938+ var group = $.makeArray($(o.stack)).sort(function(a,b) {
2939+ return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
2940+ });
2941+ if (!group.length) { return; }
2942+
2943+ var min = parseInt(group[0].style.zIndex) || 0;
2944+ $(group).each(function(i) {
2945+ this.style.zIndex = min + i;
2946+ });
2947+
2948+ this[0].style.zIndex = min + group.length;
2949+
2950+ }
2951+});
2952+
2953+$.ui.plugin.add("draggable", "zIndex", {
2954+ start: function(event, ui) {
2955+ var t = $(ui.helper), o = $(this).data("draggable").options;
2956+ if(t.css("zIndex")) o._zIndex = t.css("zIndex");
2957+ t.css('zIndex', o.zIndex);
2958+ },
2959+ stop: function(event, ui) {
2960+ var o = $(this).data("draggable").options;
2961+ if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
2962+ }
2963+});
2964+
2965+})(jQuery);
2966+/*
2967+ * jQuery UI Droppable 1.8.4
2968+ *
2969+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
2970+ * Dual licensed under the MIT or GPL Version 2 licenses.
2971+ * http://jquery.org/license
2972+ *
2973+ * http://docs.jquery.com/UI/Droppables
2974+ *
2975+ * Depends:
2976+ * jquery.ui.core.js
2977+ * jquery.ui.widget.js
2978+ * jquery.ui.mouse.js
2979+ * jquery.ui.draggable.js
2980+ */
2981+(function( $, undefined ) {
2982+
2983+$.widget("ui.droppable", {
2984+ widgetEventPrefix: "drop",
2985+ options: {
2986+ accept: '*',
2987+ activeClass: false,
2988+ addClasses: true,
2989+ greedy: false,
2990+ hoverClass: false,
2991+ scope: 'default',
2992+ tolerance: 'intersect'
2993+ },
2994+ _create: function() {
2995+
2996+ var o = this.options, accept = o.accept;
2997+ this.isover = 0; this.isout = 1;
2998+
2999+ this.accept = $.isFunction(accept) ? accept : function(d) {
3000+ return d.is(accept);
3001+ };
3002+
3003+ //Store the droppable's proportions
3004+ this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
3005+
3006+ // Add the reference and positions to the manager
3007+ $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
3008+ $.ui.ddmanager.droppables[o.scope].push(this);
3009+
3010+ (o.addClasses && this.element.addClass("ui-droppable"));
3011+
3012+ },
3013+
3014+ destroy: function() {
3015+ var drop = $.ui.ddmanager.droppables[this.options.scope];
3016+ for ( var i = 0; i < drop.length; i++ )
3017+ if ( drop[i] == this )
3018+ drop.splice(i, 1);
3019+
3020+ this.element
3021+ .removeClass("ui-droppable ui-droppable-disabled")
3022+ .removeData("droppable")
3023+ .unbind(".droppable");
3024+
3025+ return this;
3026+ },
3027+
3028+ _setOption: function(key, value) {
3029+
3030+ if(key == 'accept') {
3031+ this.accept = $.isFunction(value) ? value : function(d) {
3032+ return d.is(value);
3033+ };
3034+ }
3035+ $.Widget.prototype._setOption.apply(this, arguments);
3036+ },
3037+
3038+ _activate: function(event) {
3039+ var draggable = $.ui.ddmanager.current;
3040+ if(this.options.activeClass) this.element.addClass(this.options.activeClass);
3041+ (draggable && this._trigger('activate', event, this.ui(draggable)));
3042+ },
3043+
3044+ _deactivate: function(event) {
3045+ var draggable = $.ui.ddmanager.current;
3046+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
3047+ (draggable && this._trigger('deactivate', event, this.ui(draggable)));
3048+ },
3049+
3050+ _over: function(event) {
3051+
3052+ var draggable = $.ui.ddmanager.current;
3053+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
3054+
3055+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
3056+ if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
3057+ this._trigger('over', event, this.ui(draggable));
3058+ }
3059+
3060+ },
3061+
3062+ _out: function(event) {
3063+
3064+ var draggable = $.ui.ddmanager.current;
3065+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
3066+
3067+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
3068+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
3069+ this._trigger('out', event, this.ui(draggable));
3070+ }
3071+
3072+ },
3073+
3074+ _drop: function(event,custom) {
3075+
3076+ var draggable = custom || $.ui.ddmanager.current;
3077+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
3078+
3079+ var childrenIntersection = false;
3080+ this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
3081+ var inst = $.data(this, 'droppable');
3082+ if(
3083+ inst.options.greedy
3084+ && !inst.options.disabled
3085+ && inst.options.scope == draggable.options.scope
3086+ && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
3087+ && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
3088+ ) { childrenIntersection = true; return false; }
3089+ });
3090+ if(childrenIntersection) return false;
3091+
3092+ if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
3093+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
3094+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
3095+ this._trigger('drop', event, this.ui(draggable));
3096+ return this.element;
3097+ }
3098+
3099+ return false;
3100+
3101+ },
3102+
3103+ ui: function(c) {
3104+ return {
3105+ draggable: (c.currentItem || c.element),
3106+ helper: c.helper,
3107+ position: c.position,
3108+ offset: c.positionAbs
3109+ };
3110+ }
3111+
3112+});
3113+
3114+$.extend($.ui.droppable, {
3115+ version: "1.8.4"
3116+});
3117+
3118+$.ui.intersect = function(draggable, droppable, toleranceMode) {
3119+
3120+ if (!droppable.offset) return false;
3121+
3122+ var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
3123+ y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
3124+ var l = droppable.offset.left, r = l + droppable.proportions.width,
3125+ t = droppable.offset.top, b = t + droppable.proportions.height;
3126+
3127+ switch (toleranceMode) {
3128+ case 'fit':
3129+ return (l <= x1 && x2 <= r
3130+ && t <= y1 && y2 <= b);
3131+ break;
3132+ case 'intersect':
3133+ return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
3134+ && x2 - (draggable.helperProportions.width / 2) < r // Left Half
3135+ && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
3136+ && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
3137+ break;
3138+ case 'pointer':
3139+ var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
3140+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
3141+ isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
3142+ return isOver;
3143+ break;
3144+ case 'touch':
3145+ return (
3146+ (y1 >= t && y1 <= b) || // Top edge touching
3147+ (y2 >= t && y2 <= b) || // Bottom edge touching
3148+ (y1 < t && y2 > b) // Surrounded vertically
3149+ ) && (
3150+ (x1 >= l && x1 <= r) || // Left edge touching
3151+ (x2 >= l && x2 <= r) || // Right edge touching
3152+ (x1 < l && x2 > r) // Surrounded horizontally
3153+ );
3154+ break;
3155+ default:
3156+ return false;
3157+ break;
3158+ }
3159+
3160+};
3161+
3162+/*
3163+ This manager tracks offsets of draggables and droppables
3164+*/
3165+$.ui.ddmanager = {
3166+ current: null,
3167+ droppables: { 'default': [] },
3168+ prepareOffsets: function(t, event) {
3169+
3170+ var m = $.ui.ddmanager.droppables[t.options.scope] || [];
3171+ var type = event ? event.type : null; // workaround for #2317
3172+ var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
3173+
3174+ droppablesLoop: for (var i = 0; i < m.length; i++) {
3175+
3176+ if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
3177+ for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
3178+ m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
3179+
3180+ m[i].offset = m[i].element.offset();
3181+ m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
3182+
3183+ if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
3184+
3185+ }
3186+
3187+ },
3188+ drop: function(draggable, event) {
3189+
3190+ var dropped = false;
3191+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
3192+
3193+ if(!this.options) return;
3194+ if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
3195+ dropped = dropped || this._drop.call(this, event);
3196+
3197+ if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
3198+ this.isout = 1; this.isover = 0;
3199+ this._deactivate.call(this, event);
3200+ }
3201+
3202+ });
3203+ return dropped;
3204+
3205+ },
3206+ drag: function(draggable, event) {
3207+
3208+ //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
3209+ if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
3210+
3211+ //Run through all droppables and check their positions based on specific tolerance options
3212+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
3213+
3214+ if(this.options.disabled || this.greedyChild || !this.visible) return;
3215+ var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
3216+
3217+ var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
3218+ if(!c) return;
3219+
3220+ var parentInstance;
3221+ if (this.options.greedy) {
3222+ var parent = this.element.parents(':data(droppable):eq(0)');
3223+ if (parent.length) {
3224+ parentInstance = $.data(parent[0], 'droppable');
3225+ parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
3226+ }
3227+ }
3228+
3229+ // we just moved into a greedy child
3230+ if (parentInstance && c == 'isover') {
3231+ parentInstance['isover'] = 0;
3232+ parentInstance['isout'] = 1;
3233+ parentInstance._out.call(parentInstance, event);
3234+ }
3235+
3236+ this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
3237+ this[c == "isover" ? "_over" : "_out"].call(this, event);
3238+
3239+ // we just moved out of a greedy child
3240+ if (parentInstance && c == 'isout') {
3241+ parentInstance['isout'] = 0;
3242+ parentInstance['isover'] = 1;
3243+ parentInstance._over.call(parentInstance, event);
3244+ }
3245+ });
3246+
3247+ }
3248+};
3249+
3250+})(jQuery);
3251+/*
3252+ * jQuery UI Resizable 1.8.4
3253+ *
3254+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
3255+ * Dual licensed under the MIT or GPL Version 2 licenses.
3256+ * http://jquery.org/license
3257+ *
3258+ * http://docs.jquery.com/UI/Resizables
3259+ *
3260+ * Depends:
3261+ * jquery.ui.core.js
3262+ * jquery.ui.mouse.js
3263+ * jquery.ui.widget.js
3264+ */
3265+(function( $, undefined ) {
3266+
3267+$.widget("ui.resizable", $.ui.mouse, {
3268+ widgetEventPrefix: "resize",
3269+ options: {
3270+ alsoResize: false,
3271+ animate: false,
3272+ animateDuration: "slow",
3273+ animateEasing: "swing",
3274+ aspectRatio: false,
3275+ autoHide: false,
3276+ containment: false,
3277+ ghost: false,
3278+ grid: false,
3279+ handles: "e,s,se",
3280+ helper: false,
3281+ maxHeight: null,
3282+ maxWidth: null,
3283+ minHeight: 10,
3284+ minWidth: 10,
3285+ zIndex: 1000
3286+ },
3287+ _create: function() {
3288+
3289+ var self = this, o = this.options;
3290+ this.element.addClass("ui-resizable");
3291+
3292+ $.extend(this, {
3293+ _aspectRatio: !!(o.aspectRatio),
3294+ aspectRatio: o.aspectRatio,
3295+ originalElement: this.element,
3296+ _proportionallyResizeElements: [],
3297+ _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
3298+ });
3299+
3300+ //Wrap the element if it cannot hold child nodes
3301+ if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
3302+
3303+ //Opera fix for relative positioning
3304+ if (/relative/.test(this.element.css('position')) && $.browser.opera)
3305+ this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
3306+
3307+ //Create a wrapper element and set the wrapper to the new current internal element
3308+ this.element.wrap(
3309+ $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
3310+ position: this.element.css('position'),
3311+ width: this.element.outerWidth(),
3312+ height: this.element.outerHeight(),
3313+ top: this.element.css('top'),
3314+ left: this.element.css('left')
3315+ })
3316+ );
3317+
3318+ //Overwrite the original this.element
3319+ this.element = this.element.parent().data(
3320+ "resizable", this.element.data('resizable')
3321+ );
3322+
3323+ this.elementIsWrapper = true;
3324+
3325+ //Move margins to the wrapper
3326+ this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
3327+ this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
3328+
3329+ //Prevent Safari textarea resize
3330+ this.originalResizeStyle = this.originalElement.css('resize');
3331+ this.originalElement.css('resize', 'none');
3332+
3333+ //Push the actual element to our proportionallyResize internal array
3334+ this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
3335+
3336+ // avoid IE jump (hard set the margin)
3337+ this.originalElement.css({ margin: this.originalElement.css('margin') });
3338+
3339+ // fix handlers offset
3340+ this._proportionallyResize();
3341+
3342+ }
3343+
3344+ this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
3345+ if(this.handles.constructor == String) {
3346+
3347+ if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
3348+ var n = this.handles.split(","); this.handles = {};
3349+
3350+ for(var i = 0; i < n.length; i++) {
3351+
3352+ var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
3353+ var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
3354+
3355+ // increase zIndex of sw, se, ne, nw axis
3356+ //TODO : this modifies original option
3357+ if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
3358+
3359+ //TODO : What's going on here?
3360+ if ('se' == handle) {
3361+ axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
3362+ };
3363+
3364+ //Insert into internal handles object and append to element
3365+ this.handles[handle] = '.ui-resizable-'+handle;
3366+ this.element.append(axis);
3367+ }
3368+
3369+ }
3370+
3371+ this._renderAxis = function(target) {
3372+
3373+ target = target || this.element;
3374+
3375+ for(var i in this.handles) {
3376+
3377+ if(this.handles[i].constructor == String)
3378+ this.handles[i] = $(this.handles[i], this.element).show();
3379+
3380+ //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
3381+ if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
3382+
3383+ var axis = $(this.handles[i], this.element), padWrapper = 0;
3384+
3385+ //Checking the correct pad and border
3386+ padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
3387+
3388+ //The padding type i have to apply...
3389+ var padPos = [ 'padding',
3390+ /ne|nw|n/.test(i) ? 'Top' :
3391+ /se|sw|s/.test(i) ? 'Bottom' :
3392+ /^e$/.test(i) ? 'Right' : 'Left' ].join("");
3393+
3394+ target.css(padPos, padWrapper);
3395+
3396+ this._proportionallyResize();
3397+
3398+ }
3399+
3400+ //TODO: What's that good for? There's not anything to be executed left
3401+ if(!$(this.handles[i]).length)
3402+ continue;
3403+
3404+ }
3405+ };
3406+
3407+ //TODO: make renderAxis a prototype function
3408+ this._renderAxis(this.element);
3409+
3410+ this._handles = $('.ui-resizable-handle', this.element)
3411+ .disableSelection();
3412+
3413+ //Matching axis name
3414+ this._handles.mouseover(function() {
3415+ if (!self.resizing) {
3416+ if (this.className)
3417+ var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
3418+ //Axis, default = se
3419+ self.axis = axis && axis[1] ? axis[1] : 'se';
3420+ }
3421+ });
3422+
3423+ //If we want to auto hide the elements
3424+ if (o.autoHide) {
3425+ this._handles.hide();
3426+ $(this.element)
3427+ .addClass("ui-resizable-autohide")
3428+ .hover(function() {
3429+ $(this).removeClass("ui-resizable-autohide");
3430+ self._handles.show();
3431+ },
3432+ function(){
3433+ if (!self.resizing) {
3434+ $(this).addClass("ui-resizable-autohide");
3435+ self._handles.hide();
3436+ }
3437+ });
3438+ }
3439+
3440+ //Initialize the mouse interaction
3441+ this._mouseInit();
3442+
3443+ },
3444+
3445+ destroy: function() {
3446+
3447+ this._mouseDestroy();
3448+
3449+ var _destroy = function(exp) {
3450+ $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
3451+ .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
3452+ };
3453+
3454+ //TODO: Unwrap at same DOM position
3455+ if (this.elementIsWrapper) {
3456+ _destroy(this.element);
3457+ var wrapper = this.element;
3458+ wrapper.after(
3459+ this.originalElement.css({
3460+ position: wrapper.css('position'),
3461+ width: wrapper.outerWidth(),
3462+ height: wrapper.outerHeight(),
3463+ top: wrapper.css('top'),
3464+ left: wrapper.css('left')
3465+ })
3466+ ).remove();
3467+ }
3468+
3469+ this.originalElement.css('resize', this.originalResizeStyle);
3470+ _destroy(this.originalElement);
3471+
3472+ return this;
3473+ },
3474+
3475+ _mouseCapture: function(event) {
3476+ var handle = false;
3477+ for (var i in this.handles) {
3478+ if ($(this.handles[i])[0] == event.target) {
3479+ handle = true;
3480+ }
3481+ }
3482+
3483+ return !this.options.disabled && handle;
3484+ },
3485+
3486+ _mouseStart: function(event) {
3487+
3488+ var o = this.options, iniPos = this.element.position(), el = this.element;
3489+
3490+ this.resizing = true;
3491+ this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
3492+
3493+ // bugfix for http://dev.jquery.com/ticket/1749
3494+ if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
3495+ el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
3496+ }
3497+
3498+ //Opera fixing relative position
3499+ if ($.browser.opera && (/relative/).test(el.css('position')))
3500+ el.css({ position: 'relative', top: 'auto', left: 'auto' });
3501+
3502+ this._renderProxy();
3503+
3504+ var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
3505+
3506+ if (o.containment) {
3507+ curleft += $(o.containment).scrollLeft() || 0;
3508+ curtop += $(o.containment).scrollTop() || 0;
3509+ }
3510+
3511+ //Store needed variables
3512+ this.offset = this.helper.offset();
3513+ this.position = { left: curleft, top: curtop };
3514+ this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
3515+ this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
3516+ this.originalPosition = { left: curleft, top: curtop };
3517+ this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
3518+ this.originalMousePosition = { left: event.pageX, top: event.pageY };
3519+
3520+ //Aspect Ratio
3521+ this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
3522+
3523+ var cursor = $('.ui-resizable-' + this.axis).css('cursor');
3524+ $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
3525+
3526+ el.addClass("ui-resizable-resizing");
3527+ this._propagate("start", event);
3528+ return true;
3529+ },
3530+
3531+ _mouseDrag: function(event) {
3532+
3533+ //Increase performance, avoid regex
3534+ var el = this.helper, o = this.options, props = {},
3535+ self = this, smp = this.originalMousePosition, a = this.axis;
3536+
3537+ var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
3538+ var trigger = this._change[a];
3539+ if (!trigger) return false;
3540+
3541+ // Calculate the attrs that will be change
3542+ var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
3543+
3544+ if (this._aspectRatio || event.shiftKey)
3545+ data = this._updateRatio(data, event);
3546+
3547+ data = this._respectSize(data, event);
3548+
3549+ // plugins callbacks need to be called first
3550+ this._propagate("resize", event);
3551+
3552+ el.css({
3553+ top: this.position.top + "px", left: this.position.left + "px",
3554+ width: this.size.width + "px", height: this.size.height + "px"
3555+ });
3556+
3557+ if (!this._helper && this._proportionallyResizeElements.length)
3558+ this._proportionallyResize();
3559+
3560+ this._updateCache(data);
3561+
3562+ // calling the user callback at the end
3563+ this._trigger('resize', event, this.ui());
3564+
3565+ return false;
3566+ },
3567+
3568+ _mouseStop: function(event) {
3569+
3570+ this.resizing = false;
3571+ var o = this.options, self = this;
3572+
3573+ if(this._helper) {
3574+ var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3575+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
3576+ soffsetw = ista ? 0 : self.sizeDiff.width;
3577+
3578+ var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
3579+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
3580+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
3581+
3582+ if (!o.animate)
3583+ this.element.css($.extend(s, { top: top, left: left }));
3584+
3585+ self.helper.height(self.size.height);
3586+ self.helper.width(self.size.width);
3587+
3588+ if (this._helper && !o.animate) this._proportionallyResize();
3589+ }
3590+
3591+ $('body').css('cursor', 'auto');
3592+
3593+ this.element.removeClass("ui-resizable-resizing");
3594+
3595+ this._propagate("stop", event);
3596+
3597+ if (this._helper) this.helper.remove();
3598+ return false;
3599+
3600+ },
3601+
3602+ _updateCache: function(data) {
3603+ var o = this.options;
3604+ this.offset = this.helper.offset();
3605+ if (isNumber(data.left)) this.position.left = data.left;
3606+ if (isNumber(data.top)) this.position.top = data.top;
3607+ if (isNumber(data.height)) this.size.height = data.height;
3608+ if (isNumber(data.width)) this.size.width = data.width;
3609+ },
3610+
3611+ _updateRatio: function(data, event) {
3612+
3613+ var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
3614+
3615+ if (data.height) data.width = (csize.height * this.aspectRatio);
3616+ else if (data.width) data.height = (csize.width / this.aspectRatio);
3617+
3618+ if (a == 'sw') {
3619+ data.left = cpos.left + (csize.width - data.width);
3620+ data.top = null;
3621+ }
3622+ if (a == 'nw') {
3623+ data.top = cpos.top + (csize.height - data.height);
3624+ data.left = cpos.left + (csize.width - data.width);
3625+ }
3626+
3627+ return data;
3628+ },
3629+
3630+ _respectSize: function(data, event) {
3631+
3632+ var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
3633+ ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
3634+ isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
3635+
3636+ if (isminw) data.width = o.minWidth;
3637+ if (isminh) data.height = o.minHeight;
3638+ if (ismaxw) data.width = o.maxWidth;
3639+ if (ismaxh) data.height = o.maxHeight;
3640+
3641+ var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
3642+ var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
3643+
3644+ if (isminw && cw) data.left = dw - o.minWidth;
3645+ if (ismaxw && cw) data.left = dw - o.maxWidth;
3646+ if (isminh && ch) data.top = dh - o.minHeight;
3647+ if (ismaxh && ch) data.top = dh - o.maxHeight;
3648+
3649+ // fixing jump error on top/left - bug #2330
3650+ var isNotwh = !data.width && !data.height;
3651+ if (isNotwh && !data.left && data.top) data.top = null;
3652+ else if (isNotwh && !data.top && data.left) data.left = null;
3653+
3654+ return data;
3655+ },
3656+
3657+ _proportionallyResize: function() {
3658+
3659+ var o = this.options;
3660+ if (!this._proportionallyResizeElements.length) return;
3661+ var element = this.helper || this.element;
3662+
3663+ for (var i=0; i < this._proportionallyResizeElements.length; i++) {
3664+
3665+ var prel = this._proportionallyResizeElements[i];
3666+
3667+ if (!this.borderDif) {
3668+ var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
3669+ p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
3670+
3671+ this.borderDif = $.map(b, function(v, i) {
3672+ var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
3673+ return border + padding;
3674+ });
3675+ }
3676+
3677+ if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
3678+ continue;
3679+
3680+ prel.css({
3681+ height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
3682+ width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
3683+ });
3684+
3685+ };
3686+
3687+ },
3688+
3689+ _renderProxy: function() {
3690+
3691+ var el = this.element, o = this.options;
3692+ this.elementOffset = el.offset();
3693+
3694+ if(this._helper) {
3695+
3696+ this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
3697+
3698+ // fix ie6 offset TODO: This seems broken
3699+ var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
3700+ pxyoffset = ( ie6 ? 2 : -1 );
3701+
3702+ this.helper.addClass(this._helper).css({
3703+ width: this.element.outerWidth() + pxyoffset,
3704+ height: this.element.outerHeight() + pxyoffset,
3705+ position: 'absolute',
3706+ left: this.elementOffset.left - ie6offset +'px',
3707+ top: this.elementOffset.top - ie6offset +'px',
3708+ zIndex: ++o.zIndex //TODO: Don't modify option
3709+ });
3710+
3711+ this.helper
3712+ .appendTo("body")
3713+ .disableSelection();
3714+
3715+ } else {
3716+ this.helper = this.element;
3717+ }
3718+
3719+ },
3720+
3721+ _change: {
3722+ e: function(event, dx, dy) {
3723+ return { width: this.originalSize.width + dx };
3724+ },
3725+ w: function(event, dx, dy) {
3726+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
3727+ return { left: sp.left + dx, width: cs.width - dx };
3728+ },
3729+ n: function(event, dx, dy) {
3730+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
3731+ return { top: sp.top + dy, height: cs.height - dy };
3732+ },
3733+ s: function(event, dx, dy) {
3734+ return { height: this.originalSize.height + dy };
3735+ },
3736+ se: function(event, dx, dy) {
3737+ return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3738+ },
3739+ sw: function(event, dx, dy) {
3740+ return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3741+ },
3742+ ne: function(event, dx, dy) {
3743+ return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
3744+ },
3745+ nw: function(event, dx, dy) {
3746+ return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
3747+ }
3748+ },
3749+
3750+ _propagate: function(n, event) {
3751+ $.ui.plugin.call(this, n, [event, this.ui()]);
3752+ (n != "resize" && this._trigger(n, event, this.ui()));
3753+ },
3754+
3755+ plugins: {},
3756+
3757+ ui: function() {
3758+ return {
3759+ originalElement: this.originalElement,
3760+ element: this.element,
3761+ helper: this.helper,
3762+ position: this.position,
3763+ size: this.size,
3764+ originalSize: this.originalSize,
3765+ originalPosition: this.originalPosition
3766+ };
3767+ }
3768+
3769+});
3770+
3771+$.extend($.ui.resizable, {
3772+ version: "1.8.4"
3773+});
3774+
3775+/*
3776+ * Resizable Extensions
3777+ */
3778+
3779+$.ui.plugin.add("resizable", "alsoResize", {
3780+
3781+ start: function (event, ui) {
3782+ var self = $(this).data("resizable"), o = self.options;
3783+
3784+ var _store = function (exp) {
3785+ $(exp).each(function() {
3786+ var el = $(this);
3787+ el.data("resizable-alsoresize", {
3788+ width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
3789+ left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10),
3790+ position: el.css('position') // to reset Opera on stop()
3791+ });
3792+ });
3793+ };
3794+
3795+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
3796+ if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
3797+ else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
3798+ }else{
3799+ _store(o.alsoResize);
3800+ }
3801+ },
3802+
3803+ resize: function (event, ui) {
3804+ var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
3805+
3806+ var delta = {
3807+ height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
3808+ top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
3809+ },
3810+
3811+ _alsoResize = function (exp, c) {
3812+ $(exp).each(function() {
3813+ var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
3814+ css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
3815+
3816+ $.each(css, function (i, prop) {
3817+ var sum = (start[prop]||0) + (delta[prop]||0);
3818+ if (sum && sum >= 0)
3819+ style[prop] = sum || null;
3820+ });
3821+
3822+ // Opera fixing relative position
3823+ if ($.browser.opera && /relative/.test(el.css('position'))) {
3824+ self._revertToRelativePosition = true;
3825+ el.css({ position: 'absolute', top: 'auto', left: 'auto' });
3826+ }
3827+
3828+ el.css(style);
3829+ });
3830+ };
3831+
3832+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
3833+ $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
3834+ }else{
3835+ _alsoResize(o.alsoResize);
3836+ }
3837+ },
3838+
3839+ stop: function (event, ui) {
3840+ var self = $(this).data("resizable"), o = self.options;
3841+
3842+ var _reset = function (exp) {
3843+ $(exp).each(function() {
3844+ var el = $(this);
3845+ // reset position for Opera - no need to verify it was changed
3846+ el.css({ position: el.data("resizable-alsoresize").position });
3847+ });
3848+ }
3849+
3850+ if (self._revertToRelativePosition) {
3851+ self._revertToRelativePosition = false;
3852+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
3853+ $.each(o.alsoResize, function (exp) { _reset(exp); });
3854+ }else{
3855+ _reset(o.alsoResize);
3856+ }
3857+ }
3858+
3859+ $(this).removeData("resizable-alsoresize");
3860+ }
3861+});
3862+
3863+$.ui.plugin.add("resizable", "animate", {
3864+
3865+ stop: function(event, ui) {
3866+ var self = $(this).data("resizable"), o = self.options;
3867+
3868+ var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3869+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
3870+ soffsetw = ista ? 0 : self.sizeDiff.width;
3871+
3872+ var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
3873+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
3874+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
3875+
3876+ self.element.animate(
3877+ $.extend(style, top && left ? { top: top, left: left } : {}), {
3878+ duration: o.animateDuration,
3879+ easing: o.animateEasing,
3880+ step: function() {
3881+
3882+ var data = {
3883+ width: parseInt(self.element.css('width'), 10),
3884+ height: parseInt(self.element.css('height'), 10),
3885+ top: parseInt(self.element.css('top'), 10),
3886+ left: parseInt(self.element.css('left'), 10)
3887+ };
3888+
3889+ if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
3890+
3891+ // propagating resize, and updating values for each animation step
3892+ self._updateCache(data);
3893+ self._propagate("resize", event);
3894+
3895+ }
3896+ }
3897+ );
3898+ }
3899+
3900+});
3901+
3902+$.ui.plugin.add("resizable", "containment", {
3903+
3904+ start: function(event, ui) {
3905+ var self = $(this).data("resizable"), o = self.options, el = self.element;
3906+ var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
3907+ if (!ce) return;
3908+
3909+ self.containerElement = $(ce);
3910+
3911+ if (/document/.test(oc) || oc == document) {
3912+ self.containerOffset = { left: 0, top: 0 };
3913+ self.containerPosition = { left: 0, top: 0 };
3914+
3915+ self.parentData = {
3916+ element: $(document), left: 0, top: 0,
3917+ width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
3918+ };
3919+ }
3920+
3921+ // i'm a node, so compute top, left, right, bottom
3922+ else {
3923+ var element = $(ce), p = [];
3924+ $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
3925+
3926+ self.containerOffset = element.offset();
3927+ self.containerPosition = element.position();
3928+ self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
3929+
3930+ var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width,
3931+ width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
3932+
3933+ self.parentData = {
3934+ element: ce, left: co.left, top: co.top, width: width, height: height
3935+ };
3936+ }
3937+ },
3938+
3939+ resize: function(event, ui) {
3940+ var self = $(this).data("resizable"), o = self.options,
3941+ ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
3942+ pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
3943+
3944+ if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
3945+
3946+ if (cp.left < (self._helper ? co.left : 0)) {
3947+ self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
3948+ if (pRatio) self.size.height = self.size.width / o.aspectRatio;
3949+ self.position.left = o.helper ? co.left : 0;
3950+ }
3951+
3952+ if (cp.top < (self._helper ? co.top : 0)) {
3953+ self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
3954+ if (pRatio) self.size.width = self.size.height * o.aspectRatio;
3955+ self.position.top = self._helper ? co.top : 0;
3956+ }
3957+
3958+ self.offset.left = self.parentData.left+self.position.left;
3959+ self.offset.top = self.parentData.top+self.position.top;
3960+
3961+ var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
3962+ hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
3963+
3964+ var isParent = self.containerElement.get(0) == self.element.parent().get(0),
3965+ isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
3966+
3967+ if(isParent && isOffsetRelative) woset -= self.parentData.left;
3968+
3969+ if (woset + self.size.width >= self.parentData.width) {
3970+ self.size.width = self.parentData.width - woset;
3971+ if (pRatio) self.size.height = self.size.width / self.aspectRatio;
3972+ }
3973+
3974+ if (hoset + self.size.height >= self.parentData.height) {
3975+ self.size.height = self.parentData.height - hoset;
3976+ if (pRatio) self.size.width = self.size.height * self.aspectRatio;
3977+ }
3978+ },
3979+
3980+ stop: function(event, ui){
3981+ var self = $(this).data("resizable"), o = self.options, cp = self.position,
3982+ co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
3983+
3984+ var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
3985+
3986+ if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
3987+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3988+
3989+ if (self._helper && !o.animate && (/static/).test(ce.css('position')))
3990+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
3991+
3992+ }
3993+});
3994+
3995+$.ui.plugin.add("resizable", "ghost", {
3996+
3997+ start: function(event, ui) {
3998+
3999+ var self = $(this).data("resizable"), o = self.options, cs = self.size;
4000+
4001+ self.ghost = self.originalElement.clone();
4002+ self.ghost
4003+ .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
4004+ .addClass('ui-resizable-ghost')
4005+ .addClass(typeof o.ghost == 'string' ? o.ghost : '');
4006+
4007+ self.ghost.appendTo(self.helper);
4008+
4009+ },
4010+
4011+ resize: function(event, ui){
4012+ var self = $(this).data("resizable"), o = self.options;
4013+ if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
4014+ },
4015+
4016+ stop: function(event, ui){
4017+ var self = $(this).data("resizable"), o = self.options;
4018+ if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
4019+ }
4020+
4021+});
4022+
4023+$.ui.plugin.add("resizable", "grid", {
4024+
4025+ resize: function(event, ui) {
4026+ var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
4027+ o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
4028+ var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
4029+
4030+ if (/^(se|s|e)$/.test(a)) {
4031+ self.size.width = os.width + ox;
4032+ self.size.height = os.height + oy;
4033+ }
4034+ else if (/^(ne)$/.test(a)) {
4035+ self.size.width = os.width + ox;
4036+ self.size.height = os.height + oy;
4037+ self.position.top = op.top - oy;
4038+ }
4039+ else if (/^(sw)$/.test(a)) {
4040+ self.size.width = os.width + ox;
4041+ self.size.height = os.height + oy;
4042+ self.position.left = op.left - ox;
4043+ }
4044+ else {
4045+ self.size.width = os.width + ox;
4046+ self.size.height = os.height + oy;
4047+ self.position.top = op.top - oy;
4048+ self.position.left = op.left - ox;
4049+ }
4050+ }
4051+
4052+});
4053+
4054+var num = function(v) {
4055+ return parseInt(v, 10) || 0;
4056+};
4057+
4058+var isNumber = function(value) {
4059+ return !isNaN(parseInt(value, 10));
4060+};
4061+
4062+})(jQuery);
4063+/*
4064+ * jQuery UI Selectable 1.8.4
4065+ *
4066+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
4067+ * Dual licensed under the MIT or GPL Version 2 licenses.
4068+ * http://jquery.org/license
4069+ *
4070+ * http://docs.jquery.com/UI/Selectables
4071+ *
4072+ * Depends:
4073+ * jquery.ui.core.js
4074+ * jquery.ui.mouse.js
4075+ * jquery.ui.widget.js
4076+ */
4077+(function( $, undefined ) {
4078+
4079+$.widget("ui.selectable", $.ui.mouse, {
4080+ options: {
4081+ appendTo: 'body',
4082+ autoRefresh: true,
4083+ distance: 0,
4084+ filter: '*',
4085+ tolerance: 'touch'
4086+ },
4087+ _create: function() {
4088+ var self = this;
4089+
4090+ this.element.addClass("ui-selectable");
4091+
4092+ this.dragged = false;
4093+
4094+ // cache selectee children based on filter
4095+ var selectees;
4096+ this.refresh = function() {
4097+ selectees = $(self.options.filter, self.element[0]);
4098+ selectees.each(function() {
4099+ var $this = $(this);
4100+ var pos = $this.offset();
4101+ $.data(this, "selectable-item", {
4102+ element: this,
4103+ $element: $this,
4104+ left: pos.left,
4105+ top: pos.top,
4106+ right: pos.left + $this.outerWidth(),
4107+ bottom: pos.top + $this.outerHeight(),
4108+ startselected: false,
4109+ selected: $this.hasClass('ui-selected'),
4110+ selecting: $this.hasClass('ui-selecting'),
4111+ unselecting: $this.hasClass('ui-unselecting')
4112+ });
4113+ });
4114+ };
4115+ this.refresh();
4116+
4117+ this.selectees = selectees.addClass("ui-selectee");
4118+
4119+ this._mouseInit();
4120+
4121+ this.helper = $("<div class='ui-selectable-helper'></div>");
4122+ },
4123+
4124+ destroy: function() {
4125+ this.selectees
4126+ .removeClass("ui-selectee")
4127+ .removeData("selectable-item");
4128+ this.element
4129+ .removeClass("ui-selectable ui-selectable-disabled")
4130+ .removeData("selectable")
4131+ .unbind(".selectable");
4132+ this._mouseDestroy();
4133+
4134+ return this;
4135+ },
4136+
4137+ _mouseStart: function(event) {
4138+ var self = this;
4139+
4140+ this.opos = [event.pageX, event.pageY];
4141+
4142+ if (this.options.disabled)
4143+ return;
4144+
4145+ var options = this.options;
4146+
4147+ this.selectees = $(options.filter, this.element[0]);
4148+
4149+ this._trigger("start", event);
4150+
4151+ $(options.appendTo).append(this.helper);
4152+ // position helper (lasso)
4153+ this.helper.css({
4154+ "left": event.clientX,
4155+ "top": event.clientY,
4156+ "width": 0,
4157+ "height": 0
4158+ });
4159+
4160+ if (options.autoRefresh) {
4161+ this.refresh();
4162+ }
4163+
4164+ this.selectees.filter('.ui-selected').each(function() {
4165+ var selectee = $.data(this, "selectable-item");
4166+ selectee.startselected = true;
4167+ if (!event.metaKey) {
4168+ selectee.$element.removeClass('ui-selected');
4169+ selectee.selected = false;
4170+ selectee.$element.addClass('ui-unselecting');
4171+ selectee.unselecting = true;
4172+ // selectable UNSELECTING callback
4173+ self._trigger("unselecting", event, {
4174+ unselecting: selectee.element
4175+ });
4176+ }
4177+ });
4178+
4179+ $(event.target).parents().andSelf().each(function() {
4180+ var selectee = $.data(this, "selectable-item");
4181+ if (selectee) {
4182+ var doSelect = !event.metaKey || !selectee.$element.hasClass('ui-selected');
4183+ selectee.$element
4184+ .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
4185+ .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
4186+ selectee.unselecting = !doSelect;
4187+ selectee.selecting = doSelect;
4188+ selectee.selected = doSelect;
4189+ // selectable (UN)SELECTING callback
4190+ if (doSelect) {
4191+ self._trigger("selecting", event, {
4192+ selecting: selectee.element
4193+ });
4194+ } else {
4195+ self._trigger("unselecting", event, {
4196+ unselecting: selectee.element
4197+ });
4198+ }
4199+ return false;
4200+ }
4201+ });
4202+
4203+ },
4204+
4205+ _mouseDrag: function(event) {
4206+ var self = this;
4207+ this.dragged = true;
4208+
4209+ if (this.options.disabled)
4210+ return;
4211+
4212+ var options = this.options;
4213+
4214+ var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
4215+ if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
4216+ if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
4217+ this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
4218+
4219+ this.selectees.each(function() {
4220+ var selectee = $.data(this, "selectable-item");
4221+ //prevent helper from being selected if appendTo: selectable
4222+ if (!selectee || selectee.element == self.element[0])
4223+ return;
4224+ var hit = false;
4225+ if (options.tolerance == 'touch') {
4226+ hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
4227+ } else if (options.tolerance == 'fit') {
4228+ hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
4229+ }
4230+
4231+ if (hit) {
4232+ // SELECT
4233+ if (selectee.selected) {
4234+ selectee.$element.removeClass('ui-selected');
4235+ selectee.selected = false;
4236+ }
4237+ if (selectee.unselecting) {
4238+ selectee.$element.removeClass('ui-unselecting');
4239+ selectee.unselecting = false;
4240+ }
4241+ if (!selectee.selecting) {
4242+ selectee.$element.addClass('ui-selecting');
4243+ selectee.selecting = true;
4244+ // selectable SELECTING callback
4245+ self._trigger("selecting", event, {
4246+ selecting: selectee.element
4247+ });
4248+ }
4249+ } else {
4250+ // UNSELECT
4251+ if (selectee.selecting) {
4252+ if (event.metaKey && selectee.startselected) {
4253+ selectee.$element.removeClass('ui-selecting');
4254+ selectee.selecting = false;
4255+ selectee.$element.addClass('ui-selected');
4256+ selectee.selected = true;
4257+ } else {
4258+ selectee.$element.removeClass('ui-selecting');
4259+ selectee.selecting = false;
4260+ if (selectee.startselected) {
4261+ selectee.$element.addClass('ui-unselecting');
4262+ selectee.unselecting = true;
4263+ }
4264+ // selectable UNSELECTING callback
4265+ self._trigger("unselecting", event, {
4266+ unselecting: selectee.element
4267+ });
4268+ }
4269+ }
4270+ if (selectee.selected) {
4271+ if (!event.metaKey && !selectee.startselected) {
4272+ selectee.$element.removeClass('ui-selected');
4273+ selectee.selected = false;
4274+
4275+ selectee.$element.addClass('ui-unselecting');
4276+ selectee.unselecting = true;
4277+ // selectable UNSELECTING callback
4278+ self._trigger("unselecting", event, {
4279+ unselecting: selectee.element
4280+ });
4281+ }
4282+ }
4283+ }
4284+ });
4285+
4286+ return false;
4287+ },
4288+
4289+ _mouseStop: function(event) {
4290+ var self = this;
4291+
4292+ this.dragged = false;
4293+
4294+ var options = this.options;
4295+
4296+ $('.ui-unselecting', this.element[0]).each(function() {
4297+ var selectee = $.data(this, "selectable-item");
4298+ selectee.$element.removeClass('ui-unselecting');
4299+ selectee.unselecting = false;
4300+ selectee.startselected = false;
4301+ self._trigger("unselected", event, {
4302+ unselected: selectee.element
4303+ });
4304+ });
4305+ $('.ui-selecting', this.element[0]).each(function() {
4306+ var selectee = $.data(this, "selectable-item");
4307+ selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
4308+ selectee.selecting = false;
4309+ selectee.selected = true;
4310+ selectee.startselected = true;
4311+ self._trigger("selected", event, {
4312+ selected: selectee.element
4313+ });
4314+ });
4315+ this._trigger("stop", event);
4316+
4317+ this.helper.remove();
4318+
4319+ return false;
4320+ }
4321+
4322+});
4323+
4324+$.extend($.ui.selectable, {
4325+ version: "1.8.4"
4326+});
4327+
4328+})(jQuery);
4329+/*
4330+ * jQuery UI Sortable 1.8.4
4331+ *
4332+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
4333+ * Dual licensed under the MIT or GPL Version 2 licenses.
4334+ * http://jquery.org/license
4335+ *
4336+ * http://docs.jquery.com/UI/Sortables
4337+ *
4338+ * Depends:
4339+ * jquery.ui.core.js
4340+ * jquery.ui.mouse.js
4341+ * jquery.ui.widget.js
4342+ */
4343+(function( $, undefined ) {
4344+
4345+$.widget("ui.sortable", $.ui.mouse, {
4346+ widgetEventPrefix: "sort",
4347+ options: {
4348+ appendTo: "parent",
4349+ axis: false,
4350+ connectWith: false,
4351+ containment: false,
4352+ cursor: 'auto',
4353+ cursorAt: false,
4354+ dropOnEmpty: true,
4355+ forcePlaceholderSize: false,
4356+ forceHelperSize: false,
4357+ grid: false,
4358+ handle: false,
4359+ helper: "original",
4360+ items: '> *',
4361+ opacity: false,
4362+ placeholder: false,
4363+ revert: false,
4364+ scroll: true,
4365+ scrollSensitivity: 20,
4366+ scrollSpeed: 20,
4367+ scope: "default",
4368+ tolerance: "intersect",
4369+ zIndex: 1000
4370+ },
4371+ _create: function() {
4372+
4373+ var o = this.options;
4374+ this.containerCache = {};
4375+ this.element.addClass("ui-sortable");
4376+
4377+ //Get the items
4378+ this.refresh();
4379+
4380+ //Let's determine if the items are floating
4381+ this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
4382+
4383+ //Let's determine the parent's offset
4384+ this.offset = this.element.offset();
4385+
4386+ //Initialize mouse events for interaction
4387+ this._mouseInit();
4388+
4389+ },
4390+
4391+ destroy: function() {
4392+ this.element
4393+ .removeClass("ui-sortable ui-sortable-disabled")
4394+ .removeData("sortable")
4395+ .unbind(".sortable");
4396+ this._mouseDestroy();
4397+
4398+ for ( var i = this.items.length - 1; i >= 0; i-- )
4399+ this.items[i].item.removeData("sortable-item");
4400+
4401+ return this;
4402+ },
4403+
4404+ _setOption: function(key, value){
4405+ if ( key === "disabled" ) {
4406+ this.options[ key ] = value;
4407+
4408+ this.widget()
4409+ [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" );
4410+ } else {
4411+ // Don't call widget base _setOption for disable as it adds ui-state-disabled class
4412+ $.Widget.prototype._setOption.apply(this, arguments);
4413+ }
4414+ },
4415+
4416+ _mouseCapture: function(event, overrideHandle) {
4417+
4418+ if (this.reverting) {
4419+ return false;
4420+ }
4421+
4422+ if(this.options.disabled || this.options.type == 'static') return false;
4423+
4424+ //We have to refresh the items data once first
4425+ this._refreshItems(event);
4426+
4427+ //Find out if the clicked node (or one of its parents) is a actual item in this.items
4428+ var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
4429+ if($.data(this, 'sortable-item') == self) {
4430+ currentItem = $(this);
4431+ return false;
4432+ }
4433+ });
4434+ if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
4435+
4436+ if(!currentItem) return false;
4437+ if(this.options.handle && !overrideHandle) {
4438+ var validHandle = false;
4439+
4440+ $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
4441+ if(!validHandle) return false;
4442+ }
4443+
4444+ this.currentItem = currentItem;
4445+ this._removeCurrentsFromItems();
4446+ return true;
4447+
4448+ },
4449+
4450+ _mouseStart: function(event, overrideHandle, noActivation) {
4451+
4452+ var o = this.options, self = this;
4453+ this.currentContainer = this;
4454+
4455+ //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
4456+ this.refreshPositions();
4457+
4458+ //Create and append the visible helper
4459+ this.helper = this._createHelper(event);
4460+
4461+ //Cache the helper size
4462+ this._cacheHelperProportions();
4463+
4464+ /*
4465+ * - Position generation -
4466+ * This block generates everything position related - it's the core of draggables.
4467+ */
4468+
4469+ //Cache the margins of the original element
4470+ this._cacheMargins();
4471+
4472+ //Get the next scrolling parent
4473+ this.scrollParent = this.helper.scrollParent();
4474+
4475+ //The element's absolute position on the page minus margins
4476+ this.offset = this.currentItem.offset();
4477+ this.offset = {
4478+ top: this.offset.top - this.margins.top,
4479+ left: this.offset.left - this.margins.left
4480+ };
4481+
4482+ // Only after we got the offset, we can change the helper's position to absolute
4483+ // TODO: Still need to figure out a way to make relative sorting possible
4484+ this.helper.css("position", "absolute");
4485+ this.cssPosition = this.helper.css("position");
4486+
4487+ $.extend(this.offset, {
4488+ click: { //Where the click happened, relative to the element
4489+ left: event.pageX - this.offset.left,
4490+ top: event.pageY - this.offset.top
4491+ },
4492+ parent: this._getParentOffset(),
4493+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
4494+ });
4495+
4496+ //Generate the original position
4497+ this.originalPosition = this._generatePosition(event);
4498+ this.originalPageX = event.pageX;
4499+ this.originalPageY = event.pageY;
4500+
4501+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
4502+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
4503+
4504+ //Cache the former DOM position
4505+ this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
4506+
4507+ //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
4508+ if(this.helper[0] != this.currentItem[0]) {
4509+ this.currentItem.hide();
4510+ }
4511+
4512+ //Create the placeholder
4513+ this._createPlaceholder();
4514+
4515+ //Set a containment if given in the options
4516+ if(o.containment)
4517+ this._setContainment();
4518+
4519+ if(o.cursor) { // cursor option
4520+ if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
4521+ $('body').css("cursor", o.cursor);
4522+ }
4523+
4524+ if(o.opacity) { // opacity option
4525+ if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
4526+ this.helper.css("opacity", o.opacity);
4527+ }
4528+
4529+ if(o.zIndex) { // zIndex option
4530+ if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
4531+ this.helper.css("zIndex", o.zIndex);
4532+ }
4533+
4534+ //Prepare scrolling
4535+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
4536+ this.overflowOffset = this.scrollParent.offset();
4537+
4538+ //Call callbacks
4539+ this._trigger("start", event, this._uiHash());
4540+
4541+ //Recache the helper size
4542+ if(!this._preserveHelperProportions)
4543+ this._cacheHelperProportions();
4544+
4545+
4546+ //Post 'activate' events to possible containers
4547+ if(!noActivation) {
4548+ for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
4549+ }
4550+
4551+ //Prepare possible droppables
4552+ if($.ui.ddmanager)
4553+ $.ui.ddmanager.current = this;
4554+
4555+ if ($.ui.ddmanager && !o.dropBehaviour)
4556+ $.ui.ddmanager.prepareOffsets(this, event);
4557+
4558+ this.dragging = true;
4559+
4560+ this.helper.addClass("ui-sortable-helper");
4561+ this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
4562+ return true;
4563+
4564+ },
4565+
4566+ _mouseDrag: function(event) {
4567+
4568+ //Compute the helpers position
4569+ this.position = this._generatePosition(event);
4570+ this.positionAbs = this._convertPositionTo("absolute");
4571+
4572+ if (!this.lastPositionAbs) {
4573+ this.lastPositionAbs = this.positionAbs;
4574+ }
4575+
4576+ //Do scrolling
4577+ if(this.options.scroll) {
4578+ var o = this.options, scrolled = false;
4579+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
4580+
4581+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
4582+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
4583+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
4584+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
4585+
4586+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
4587+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
4588+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
4589+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
4590+
4591+ } else {
4592+
4593+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
4594+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
4595+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
4596+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
4597+
4598+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
4599+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
4600+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
4601+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
4602+
4603+ }
4604+
4605+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
4606+ $.ui.ddmanager.prepareOffsets(this, event);
4607+ }
4608+
4609+ //Regenerate the absolute position used for position checks
4610+ this.positionAbs = this._convertPositionTo("absolute");
4611+
4612+ //Set the helper position
4613+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
4614+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
4615+
4616+ //Rearrange
4617+ for (var i = this.items.length - 1; i >= 0; i--) {
4618+
4619+ //Cache variables and intersection, continue if no intersection
4620+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
4621+ if (!intersection) continue;
4622+
4623+ if(itemElement != this.currentItem[0] //cannot intersect with itself
4624+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
4625+ && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
4626+ && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
4627+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
4628+ ) {
4629+
4630+ this.direction = intersection == 1 ? "down" : "up";
4631+
4632+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
4633+ this._rearrange(event, item);
4634+ } else {
4635+ break;
4636+ }
4637+
4638+ this._trigger("change", event, this._uiHash());
4639+ break;
4640+ }
4641+ }
4642+
4643+ //Post events to containers
4644+ this._contactContainers(event);
4645+
4646+ //Interconnect with droppables
4647+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
4648+
4649+ //Call callbacks
4650+ this._trigger('sort', event, this._uiHash());
4651+
4652+ this.lastPositionAbs = this.positionAbs;
4653+ return false;
4654+
4655+ },
4656+
4657+ _mouseStop: function(event, noPropagation) {
4658+
4659+ if(!event) return;
4660+
4661+ //If we are using droppables, inform the manager about the drop
4662+ if ($.ui.ddmanager && !this.options.dropBehaviour)
4663+ $.ui.ddmanager.drop(this, event);
4664+
4665+ if(this.options.revert) {
4666+ var self = this;
4667+ var cur = self.placeholder.offset();
4668+
4669+ self.reverting = true;
4670+
4671+ $(this.helper).animate({
4672+ left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
4673+ top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
4674+ }, parseInt(this.options.revert, 10) || 500, function() {
4675+ self._clear(event);
4676+ });
4677+ } else {
4678+ this._clear(event, noPropagation);
4679+ }
4680+
4681+ return false;
4682+
4683+ },
4684+
4685+ cancel: function() {
4686+
4687+ var self = this;
4688+
4689+ if(this.dragging) {
4690+
4691+ this._mouseUp();
4692+
4693+ if(this.options.helper == "original")
4694+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4695+ else
4696+ this.currentItem.show();
4697+
4698+ //Post deactivating events to containers
4699+ for (var i = this.containers.length - 1; i >= 0; i--){
4700+ this.containers[i]._trigger("deactivate", null, self._uiHash(this));
4701+ if(this.containers[i].containerCache.over) {
4702+ this.containers[i]._trigger("out", null, self._uiHash(this));
4703+ this.containers[i].containerCache.over = 0;
4704+ }
4705+ }
4706+
4707+ }
4708+
4709+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4710+ if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4711+ if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
4712+
4713+ $.extend(this, {
4714+ helper: null,
4715+ dragging: false,
4716+ reverting: false,
4717+ _noFinalSort: null
4718+ });
4719+
4720+ if(this.domPosition.prev) {
4721+ $(this.domPosition.prev).after(this.currentItem);
4722+ } else {
4723+ $(this.domPosition.parent).prepend(this.currentItem);
4724+ }
4725+
4726+ return this;
4727+
4728+ },
4729+
4730+ serialize: function(o) {
4731+
4732+ var items = this._getItemsAsjQuery(o && o.connected);
4733+ var str = []; o = o || {};
4734+
4735+ $(items).each(function() {
4736+ var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
4737+ if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
4738+ });
4739+
4740+ if(!str.length && o.key) {
4741+ str.push(o.key + '=');
4742+ }
4743+
4744+ return str.join('&');
4745+
4746+ },
4747+
4748+ toArray: function(o) {
4749+
4750+ var items = this._getItemsAsjQuery(o && o.connected);
4751+ var ret = []; o = o || {};
4752+
4753+ items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
4754+ return ret;
4755+
4756+ },
4757+
4758+ /* Be careful with the following core functions */
4759+ _intersectsWith: function(item) {
4760+
4761+ var x1 = this.positionAbs.left,
4762+ x2 = x1 + this.helperProportions.width,
4763+ y1 = this.positionAbs.top,
4764+ y2 = y1 + this.helperProportions.height;
4765+
4766+ var l = item.left,
4767+ r = l + item.width,
4768+ t = item.top,
4769+ b = t + item.height;
4770+
4771+ var dyClick = this.offset.click.top,
4772+ dxClick = this.offset.click.left;
4773+
4774+ var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
4775+
4776+ if( this.options.tolerance == "pointer"
4777+ || this.options.forcePointerForContainers
4778+ || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
4779+ ) {
4780+ return isOverElement;
4781+ } else {
4782+
4783+ return (l < x1 + (this.helperProportions.width / 2) // Right Half
4784+ && x2 - (this.helperProportions.width / 2) < r // Left Half
4785+ && t < y1 + (this.helperProportions.height / 2) // Bottom Half
4786+ && y2 - (this.helperProportions.height / 2) < b ); // Top Half
4787+
4788+ }
4789+ },
4790+
4791+ _intersectsWithPointer: function(item) {
4792+
4793+ var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
4794+ isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
4795+ isOverElement = isOverElementHeight && isOverElementWidth,
4796+ verticalDirection = this._getDragVerticalDirection(),
4797+ horizontalDirection = this._getDragHorizontalDirection();
4798+
4799+ if (!isOverElement)
4800+ return false;
4801+
4802+ return this.floating ?
4803+ ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
4804+ : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
4805+
4806+ },
4807+
4808+ _intersectsWithSides: function(item) {
4809+
4810+ var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
4811+ isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
4812+ verticalDirection = this._getDragVerticalDirection(),
4813+ horizontalDirection = this._getDragHorizontalDirection();
4814+
4815+ if (this.floating && horizontalDirection) {
4816+ return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
4817+ } else {
4818+ return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
4819+ }
4820+
4821+ },
4822+
4823+ _getDragVerticalDirection: function() {
4824+ var delta = this.positionAbs.top - this.lastPositionAbs.top;
4825+ return delta != 0 && (delta > 0 ? "down" : "up");
4826+ },
4827+
4828+ _getDragHorizontalDirection: function() {
4829+ var delta = this.positionAbs.left - this.lastPositionAbs.left;
4830+ return delta != 0 && (delta > 0 ? "right" : "left");
4831+ },
4832+
4833+ refresh: function(event) {
4834+ this._refreshItems(event);
4835+ this.refreshPositions();
4836+ return this;
4837+ },
4838+
4839+ _connectWith: function() {
4840+ var options = this.options;
4841+ return options.connectWith.constructor == String
4842+ ? [options.connectWith]
4843+ : options.connectWith;
4844+ },
4845+
4846+ _getItemsAsjQuery: function(connected) {
4847+
4848+ var self = this;
4849+ var items = [];
4850+ var queries = [];
4851+ var connectWith = this._connectWith();
4852+
4853+ if(connectWith && connected) {
4854+ for (var i = connectWith.length - 1; i >= 0; i--){
4855+ var cur = $(connectWith[i]);
4856+ for (var j = cur.length - 1; j >= 0; j--){
4857+ var inst = $.data(cur[j], 'sortable');
4858+ if(inst && inst != this && !inst.options.disabled) {
4859+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
4860+ }
4861+ };
4862+ };
4863+ }
4864+
4865+ queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
4866+
4867+ for (var i = queries.length - 1; i >= 0; i--){
4868+ queries[i][0].each(function() {
4869+ items.push(this);
4870+ });
4871+ };
4872+
4873+ return $(items);
4874+
4875+ },
4876+
4877+ _removeCurrentsFromItems: function() {
4878+
4879+ var list = this.currentItem.find(":data(sortable-item)");
4880+
4881+ for (var i=0; i < this.items.length; i++) {
4882+
4883+ for (var j=0; j < list.length; j++) {
4884+ if(list[j] == this.items[i].item[0])
4885+ this.items.splice(i,1);
4886+ };
4887+
4888+ };
4889+
4890+ },
4891+
4892+ _refreshItems: function(event) {
4893+
4894+ this.items = [];
4895+ this.containers = [this];
4896+ var items = this.items;
4897+ var self = this;
4898+ var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
4899+ var connectWith = this._connectWith();
4900+
4901+ if(connectWith) {
4902+ for (var i = connectWith.length - 1; i >= 0; i--){
4903+ var cur = $(connectWith[i]);
4904+ for (var j = cur.length - 1; j >= 0; j--){
4905+ var inst = $.data(cur[j], 'sortable');
4906+ if(inst && inst != this && !inst.options.disabled) {
4907+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
4908+ this.containers.push(inst);
4909+ }
4910+ };
4911+ };
4912+ }
4913+
4914+ for (var i = queries.length - 1; i >= 0; i--) {
4915+ var targetData = queries[i][1];
4916+ var _queries = queries[i][0];
4917+
4918+ for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
4919+ var item = $(_queries[j]);
4920+
4921+ item.data('sortable-item', targetData); // Data for target checking (mouse manager)
4922+
4923+ items.push({
4924+ item: item,
4925+ instance: targetData,
4926+ width: 0, height: 0,
4927+ left: 0, top: 0
4928+ });
4929+ };
4930+ };
4931+
4932+ },
4933+
4934+ refreshPositions: function(fast) {
4935+
4936+ //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
4937+ if(this.offsetParent && this.helper) {
4938+ this.offset.parent = this._getParentOffset();
4939+ }
4940+
4941+ for (var i = this.items.length - 1; i >= 0; i--){
4942+ var item = this.items[i];
4943+
4944+ var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
4945+
4946+ if (!fast) {
4947+ item.width = t.outerWidth();
4948+ item.height = t.outerHeight();
4949+ }
4950+
4951+ var p = t.offset();
4952+ item.left = p.left;
4953+ item.top = p.top;
4954+ };
4955+
4956+ if(this.options.custom && this.options.custom.refreshContainers) {
4957+ this.options.custom.refreshContainers.call(this);
4958+ } else {
4959+ for (var i = this.containers.length - 1; i >= 0; i--){
4960+ var p = this.containers[i].element.offset();
4961+ this.containers[i].containerCache.left = p.left;
4962+ this.containers[i].containerCache.top = p.top;
4963+ this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
4964+ this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
4965+ };
4966+ }
4967+
4968+ return this;
4969+ },
4970+
4971+ _createPlaceholder: function(that) {
4972+
4973+ var self = that || this, o = self.options;
4974+
4975+ if(!o.placeholder || o.placeholder.constructor == String) {
4976+ var className = o.placeholder;
4977+ o.placeholder = {
4978+ element: function() {
4979+
4980+ var el = $(document.createElement(self.currentItem[0].nodeName))
4981+ .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
4982+ .removeClass("ui-sortable-helper")[0];
4983+
4984+ if(!className)
4985+ el.style.visibility = "hidden";
4986+
4987+ return el;
4988+ },
4989+ update: function(container, p) {
4990+
4991+ // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
4992+ // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
4993+ if(className && !o.forcePlaceholderSize) return;
4994+
4995+ //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
4996+ if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
4997+ if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
4998+ }
4999+ };
5000+ }
The diff has been truncated for viewing.