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

Proposed by Robert O'Connor on 2010-09-02
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 2010-09-02 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
=== modified file 'VERSION' (properties changed: +x to -x)
=== modified file 'controllers/budget.py'
--- controllers/budget.py 2010-08-28 09:44:22 +0000
+++ controllers/budget.py 2010-09-02 01:18:42 +0000
@@ -153,7 +153,7 @@
153 "Module's Home Page"153 "Module's Home Page"
154 154
155 module_name = deployment_settings.modules[module].name_nice155 module_name = deployment_settings.modules[module].name_nice
156 156
157 return dict(module_name=module_name)157 return dict(module_name=module_name)
158158
159def parameters():159def parameters():
@@ -1418,7 +1418,7 @@
1418 budget = int(request.args(0))1418 budget = int(request.args(0))
1419 except TypeError, ValueError:1419 except TypeError, ValueError:
1420 session.error = T("Need to specify a Budget!")1420 session.error = T("Need to specify a Budget!")
1421 redirect(URL(r=request, f="budget"))1421 redirect(URL(r=reqest, f="budget"))
1422 1422
1423 tables = [db.budget_budget_staff, db.budget_budget_bundle]1423 tables = [db.budget_budget_staff, db.budget_budget_bundle]
1424 authorised = shn_has_permission("update", tables[0]) and shn_has_permission("update", tables[1])1424 authorised = shn_has_permission("update", tables[0]) and shn_has_permission("update", tables[1])
14251425
=== modified file 'controllers/default.py' (properties changed: +x to -x)
=== modified file 'controllers/pr.py' (properties changed: +x to -x)
=== added file 'controllers/survey.py'
--- controllers/survey.py 1970-01-01 00:00:00 +0000
+++ controllers/survey.py 2010-09-02 01:18:42 +0000
@@ -0,0 +1,460 @@
1# -*- coding: utf-8 -*-
2
3"""
4 Survey Module -- a tool to create surveys.
5
6 @author: Robert O'Connor
7"""
8from gluon.html import *
9from gluon.sqlhtml import SQLFORM
10
11module = "survey"
12
13
14# Will populate later on.
15response.menu_options = [
16 [T("Template"), False, URL(r=request, f="template"),[
17 [T("List"), False, URL(r=request, f="template")],
18 [T("Add"), False, URL(r=request, f="template", args="create")]
19 ]],
20 [T("Series"), False, URL(r=request, f="series"),[
21 [T("List"), False, URL(r=request, f="series")],
22 [T("Add"), False, URL(r=request, f="series", args="create")]
23 ]]]
24def template_link():
25 response.s3.prep = response.s3.prep = lambda jr: jr.representation in ("xml", "json") and True or False
26 return shn_rest_controller("survey", "template_link")
27
28def index():
29 "Module's Home Page"
30 module_name = deployment_settings.modules[module].name_nice
31 return dict(module_name=module_name)
32
33def template():
34 """ RESTlike CRUD controller """
35 resource = "template"
36 def _prep(jr):
37 crud.settings.create_next = URL(r = request, c="survey", f="questions")
38 crud.settings.update_next = URL(r = request, c="survey", f="questions")
39 return True
40 response.s3.prep = _prep
41
42 tablename = "%s_%s" % (module, resource)
43 table = db[tablename]
44 table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
45 table.name.requires = IS_NOT_EMPTY()
46 table.name.label = T("Survey Name")
47 table.name.comment = SPAN("*", _class="req")
48 table.description.label = T("Description")
49
50
51 # CRUD Strings
52 s3.crud_strings[tablename] = Storage(
53 title_create = T("Add Survey Template"),
54 title_display = T('Survey Template Details'),
55 title_list = T("List Survey Templates"),
56 title_update = T('Edit Survey Template'),
57 subtitle_create = T('Add New Survey Template'),
58 subtitle_list = T('Survey Templates'),
59 label_list_button = T("List Survey Templates"),
60 label_create_button = T("Add Survey Template"),
61 msg_record_created = T('Survey Template added'),
62 msg_record_modified = T('Survey Template updated'),
63 msg_record_deleted = T('Survey Template deleted'),
64 msg_list_empty = T('No Survey Template currently registered'))
65
66 output = shn_rest_controller(module,resource,listadd=False)
67 return transform_buttons(output,next=True,cancel=True)
68
69def questions():
70 """
71 At this stage, the user the following workflow will be implemented:
72
73 - User adds questions via the drop down or clicks "Add Question" to add a new one.
74 """
75 table = db["survey_questions"]
76 record = request.args(0)
77 template = db(db.survey_template.id == session.rcvars.survey_template).select().first()
78 if not record:
79 questions_query = (db.survey_template_link.survey_questions_id == db.survey_questions.id) & (template.id == db.survey_template_link.survey_template_id)
80 record = db(questions_query).select(db.survey_questions.id).first()
81 if record:
82 redirect(URL(r=request,f="questions",args=[record.id]))
83 questions_form = SQLFORM(table,record,deletable=True,keepvalues=True)
84 all_questions = db().select(db.survey_question.ALL)
85 output = dict(all_questions=all_questions)
86 # Let's avoid blowing up -- this loads questions
87 try:
88 query = (template.id == db.survey_template_link.survey_template_id)
89 contained_questions = db(query).select(db.survey_question.id)
90 if len(contained_questions) > 0:
91 output.update(contained_questions=contained_questions)
92 else:
93 output.update(contained_questions=contained_questions)
94 except:
95 output.update(contained_questions=[])
96 pass # this means we didn't pass an id, e.g., making a new section!
97 if questions_form.accepts(request.vars,session,keepvalues=True):
98 questions = request.post_vars.questions
99 if questions:
100 for question in questions:
101 if not has_dupe_questions(template.id,question):
102 db.survey_template_link.insert(survey_template_id=session.rcvars.survey_template,survey_questions_id=questions_form.vars.id,
103 survey_question_id=question)
104 elif questions_form.errors:
105 response.flash= T("Please correct all errors.")
106 output.update(form=questions_form)
107 return output
108def table():
109 if not "series_id" in request.vars:
110 response.error = "You must provide both a series id to proceed."
111 return dict() # empty dict!
112
113 # store the necessary information -- we're safe at this point.
114 series_id = request.vars.series_id
115
116 # first solely check the series exists.
117 series = db(db.survey_series.id == series_id).select().first()
118 if not series:
119 response.error = T("A survey series with id %s does not exist. Please go back and create one.") % (series_id)
120 return dict()
121 # query for the template to get the table name
122 template = db(db.survey_template.id == series.survey_template_id).select().first()
123
124
125 # everything is good at this point!
126 table = get_table_for_template(template.id)
127 resource = "template_%s" % (template.id)
128 table.uuid.requires = IS_NOT_IN_DB(db, "%s.uuid" % template.table_name)
129 table.id.represent = lambda id: A(id,_href=URL(r=request,f="table",args=[id,"update"], vars={"series_id":request.vars.series_id}))
130 # CRUD Strings
131 s3.crud_strings[template.table_name] = Storage(
132 title_create = T("Add Survey Answer"),
133 title_display = T("Survey Answer Details"),
134 title_list = T("List Survey Answers"),
135 title_update = T("Edit Survey Answer"),
136 subtitle_create = T("Add New Survey Answer"),
137 subtitle_list = T("Survey Answer"),
138 label_list_button = T("List Survey Answers"),
139 label_create_button = T("Add Survey Answer"),
140 msg_record_created = T("Survey Answer added"),
141 msg_record_modified = T("Survey Answer updated"),
142 msg_record_deleted = T("Survey Answer deleted"),
143 msg_list_empty = T("No Survey Answers currently registered"))
144 response.s3.filter = (table.series_id == series_id)
145 output = shn_rest_controller("survey", resource,listadd=False)
146 authorised = shn_has_permission("create", table)
147 if authorised:
148 output.update(add_btn=A(Tstr("Add Survey Answer"), _href=URL(r=request,f="table", args=["create"], vars={"series_id":request.vars.series_id}),
149 _class="action-btn"))
150 else:
151 output.update(add_btn="")
152 return output
153
154def series():
155 """ RESTlike CRUD controller """
156 resource = "series"
157 tablename = "%s_%s" % (module, resource)
158 table = db[tablename]
159 table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
160 table.name.requires = IS_NOT_EMPTY()
161 table.name.label = T("Survey Series Name")
162 table.name.comment = SPAN("*", _class="req")
163 table.description.label = T("Description")
164 table.survey_template_id.label = T("Survey Template")
165 table.survey_template_id.requires = IS_ONE_OF(db, "survey_template.id", "%(name)s")
166 table.survey_template_id.represent = lambda id: (id and [db(db.survey_template.id==id).select()[0].name] or [""])[0]
167 table.survey_template_id.comment = SPAN("*", _class="req")
168 table.from_date.label = T("Start of Period")
169 table.from_date.requires = IS_NOT_EMPTY()
170 table.from_date.comment = SPAN("*", _class="req")
171 table.to_date.label = T("End of Period")
172 table.to_date.requires = IS_NOT_EMPTY()
173 table.to_date.comment = SPAN("*", _class="req")
174
175 # CRUD Strings
176 s3.crud_strings[tablename] = Storage(
177 title_create = T("Add Survey Series"),
178 title_display = T("Survey Series Details"),
179 title_list = T("List Survey Series"),
180 title_update = T("Edit Survey Series"),
181 subtitle_create = T("Add New Survey Series"),
182 subtitle_list = T("Survey Series"),
183 label_list_button = T("List Survey Series"),
184 label_create_button = T("Add Survey Series"),
185 msg_record_created = T("Survey Series added"),
186 msg_record_modified = T("Survey Series updated"),
187 msg_record_deleted = T("Survey Series deleted"),
188 msg_list_empty = T("No Survey Series currently registered"))
189 def _postp(jr, output):
190 shn_action_buttons(jr, deletable=False)
191 return output
192 response.s3.postp = _postp
193
194 output = shn_rest_controller(module, resource,listadd=False)
195
196 return output
197
198#def section():
199# """ RESTlike CRUD controller """
200# resource = "section"
201# tablename = "%s_%s" % (module, resource)
202# table = db[tablename]
203# table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
204# table.name.requires = IS_NOT_EMPTY()
205# table.name.comment = SPAN("*", _class="req")
206# table.name.label = T("Survey Section Display Name")
207# table.description.label = T("Description")
208#
209#
210# # CRUD Strings
211# s3.crud_strings[tablename] = Storage(
212# title_create = T("Add Survey Section"),
213# title_display = T("Survey Section Details"),
214# title_list = T("List Survey Sections"),
215# title_update = T("Edit Survey Section"),
216# subtitle_create = T("Add New Survey Section"),
217# subtitle_list = T("Survey Section"),
218# label_list_button = T("List Survey Sections"),
219# label_create_button = T("Add Survey Section"),
220# msg_record_created = T("Survey Section added"),
221# msg_record_modified = T("Survey Section updated"),
222# msg_record_deleted = T("Survey Section deleted"),
223# msg_list_empty = T("No Survey Sections currently registered"))
224# output = shn_rest_controller(module, resource,listadd=False)
225#
226# return transform_buttons(output,save=True,cancel=True)
227
228def question():
229 # Question data, e.g., name,description, etc.
230 resource = "question"
231 tablename = "%s_%s" % (module, resource)
232 table = db[tablename]
233 table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
234 table.name.requires = IS_NOT_EMPTY()
235 table.name.label = T("Survey Question Display Name")
236 table.name.comment = SPAN("*", _class="req")
237 table.description.label = T("Description")
238# table.tf_ta_columns.label = T("Number of Columns")
239# table.ta_rows.label = T("Number of Rows")
240# table.aggregation_type.writable = False
241# table.aggregation_type.readable = False
242
243 question_types = {
244# 1:T("Multiple Choice (Only One Answer)"),
245# 2:T("Multiple Choice (Multiple Answers)"),
246# 3:T("Matrix of Choices (Only one answer)"),
247# 4:T("Matrix of Choices (Multiple Answers)"),
248# 5:T("Rating Scale"),
249 6:T("Text"),
250# 7:T("Multiple Text Fields"),
251# 8:T("Matrix of Text Fields"),
252 9:T("Long Text"),
253 10:T("Number"),
254 11:T("Date"),
255# 12:T("Image"),
256# 13:T("Descriptive Text (e.g., Prose, etc)"),
257# 14:T("Location"),
258# 15:T("Organisation"),
259# 16:T("Person"),
260## 16:T("Custom Database Resource (e.g., anything defined as a resource in Sahana)")
261 }
262
263 table.question_type.requires = IS_IN_SET(question_types)
264 table.question_type.comment = SPAN("*", _class="req")
265 # CRUD Strings
266 s3.crud_strings[tablename] = Storage(
267 title_create = T("Add Survey Question"),
268 title_display = T("Survey Question Details"),
269 title_list = T("List Survey Questions"),
270 title_update = T("Edit Survey Question"),
271 subtitle_create = T("Add New Survey Question"),
272 subtitle_list = T("Survey Question'"),
273 label_list_button = T("List Survey Questions"),
274 label_create_button = T("Add Survey Question"),
275 msg_record_created = T("Survey Question added"),
276 msg_record_modified = T("Survey Question updated"),
277 msg_record_deleted = T("Survey Question deleted"),
278 msg_list_empty = T("No Survey Questions currently registered"))
279 output = shn_rest_controller(module, resource,listadd=False)
280
281 return transform_buttons(output,cancel=True,save=True)
282
283#def question_options():
284# resource = "question"
285# tablename = "%s_%s" % (module, resource)
286# table = db[tablename]
287# table.uuid.requires = IS_NOT_IN_DB(db,"%s.uuid" % tablename)
288# table.tf_ta_columns.label = T("Number of Columns")
289# table.ta_rows.label = T("Number of Rows")
290## table.answer_choices.label = T("Answer Choices (One Per Line)")
291# table.aggregation_type.writable = False
292# table.aggregation_type.readable = False
293## table.row_choices.label = T("Row Choices (One Per Line)")
294## table.column_choices.label = T("Column Choices (One Per Line")
295## table.tf_choices.label = T("Text before each Text Field (One per line)")
296# output = shn_rest_controller(module, resource,listadd=False)
297# output.update(question_type=question_type)
298# return transform_buttons(output,prev=True,finish=True,cancel=True)
299
300def add_buttons(form, save = None, prev = None, next = None, finish = None,cancel=None):
301 """
302 Utility Function to reduce code duplication as this deals with:
303
304 1) removing the save button
305 2) adding the following: Cancel, Next, Previous and Finish(shown on the last step *ONLY*)
306 """
307 form[0][-1][1][0] = "" # remove the original Save Button
308 if save:
309 form[-1][-1][1].append(INPUT(_type="submit",_name = "save",_value=T("Save"), _id="save"))
310
311 if cancel:
312 form[-1][-1][1].append(INPUT(_type="button",_name = "cancel",_value=T("Cancel"), _id="cancel"))
313
314 if prev:
315 form[-1][-1][1].append(INPUT(_type="submit",_name = "prev",_value=T("Previous"), _id="prev"))
316
317 if next:
318 form[-1][-1][1].append(INPUT(_type="submit", _value=T("Next"),_name="next",_id="next"))
319
320 if finish:
321 form[-1][-1][1].append(INPUT(_type="submit", _value=T("Finish"),_name="finish",_id="finish"))
322 return form
323
324def transform_buttons(output,save = None, prev = None, next = None, finish = None,cancel=None):
325 # fails when output is not HTML (e.g., JSON)
326 if isinstance(output, dict):
327 form = output.get("form",None)
328 if form:
329 add_buttons(form,save,prev,next,finish,cancel)
330 return output
331
332def has_dupe_questions(template_id,question_id):
333 question_query = (db.survey_template_link.survey_template_id == template_id) \
334 & (question_id == db.survey_template_link.survey_question_id)
335 questions = db(question_query).select(db.survey_question.ALL)
336 if len(questions) > 1:
337
338 return True
339 else:
340 return False
341
342def prune_questions(questions_id, questions,all_questions):
343 if not questions_id:
344 return # do nothing
345 if not questions:
346 return # nothing to act on.
347 for question in all_questions:
348 if not question in questions:
349 question_query = (db.survey_template_link.survey_questions_id == questions_id) \
350 & (question.id == db.survey_template_link.survey_question_id)
351 db(question_query).delete()
352 db.commit()
353 return questions
354
355def get_contained_questions(questions_id):
356 question_query = (db.survey_template_link.survey_questions_id == questions_id) & \
357 (db.survey_question.id == db.survey_template_link.survey_question_id) & \
358 (db.survey_template.id == db.survey_template_link.survey_template_id)
359 contained_questions = db(question_query).select(db.survey_question.ALL)
360 return contained_questions
361
362def get_table_for_template(template_id):
363 """ Returns the table for the given template and if it doesn't exist -- creates and returns that"""
364
365 # get the template first -- we need to get the table name
366 template = db(db.survey_template.id == template_id).select().first()
367
368 tbl = None
369
370 if template: # avoid blow ups!
371 fields = [Field("series_id", db.survey_series, writable=False, readable=False)
372 ] # A list of Fields representing the questions
373
374 questions = db((db.survey_template_link.survey_template_id == template_id) & \
375 (db.survey_question.id == db.survey_template_link.survey_question_id)).select(db.survey_question.ALL)
376 # for each question, depending on its type create a Field
377 for question in questions:
378 question_type = question.question_type
379
380 if question_type == 6: # Single TF -- simple for now -- will introduce more types later.
381 fields.append(Field("question_%s" % (question.id), label=question.name))
382
383
384 elif question_type == 9:
385 fields.append(Field("question_%s" % (question.id), "text", label=question.name))
386
387 elif question_type == 10:
388 fields.append(Field("question_%s" % (question.id), "integer", label=question.name))
389
390 elif question_type == 11:
391 fields.append(Field("question_%s" % (question.id), "date", label=question.name))
392
393 tbl = db.define_table("survey_template_%s" % (template_id), uuidstamp, deletion_status, authorstamp,
394 *fields, migrate=True)
395 # now add the table name to the template record so we can reference it later.
396 db(db.survey_template.id == template_id).update(table_name="survey_template_%s" % (template.id))
397 db.commit()
398
399 # set up onaccept for this table.
400 def _onaccept(form):
401 db(tbl.id == form.vars.id).update(series_id=request.vars.series_id)
402 db.commit()
403
404 s3xrc.model.configure(tbl,
405 onaccept=lambda form: _onaccept(form))
406 # finally we return the newly created or existing table.
407 return tbl
408
409def shn_action_buttons(jr, deletable=True):
410
411 """ Provide the usual Action Buttons for Column views. Designed to be called from a postp """
412
413 if jr.component:
414 args = [jr.component_name, "[id]"]
415 else:
416 args = ["[id]"]
417 if auth.is_logged_in():
418 # Provide the ability to delete records in bulk
419 if deletable:
420 response.s3.actions = [
421 dict(label=str(UPDATE), _class="action-btn", url=str(URL(r=request, args = args + ["update"]))),
422 dict(label=str(DELETE), _class="action-btn", url=str(URL(r=request, args = args + ["delete"])))
423 ]
424 else:
425 url = URL(r=request,f="table",vars= {"series_id":args})
426 response.s3.actions = [
427 dict(label=str(UPDATE), _class="action-btn", url=str(URL(r=request, args = args + ["update"]))),
428 dict(label="Answer", _class="action-btn", url=str( URL(r=request,f="table",args="create", vars= {"series_id":"[id]"}))),
429 dict(label="Results", _class="action-btn", url=str( URL(r=request,f="table",vars= {"series_id":"[id]"}))) ]
430 else:
431 response.s3.actions = [
432 dict(label=str(READ), _class="action-btn", url=str(URL(r=request, args = args)))
433 ]
434
435 return
436#def get_options_for_questions(template_id):
437# questions = db((db.survey_template_link.survey_template_id == template_id) & \
438# (db.survey_question.id == db.survey_template_link.survey_question_id)).select(db.survey_question.ALL)
439# opt_map = {}
440# for question in questions:
441# question_type = question.question_type
442# if question_type == 6: # Single TF -- simple for now -- will introduce more types later.
443# opt_map[question.id] = {"allow_comments":question.allow_comments,\
444# "comment_display_label":question.comment_display_label,\
445# "required":question.required}
446#
447# elif question_type == 9:
448# opt_map[question.id] = { "allow_comments":question.allow_comments,\
449# "comment_display_label":question.comment_display_label,\
450# "required":question.required}
451# elif question_type == 10:
452# opt_map[question.id] = {"allow_comments":question.allow_comments,\
453# "comment_display_label":question.comment_display_label,\
454# "required":question.required}
455#
456# elif question_type == 11:
457# opt_map[question.id] = {"allow_comments":question.allow_comments,\
458# "comment_display_label":question.comment_display_label,\
459# "required":question.required}
460# return opt_map
0\ No newline at end of file461\ No newline at end of file
1462
=== modified file 'models/000_config.py'
--- models/000_config.py 2010-09-02 00:15:55 +0000
+++ models/000_config.py 2010-09-02 01:18:42 +0000
@@ -1,288 +1,295 @@
1# -*- coding: utf-8 -*-
2
3"""
4 Deployment settings
5 All settings which are typically edited for a deployment should be done here
6 Deployers shouldn't typically need to edit any other files.
7"""
8
9def Tstr(text):
10 """
11 Convenience function for non-Web2Py modules
12 (latest web2py no longer needs this)
13 """
14 return str(T(text))
15
16s3cfg = local_import("s3cfg")
17deployment_settings = s3cfg.S3Config(T)
18
19# Database settings
20deployment_settings.database.db_type = "sqlite"
21deployment_settings.database.host = "localhost"
22deployment_settings.database.port = "" # use default
23deployment_settings.database.database = "sahana"
24deployment_settings.database.username = "sahana"
25deployment_settings.database.password = "password"
26deployment_settings.database.pool_size = 30
27
28# Authentication settings
29# This setting should be changed _before_ registering the 1st user
30deployment_settings.auth.hmac_key = "akeytochange"
31# These settings should be changed _after_ the 1st (admin) user is
32# registered in order to secure the deployment
33deployment_settings.auth.registration_requires_verification = False
34deployment_settings.auth.registration_requires_approval = False
35deployment_settings.auth.openid = False
36
37# Base settings
38# Set this to the Public URL of the instance
39deployment_settings.base.public_url = "http://127.0.0.1:8000"
40
41# Switch to "False" in Production for a Performance gain
42# (need to set to "True" again when Table definitions are changed)
43deployment_settings.base.migrate = True
44
45# Enable/disable pre-population of the database.
46# Set to False during first run for manual DB migration in case this
47# is explicitly required for a code upgrade, otherwise leave at True
48# NOTE: the web UI will not be accessible while the DB is empty,
49# instead run:
50# python web2py.py -S eden -M
51# to create the db structure, then exit and re-import the data.
52deployment_settings.base.prepopulate = True
53
54# Email settings
55# Outbound server
56deployment_settings.mail.server = "127.0.0.1:25"
57# Useful for Windows Laptops:
58#deployment_settings.mail.server = "smtp.gmail.com:587"
59#deployment_settings.mail.login = "username:password"
60# From Address
61deployment_settings.mail.sender = "'Sahana' <sahana@your.org>"
62# Address to which mails get sent to approve new users
63deployment_settings.mail.approver = "useradmin@your.org"
64
65# L10n settings
66# Uncomment this if the deployment is just in a few countries
67# (used in the GIS Location Selector & maybe in future: Messaging)
68#deployment_settings.L10n.countries = ["PK"]
69# Languages used in the deployment (used for Language Toolbar & GIS Locations)
70# http://www.loc.gov/standards/iso639-2/php/code_list.php
71deployment_settings.L10n.languages = {
72 "en":T("English"),
73 "es":T("Spanish"),
74 "pa":T("Punjabi"),
75 "ps":T("Pashto"),
76 "sd":T("Sindhi"),
77 "ur":T("Urdu"),
78 "zh-tw":T("Chinese (Taiwan)"),
79 "seraiki":T("Seraiki"),
80 "balochi":T("Balochi"),
81}
82# Default language for Language Toolbar (& GIS Locations in future)
83deployment_settings.L10n.default_language = "en"
84# Display the language toolbar
85deployment_settings.L10n.display_toolbar = True
86# Default timezone for users
87deployment_settings.L10n.utc_offset = "UTC +0000"
88
89# GIS (Map) settings
90# Provide a tool to select locations via a map on all forms with location_id
91deployment_settings.gis.map_selector = True
92# Display Resources recorded to Admin-Level Locations on the map
93deployment_settings.gis.display_L0 = False
94# Currently unused
95#deployment_settings.gis.display_L1 = True
96# Allow non-MapAdmins to edit Admin locations?
97# (defaults to True, if not set)
98deployment_settings.gis.edit_L0 = False
99deployment_settings.gis.edit_L1 = True
100#deployment_settings.gis.edit_L2 = True
101deployment_settings.gis.locations_hierarchy = {
102 "L0":T("Country"),
103 "L1":T("Province"),
104 "L2":T("District"),
105 "L3":T("Town"),
106 "L4":T("Village")
107}
108# Do we have a spatial DB available? (currently unused. Will support PostGIS & Spatialite.)
109deployment_settings.gis.spatialdb = False
110# GeoServer (currently unused. Will allow REST control of GeoServer.)
111deployment_settings.gis.geoserver_url = "http://localhost/geoserver"
112deployment_settings.gis.geoserver_username = "admin"
113deployment_settings.gis.geoserver_password = "password"
114
115
116# Security Policy settings
117# Lock-down access to Map Editing
118#deployment_settings.security.map = True
119# Currently unused
120#deployment_settings.security.policy = 2 # Editor
121
122# Comment/uncomment modules here to disable/enable them
123# Modules menu is defined in 01_menu.py
124from gluon.storage import Storage
125deployment_settings.modules = Storage(
126 default = Storage(
127 name_nice = Tstr("Home"),
128 access = None, # All Users (inc Anonymous) can see this module in the default menu & access the controller
129 module_type = 0 # This item is always 1st in the menu
130 ),
131 admin = Storage(
132 name_nice = Tstr("Administration"),
133 description = Tstr("Site Administration"),
134 access = "|1|", # Only Administrators can see this module in the default menu & access the controller
135 module_type = 0 # This item is handled separately in the menu
136 ),
137 gis = Storage(
138 name_nice = Tstr("Map"),
139 description = Tstr("Situation Awareness & Geospatial Analysis"),
140 module_type = 1, # 1st item in the menu
141 resources = Storage(
142 gis_location = {'importer' : True}
143 )
144 ),
145 mpr = Storage(
146 name_nice = Tstr("Missing Persons"),
147 description = Tstr("Helps to report and search for Missing Persons"),
148 module_type = 2,
149 ),
150 rms = Storage(
151 name_nice = Tstr("Requests"),
152 description = Tstr("Tracks requests for aid and matches them against donors who have pledged aid"),
153 module_type = 3,
154 resources = Storage(
155 rms_req = {'importer' : True},
156 )
157 ),
158 hms = Storage(
159 name_nice = Tstr("Hospitals"),
160 description = Tstr("Helps to monitor status of hospitals"),
161 module_type = 4,
162 resources = Storage(
163 hms_hospital = {'importer' : True}
164 )
165 ),
166 vol = Storage(
167 name_nice = Tstr("Volunteers"),
168 description = Tstr("Manage volunteers by capturing their skills, availability and allocation"),
169 module_type = 5,
170 ),
171 logs = Storage(
172 name_nice = Tstr("Logistics Management"),
173 description = Tstr("Managing, Storing and Distributing Relief Items"),
174 module_type = 10
175 ),
176 project = Storage(
177 name_nice = Tstr("Project Management"),
178 description = Tstr("Project Activities"),
179 module_type = 10
180 ),
181 msg = Storage(
182 name_nice = Tstr("Messaging"),
183 description = Tstr("Sends & Receives Alerts via Email & SMS"),
184 module_type = 10,
185 ),
186 flood = Storage(
187 name_nice = Tstr("Flood Alerts"),
188 description = Tstr("Flood Alerts show water levels in various parts of the country"),
189 module_type = 10
190 ),
191 sitrep = Storage(
192 name_nice = Tstr("Assessments"),
193 description = Tstr("Assessments are structured reports done by Professional Organisations - data includes WFP Assessments"),
194 module_type = 10
195 ),
196 rat = Storage(
197 name_nice = Tstr("Rapid Assessments"),
198 description = Tstr("Assessments are structured reports done by Professional Organisations"),
199 module_type = 10
200 ),
201 pr = Storage(
202 name_nice = Tstr("Person Registry"),
203 description = Tstr("Central point to record details on People"),
204 module_type = 10,
205 resources = Storage(
206 pr_address = {'importer' : True},
207 pr_pe_contact = {'importer' : True},
208 pr_presence = {'importer' : True},
209 pr_identity = {'importer' : True},
210 pr_person = {'importer' : True},
211 pr_group = {'importer' : True},
212 pr_group_membership = {'importer' : True},
213 )
214 ),
215 dvi = Storage(
216 name_nice = Tstr("Disaster Victim Identification"),
217 description = Tstr("Disaster Victim Identification"),
218 module_type = 10,
219 resources = Storage(
220 dvi_recreq = {'importer' : True},
221 )
222 ),
223 #dvr = Storage(
224 # name_nice = Tstr("Disaster Victim Registry"),
225 # description = Tstr("Traces internally displaced people (IDPs) and their needs"),
226 # module_type = 10
227 # ),
228 budget = Storage(
229 name_nice = Tstr("Budgeting Module"),
230 description = Tstr("Allows a Budget to be drawn up"),
231 module_type = 10,
232 resources = Storage(
233 budget_item = {'importer' : True},
234 budget_kit = {'importer' : True},
235 budget_bundle = {'importer' : True},
236 )
237 ),
238 cr = Storage(
239 name_nice = Tstr("Shelter Registry"),
240 description = Tstr("Tracks the location, distibution, capacity and breakdown of victims in Shelters"),
241 module_type = 10,
242 resources = Storage(
243 cr_shelter = {'importer' : True }
244 )
245 ),
246 delphi = Storage(
247 name_nice = Tstr("Delphi Decision Maker"),
248 description = Tstr("Supports the decision making of large groups of Crisis Management Experts by helping the groups create ranked list."),
249 module_type = 10,
250 ),
251 doc = Storage(
252 name_nice = Tstr("Documents and Photos"),
253 description = Tstr("A library of digital resources, such as photos, documents and reports"),
254 module_type = 10,
255 ),
256 irs = Storage(
257 name_nice = Tstr("Incident Reporting"),
258 description = Tstr("Incident Reporting System"),
259 module_type = 10
260 ),
261 org = Storage(
262 name_nice = Tstr("Organization Registry"),
263 description = Tstr('Lists "who is doing what & where". Allows relief agencies to coordinate their activities'),
264 module_type = 10,
265 resources = Storage(
266 org_organisation = {'importer' : True},
267 org_office = {'importer' : True},
268 org_project = {'importer' : True},
269 org_staff = {'importer' : True},
270 org_task = {'importer' : True}
271 )
272 ),
273 ticket = Storage(
274 name_nice = Tstr("Ticketing Module"),
275 description = Tstr("Master Message Log to process incoming reports & requests"),
276 module_type = 10,
277 ),
278 importer = Storage(
279 name_nice = "Spreadsheet Importer",
280 description = "Used to import data from spreadsheets into the database",
281 module_type = 10,
282 )
283 #lms = Storage(
284 # name_nice = Tstr("Logistics Management System"),
285 # description = Tstr("An intake system, a warehouse management system, commodity tracking, supply chain management, procurement and other asset and resource management capabilities."),
286 # module_type = 10
287 # ),
288)
289\ No newline at end of file1\ No newline at end of file
2
3# -*- coding: utf-8 -*-
4
5"""
6 Deployment settings
7 All settings which are typically edited for a deployment should be done here
8 Deployers shouldn't typically need to edit any other files.
9"""
10
11def Tstr(text):
12 """
13 Convenience function for non-Web2Py modules
14 (latest web2py no longer needs this)
15 """
16 return str(T(text))
17
18s3cfg = local_import("s3cfg")
19deployment_settings = s3cfg.S3Config(T)
20
21# Database settings
22deployment_settings.database.db_type = "sqlite"
23deployment_settings.database.host = "localhost"
24deployment_settings.database.port = "" # use default
25deployment_settings.database.database = "sahana"
26deployment_settings.database.username = "sahana"
27deployment_settings.database.password = "password"
28deployment_settings.database.pool_size = 30
29
30# Authentication settings
31# This setting should be changed _before_ registering the 1st user
32deployment_settings.auth.hmac_key = "akeytochange"
33# These settings should be changed _after_ the 1st (admin) user is
34# registered in order to secure the deployment
35deployment_settings.auth.registration_requires_verification = False
36deployment_settings.auth.registration_requires_approval = False
37deployment_settings.auth.openid = False
38
39# Base settings
40# Set this to the Public URL of the instance
41deployment_settings.base.public_url = "http://127.0.0.1:8000"
42
43# Switch to "False" in Production for a Performance gain
44# (need to set to "True" again when Table definitions are changed)
45deployment_settings.base.migrate = True
46
47# Enable/disable pre-population of the database.
48# Set to False during first run for manual DB migration in case this
49# is explicitly required for a code upgrade, otherwise leave at True
50# NOTE: the web UI will not be accessible while the DB is empty,
51# instead run:
52# python web2py.py -S eden -M
53# to create the db structure, then exit and re-import the data.
54deployment_settings.base.prepopulate = True
55
56# Email settings
57# Outbound server
58deployment_settings.mail.server = "127.0.0.1:25"
59# Useful for Windows Laptops:
60#deployment_settings.mail.server = "smtp.gmail.com:587"
61#deployment_settings.mail.login = "username:password"
62# From Address
63deployment_settings.mail.sender = "'Sahana' <sahana@your.org>"
64# Address to which mails get sent to approve new users
65deployment_settings.mail.approver = "useradmin@your.org"
66
67# L10n settings
68# Uncomment this if the deployment is just in a few countries
69# (used in the GIS Location Selector & maybe in future: Messaging)
70#deployment_settings.L10n.countries = ["PK"]
71# Languages used in the deployment (used for Language Toolbar & GIS Locations)
72# http://www.loc.gov/standards/iso639-2/php/code_list.php
73deployment_settings.L10n.languages = {
74 "en":T("English"),
75 "es":T("Spanish"),
76 "pa":T("Punjabi"),
77 "ps":T("Pashto"),
78 "sd":T("Sindhi"),
79 "ur":T("Urdu"),
80 "zh-tw":T("Chinese (Taiwan)"),
81 "seraiki":T("Seraiki"),
82 "balochi":T("Balochi"),
83}
84# Default language for Language Toolbar (& GIS Locations in future)
85deployment_settings.L10n.default_language = "en"
86# Display the language toolbar
87deployment_settings.L10n.display_toolbar = True
88# Default timezone for users
89deployment_settings.L10n.utc_offset = "UTC +0000"
90
91# GIS (Map) settings
92# Provide a tool to select locations via a map on all forms with location_id
93deployment_settings.gis.map_selector = True
94# Display Resources recorded to Admin-Level Locations on the map
95deployment_settings.gis.display_L0 = False
96# Currently unused
97#deployment_settings.gis.display_L1 = True
98# Allow non-MapAdmins to edit Admin locations?
99# (defaults to True, if not set)
100deployment_settings.gis.edit_L0 = False
101deployment_settings.gis.edit_L1 = True
102#deployment_settings.gis.edit_L2 = True
103deployment_settings.gis.locations_hierarchy = {
104 "L0":T("Country"),
105 "L1":T("Province"),
106 "L2":T("District"),
107 "L3":T("Town"),
108 "L4":T("Village")
109}
110# Do we have a spatial DB available? (currently unused. Will support PostGIS & Spatialite.)
111deployment_settings.gis.spatialdb = False
112# GeoServer (currently unused. Will allow REST control of GeoServer.)
113deployment_settings.gis.geoserver_url = "http://localhost/geoserver"
114deployment_settings.gis.geoserver_username = "admin"
115deployment_settings.gis.geoserver_password = "password"
116
117
118# Security Policy settings
119# Lock-down access to Map Editing
120#deployment_settings.security.map = True
121# Currently unused
122#deployment_settings.security.policy = 2 # Editor
123
124# Comment/uncomment modules here to disable/enable them
125# Modules menu is defined in 01_menu.py
126from gluon.storage import Storage
127deployment_settings.modules = Storage(
128 default = Storage(
129 name_nice = Tstr("Home"),
130 access = None, # All Users (inc Anonymous) can see this module in the default menu & access the controller
131 module_type = 0 # This item is always 1st in the menu
132 ),
133 admin = Storage(
134 name_nice = Tstr("Administration"),
135 description = Tstr("Site Administration"),
136 access = "|1|", # Only Administrators can see this module in the default menu & access the controller
137 module_type = 0 # This item is handled separately in the menu
138 ),
139 gis = Storage(
140 name_nice = Tstr("Map"),
141 description = Tstr("Situation Awareness & Geospatial Analysis"),
142 module_type = 1, # 1st item in the menu
143 resources = Storage(
144 gis_location = {'importer' : True}
145 )
146 ),
147 mpr = Storage(
148 name_nice = Tstr("Missing Persons"),
149 description = Tstr("Helps to report and search for Missing Persons"),
150 module_type = 2,
151 ),
152 rms = Storage(
153 name_nice = Tstr("Requests"),
154 description = Tstr("Tracks requests for aid and matches them against donors who have pledged aid"),
155 module_type = 3,
156 resources = Storage(
157 rms_req = {'importer' : True},
158 )
159 ),
160 hms = Storage(
161 name_nice = Tstr("Hospitals"),
162 description = Tstr("Helps to monitor status of hospitals"),
163 module_type = 4,
164 resources = Storage(
165 hms_hospital = {'importer' : True}
166 )
167 ),
168 vol = Storage(
169 name_nice = Tstr("Volunteers"),
170 description = Tstr("Manage volunteers by capturing their skills, availability and allocation"),
171 module_type = 5,
172 ),
173 logs = Storage(
174 name_nice = Tstr("Logistics Management"),
175 description = Tstr("Managing, Storing and Distributing Relief Items"),
176 module_type = 10
177 ),
178 project = Storage(
179 name_nice = Tstr("Project Management"),
180 description = Tstr("Project Activities"),
181 module_type = 10
182 ),
183 msg = Storage(
184 name_nice = Tstr("Messaging"),
185 description = Tstr("Sends & Receives Alerts via Email & SMS"),
186 module_type = 10,
187 ),
188 flood = Storage(
189 name_nice = Tstr("Flood Alerts"),
190 description = Tstr("Flood Alerts show water levels in various parts of the country"),
191 module_type = 10
192 ),
193 sitrep = Storage(
194 name_nice = Tstr("Assessments"),
195 description = Tstr("Assessments are structured reports done by Professional Organisations - data includes WFP Assessments"),
196 module_type = 10
197 ),
198 rat = Storage(
199 name_nice = Tstr("Rapid Assessments"),
200 description = Tstr("Assessments are structured reports done by Professional Organisations"),
201 module_type = 10
202 ),
203 pr = Storage(
204 name_nice = Tstr("Person Registry"),
205 description = Tstr("Central point to record details on People"),
206 module_type = 10,
207 resources = Storage(
208 pr_address = {'importer' : True},
209 pr_pe_contact = {'importer' : True},
210 pr_presence = {'importer' : True},
211 pr_identity = {'importer' : True},
212 pr_person = {'importer' : True},
213 pr_group = {'importer' : True},
214 pr_group_membership = {'importer' : True},
215 )
216 ),
217 dvi = Storage(
218 name_nice = Tstr("Disaster Victim Identification"),
219 description = Tstr("Disaster Victim Identification"),
220 module_type = 10,
221 resources = Storage(
222 dvi_recreq = {'importer' : True},
223 )
224 ),
225 #dvr = Storage(
226 # name_nice = Tstr("Disaster Victim Registry"),
227 # description = Tstr("Traces internally displaced people (IDPs) and their needs"),
228 # module_type = 10
229 # ),
230 budget = Storage(
231 name_nice = Tstr("Budgeting Module"),
232 description = Tstr("Allows a Budget to be drawn up"),
233 module_type = 10,
234 resources = Storage(
235 budget_item = {'importer' : True},
236 budget_kit = {'importer' : True},
237 budget_bundle = {'importer' : True},
238 )
239 ),
240 cr = Storage(
241 name_nice = Tstr("Shelter Registry"),
242 description = Tstr("Tracks the location, distibution, capacity and breakdown of victims in Shelters"),
243 module_type = 10,
244 resources = Storage(
245 cr_shelter = {'importer' : True }
246 )
247 ),
248 delphi = Storage(
249 name_nice = Tstr("Delphi Decision Maker"),
250 description = Tstr("Supports the decision making of large groups of Crisis Management Experts by helping the groups create ranked list."),
251 module_type = 10,
252 ),
253 doc = Storage(
254 name_nice = Tstr("Documents and Photos"),
255 description = Tstr("A library of digital resources, such as photos, documents and reports"),
256 module_type = 10,
257 ),
258 irs = Storage(
259 name_nice = Tstr("Incident Reporting"),
260 description = Tstr("Incident Reporting System"),
261 module_type = 10
262 ),
263 org = Storage(
264 name_nice = Tstr("Organization Registry"),
265 description = Tstr('Lists "who is doing what & where". Allows relief agencies to coordinate their activities'),
266 module_type = 10,
267 resources = Storage(
268 org_organisation = {'importer' : True},
269 org_office = {'importer' : True},
270 org_project = {'importer' : True},
271 org_staff = {'importer' : True},
272 org_task = {'importer' : True}
273 )
274 ),
275 ticket = Storage(
276 name_nice = Tstr("Ticketing Module"),
277 description = Tstr("Master Message Log to process incoming reports & requests"),
278 module_type = 10,
279 ),
280 importer = Storage(
281 name_nice = "Spreadsheet Importer",
282 description = "Used to import data from spreadsheets into the database",
283 module_type = 10,
284 ),
285 survey = Storage(
286 name_nice = "Survey Module",
287 description = "Create, enter, and manage surveys.",
288 module_type = 10,
289 )
290
291 #lms = Storage(
292 # name_nice = Tstr("Logistics Management System"),
293 # description = Tstr("An intake system, a warehouse management system, commodity tracking, supply chain management, procurement and other asset and resource management capabilities."),
294 # module_type = 10
295 # ),
296)
290297
=== modified file 'models/msg.py' (properties changed: +x to -x)
=== added file 'models/survey.py'
--- models/survey.py 1970-01-01 00:00:00 +0000
+++ models/survey.py 2010-09-02 01:18:42 +0000
@@ -0,0 +1,142 @@
1# -*- coding: utf-8 -*-
2
3"""
4 Survey Module
5
6 @author: Robert O'Connor
7"""
8
9module = "survey"
10
11from gluon.sqlhtml import *
12
13
14ADD_LOCATION = T("Add Location")
15repr_select = lambda l: len(l.name) > 48 and "%s..." % l.name[:44] or l.name
16location_id = db.Table(None, "location_id",
17 FieldS3("location_id", db.gis_location, sortby="name",
18 requires = IS_NULL_OR(IS_ONE_OF(db, "gis_location.id", repr_select, sort=True)),
19 represent = lambda id: shn_gis_location_represent(id),
20 label = T("Location"),
21 comment = DIV(SPAN("* ", _class="req"),A(ADD_LOCATION,
22 _class="colorbox",
23 _href=URL(r=request, c="gis", f="location", args="create", vars=dict(format="popup")),
24 _target="top",
25 _title=ADD_LOCATION),
26 DIV( _class="tooltip",
27 _title=Tstr("Location") + "|" + Tstr("The Location of this Site, which can be general (for Reporting) or precise (for displaying on a Map)."))),
28 ondelete = "RESTRICT"))
29
30s3xrc.model.configure(db.gis_location,
31 onvalidation=lambda form: gis.wkt_centroid(form),
32 onaccept=gis.update_location_tree())
33
34
35if deployment_settings.has_module(module):
36
37 #Reusable table
38 name_desc = db.Table(db,timestamp, uuidstamp, deletion_status, authorstamp,
39 Field("name", "string", default="", length=120),
40 Field("description", "text", default="",length=500))
41
42 #Survey Template
43 resource = "template"
44 tablename = module + "_" + resource
45 template = db.define_table(tablename,name_desc,
46 Field("table_name","string",readable=False,writable=False),
47 Field("locked","boolean",readable=False,writable=False),
48 person_id,
49 organisation_id)
50
51 # Survey Series
52 resource = "series"
53 tablename = module + "_" + resource
54 series = db.define_table(tablename,name_desc,
55 Field("survey_template_id", db.survey_template),
56 Field("from_date", "date", default=None),
57 Field("to_date","date",default=None),
58 location_id)
59
60 # Survey Section
61 resource = "questions"
62 tablename = module +"_" + resource
63 section = db.define_table(tablename,uuidstamp,deletion_status,authorstamp)
64
65# # Question options e.g., Row choices, Column Choices, Layout Configuration data, etc...
66# resource = "question_options"
67# tablename = module +"_" + resource
68# question_options = db.define_table(tablename,uuidstamp,deletion_status,authorstamp,
69## # Field("display_option","integer"),
70### Field("answer_choices","text",length=700),
71### Field("row_choices","text"), # row choices
72### Field("column_choices","text"), # column choices
73## Field("tf_choices","text"), # text before the text fields.
74# Field("tf_ta_columns","integer"), # number of columns for TF/TA
75# Field("ta_rows","integer"), # number of rows for text areas
76### Field("number_of_options","integer"),
77# Field("allow_comments","boolean"), # whether or not to allow comments
78# Field("comment_display_label"), # the label for the comment field
79# Field("required","boolean"), # marks the question as required
80## Field("validate","boolean"), # whether or not to enable validation
81### Field("validation_options","integer"), # pre-set validation regexps and such.
82# Field("aggregation_type","string"))
83
84 # Survey Question
85 resource = "question"
86 tablename = module +"_" + resource
87 question = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
88 Field("name", "string", default="", length=120),
89 Field("question_type","integer"),
90 Field("description", "text", default="",length=500))
91# Field("options_id",db.survey_question_options),
92# Field("tf_ta_columns","integer"), # number of columns for TF/TA
93# Field("ta_rows","integer"), # number of rows for text areas
94# Field("allow_comments","boolean"), # whether or not to allow comments
95# Field("comment_display_label"), # the label for the comment field
96# Field("required","boolean"), # marks the question as required
97# Field("aggregation_type","string"))
98
99
100# #Survey Instance
101# resource = "instance"
102# tablename = module +"_" + resource
103# instance = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
104# Field("survey_series_id",db.survey_series))
105
106# # Survey Answer
107# resource = "answer"
108# tablename = module +"_" + resource
109# answer = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
110# Field("survey_instance_id",db.survey_instance),
111# Field("question_id",db.survey_question),
112# Field("answer_value","text",length=600),
113# Field("answer_image","upload"), # store the image if "Image" is selected.
114# Field("answer_location",db.gis_location),
115# Field("answer_person",db.pr_person),
116# Field("answer_organisation",db.org_organisation))
117
118 # Link table
119 resource = "template_link"
120 tablename = module +"_" + resource
121 link_table = db.define_table(tablename,timestamp, uuidstamp, deletion_status, authorstamp,
122 Field("survey_question_id",db.survey_question),
123 Field("survey_template_id", db.survey_template),
124 Field("survey_questions_id", db.survey_questions))
125 link_table.survey_question_id.requires =IS_NULL_OR(IS_ONE_OF(db, "survey_question.id", "%(name)s"))
126
127
128# def question_options_onaccept(form):
129# if form.vars.id and session.rcvars.survey_question:
130# table = db.survey_question_options
131# opts = db(table.id == form.vars.id).update(question_id=session.rcvars.survey_question)
132# db.commit()
133#
134# s3xrc.model.configure(db.survey_question_options,
135# onaccept=lambda form: question_options_onaccept(form))
136
137# def question_onaccept(form):
138# if form.vars.id and session.rcvars.survey_section and session.rcvars.survey_template:
139# db.survey_template_link_table.insert(survey_section_id=session.rcvars.survey_section, survey_template_id=session.rcvars.survey_template)
140# db.commit()
141# s3xrc.model.configure(db.survey_question,
142# onaccept=lambda form: question_onaccept(form))
0\ No newline at end of file143\ No newline at end of file
1144
=== modified file 'modules/validators.py'
--- modules/validators.py 2010-08-15 21:47:42 +0000
+++ modules/validators.py 2010-09-02 01:18:42 +0000
@@ -489,4 +489,4 @@
489489
490 def formatter(self, value):490 def formatter(self, value):
491 # Always format with trailing UTC offset491 # Always format with trailing UTC offset
492 return value.strftime(str(self.format)) + " +0000"492 return value.strftime(str(self.format)) + " +0000"
493\ No newline at end of file493\ No newline at end of file
494494
=== modified file 'modules/widgets.py'
--- modules/widgets.py 2010-08-25 11:30:41 +0000
+++ modules/widgets.py 2010-09-02 01:18:42 +0000
@@ -619,4 +619,4 @@
619 else:619 else:
620 return_value = return_value620 return_value = return_value
621621
622 return return_value622 return return_value
623\ No newline at end of file623\ No newline at end of file
624624
=== added directory 'static/img/tango'
=== added file 'static/img/tango/down.png'
625Binary 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 differ625Binary 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
=== added file 'static/img/tango/minus.png'
626Binary 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 differ626Binary 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
=== added file 'static/img/tango/plus.png'
627Binary 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 differ627Binary 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
=== added file 'static/img/tango/up.png'
628Binary 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 differ628Binary 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
=== modified file 'static/scripts/S3/S3.js' (properties changed: +x to -x)
=== added directory 'static/scripts/survey'
=== added file 'static/scripts/survey/jquery-ui-1.8.4.custom.js'
--- static/scripts/survey/jquery-ui-1.8.4.custom.js 1970-01-01 00:00:00 +0000
+++ static/scripts/survey/jquery-ui-1.8.4.custom.js 2010-09-02 01:18:42 +0000
@@ -0,0 +1,11241 @@
1/*!
2 * jQuery UI 1.8.4
3 *
4 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
7 *
8 * http://docs.jquery.com/UI
9 */
10(function( $, undefined ) {
11
12// prevent duplicate loading
13// this is only a problem because we proxy existing functions
14// and we don't want to double proxy them
15$.ui = $.ui || {};
16if ( $.ui.version ) {
17 return;
18}
19
20//Helper functions and ui object
21$.extend( $.ui, {
22 version: "1.8.4",
23
24 // $.ui.plugin is deprecated. Use the proxy pattern instead.
25 plugin: {
26 add: function( module, option, set ) {
27 var proto = $.ui[ moduleĀ ].prototype;
28 for ( var i in set ) {
29 proto.plugins[ i ] = proto.plugins[ i ] || [];
30 proto.plugins[ i ].push( [ option, set[ i ] ] );
31 }
32 },
33 call: function( instance, name, args ) {
34 var set = instance.plugins[ name ];
35 if ( !set || !instance.element[ 0 ].parentNode ) {
36 return;
37 }
38
39 for ( var i = 0; i < set.length; i++ ) {
40 if ( instance.options[ set[ i ][ 0 ] ] ) {
41 set[ i ][ 1 ].apply( instance.element, args );
42 }
43 }
44 }
45 },
46
47 contains: function( a, b ) {
48 return document.compareDocumentPosition ?
49 a.compareDocumentPosition( b ) & 16 :
50 a !== b && a.contains( b );
51 },
52
53 hasScroll: function( el, a ) {
54
55 //If overflow is hidden, the element might have extra content, but the user wants to hide it
56 if ( $( el ).css( "overflow" ) === "hidden") {
57 return false;
58 }
59
60 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
61 has = false;
62
63 if ( el[ scroll ] > 0 ) {
64 return true;
65 }
66
67 // TODO: determine which cases actually cause this to happen
68 // if the element doesn't have the scroll set, see if it's possible to
69 // set the scroll
70 el[ scroll ] = 1;
71 has = ( el[ scroll ] > 0 );
72 el[ scroll ] = 0;
73 return has;
74 },
75
76 isOverAxis: function( x, reference, size ) {
77 //Determines when x coordinate is over "b" element axis
78 return ( x > reference ) && ( x < ( reference + size ) );
79 },
80
81 isOver: function( y, x, top, left, height, width ) {
82 //Determines when x, y coordinates is over "b" element
83 return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
84 },
85
86 keyCode: {
87 ALT: 18,
88 BACKSPACE: 8,
89 CAPS_LOCK: 20,
90 COMMA: 188,
91 COMMAND: 91,
92 COMMAND_LEFT: 91, // COMMAND
93 COMMAND_RIGHT: 93,
94 CONTROL: 17,
95 DELETE: 46,
96 DOWN: 40,
97 END: 35,
98 ENTER: 13,
99 ESCAPE: 27,
100 HOME: 36,
101 INSERT: 45,
102 LEFT: 37,
103 MENU: 93, // COMMAND_RIGHT
104 NUMPAD_ADD: 107,
105 NUMPAD_DECIMAL: 110,
106 NUMPAD_DIVIDE: 111,
107 NUMPAD_ENTER: 108,
108 NUMPAD_MULTIPLY: 106,
109 NUMPAD_SUBTRACT: 109,
110 PAGE_DOWN: 34,
111 PAGE_UP: 33,
112 PERIOD: 190,
113 RIGHT: 39,
114 SHIFT: 16,
115 SPACE: 32,
116 TAB: 9,
117 UP: 38,
118 WINDOWS: 91 // COMMAND
119 }
120});
121
122//jQuery plugins
123$.fn.extend({
124 _focus: $.fn.focus,
125 focus: function( delay, fn ) {
126 return typeof delay === "number" ?
127 this.each(function() {
128 var elem = this;
129 setTimeout(function() {
130 $( elem ).focus();
131 if ( fn ) {
132 fn.call( elem );
133 }
134 }, delay );
135 }) :
136 this._focus.apply( this, arguments );
137 },
138
139 enableSelection: function() {
140 return this
141 .attr( "unselectable", "off" )
142 .css( "MozUserSelect", "" );
143 },
144
145 disableSelection: function() {
146 return this
147 .attr( "unselectable", "on" )
148 .css( "MozUserSelect", "none" );
149 },
150
151 scrollParent: function() {
152 var scrollParent;
153 if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
154 scrollParent = this.parents().filter(function() {
155 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));
156 }).eq(0);
157 } else {
158 scrollParent = this.parents().filter(function() {
159 return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
160 }).eq(0);
161 }
162
163 return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
164 },
165
166 zIndex: function( zIndex ) {
167 if ( zIndex !== undefined ) {
168 return this.css( "zIndex", zIndex );
169 }
170
171 if ( this.length ) {
172 var elem = $( this[ 0 ] ), position, value;
173 while ( elem.length && elem[ 0 ] !== document ) {
174 // Ignore z-index if position is set to a value where z-index is ignored by the browser
175 // This makes behavior of this function consistent across browsers
176 // WebKit always returns auto if the element is positioned
177 position = elem.css( "position" );
178 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
179 // IE returns 0 when zIndex is not specified
180 // other browsers return a string
181 // we ignore the case of nested elements with an explicit value of 0
182 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
183 value = parseInt( elem.css( "zIndex" ) );
184 if ( !isNaN( value ) && value != 0 ) {
185 return value;
186 }
187 }
188 elem = elem.parent();
189 }
190 }
191
192 return 0;
193 }
194});
195
196$.each( [ "Width", "Height" ], function( i, name ) {
197 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
198 type = name.toLowerCase(),
199 orig = {
200 innerWidth: $.fn.innerWidth,
201 innerHeight: $.fn.innerHeight,
202 outerWidth: $.fn.outerWidth,
203 outerHeight: $.fn.outerHeight
204 };
205
206 function reduce( elem, size, border, margin ) {
207 $.each( side, function() {
208 size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
209 if ( border ) {
210 size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
211 }
212 if ( margin ) {
213 size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
214 }
215 });
216 return size;
217 }
218
219 $.fn[ "inner" + name ] = function( size ) {
220 if ( size === undefined ) {
221 return orig[ "inner" + name ].call( this );
222 }
223
224 return this.each(function() {
225 $.style( this, type, reduce( this, size ) + "px" );
226 });
227 };
228
229 $.fn[ "outer" + name] = function( size, margin ) {
230 if ( typeof size !== "number" ) {
231 return orig[ "outer" + name ].call( this, size );
232 }
233
234 return this.each(function() {
235 $.style( this, type, reduce( this, size, true, margin ) + "px" );
236 });
237 };
238});
239
240//Additional selectors
241function visible( element ) {
242 return !$( element ).parents().andSelf().filter(function() {
243 return $.curCSS( this, "visibility" ) === "hidden" ||
244 $.expr.filters.hidden( this );
245 }).length;
246}
247
248$.extend( $.expr[ ":" ], {
249 data: function( elem, i, match ) {
250 return !!$.data( elem, match[ 3 ] );
251 },
252
253 focusable: function( element ) {
254 var nodeName = element.nodeName.toLowerCase(),
255 tabIndex = $.attr( element, "tabindex" );
256 if ( "area" === nodeName ) {
257 var map = element.parentNode,
258 mapName = map.name,
259 img;
260 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
261 return false;
262 }
263 img = $( "img[usemap=#" + mapName + "]" )[0];
264 return !!img && visible( img );
265 }
266 return ( /input|select|textarea|button|object/.test( nodeName )
267 ? !element.disabled
268 : "a" == nodeName
269 ? element.href || !isNaN( tabIndex )
270 : !isNaN( tabIndex ))
271 // the element and all of its ancestors must be visible
272 && visible( element );
273 },
274
275 tabbable: function( element ) {
276 var tabIndex = $.attr( element, "tabindex" );
277 return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" );
278 }
279});
280
281})( jQuery );
282/*!
283 * jQuery UI Widget 1.8.4
284 *
285 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
286 * Dual licensed under the MIT or GPL Version 2 licenses.
287 * http://jquery.org/license
288 *
289 * http://docs.jquery.com/UI/Widget
290 */
291(function( $, undefined ) {
292
293var _remove = $.fn.remove;
294
295$.fn.remove = function( selector, keepData ) {
296 return this.each(function() {
297 if ( !keepData ) {
298 if ( !selector || $.filter( selector, [ this ] ).length ) {
299 $( "*", this ).add( [ this ] ).each(function() {
300 $( this ).triggerHandler( "remove" );
301 });
302 }
303 }
304 return _remove.call( $(this), selector, keepData );
305 });
306};
307
308$.widget = function( name, base, prototype ) {
309 var namespace = name.split( "." )[ 0 ],
310 fullName;
311 name = name.split( "." )[ 1 ];
312 fullName = namespace + "-" + name;
313
314 if ( !prototype ) {
315 prototype = base;
316 base = $.Widget;
317 }
318
319 // create selector for plugin
320 $.expr[ ":" ][ fullName ] = function( elem ) {
321 return !!$.data( elem, name );
322 };
323
324 $[ namespace ] = $[ namespace ] || {};
325 $[ namespace ][ name ] = function( options, element ) {
326 // allow instantiation without initializing for simple inheritance
327 if ( arguments.length ) {
328 this._createWidget( options, element );
329 }
330 };
331
332 var basePrototype = new base();
333 // we need to make the options hash a property directly on the new instance
334 // otherwise we'll modify the options hash on the prototype that we're
335 // inheriting from
336// $.each( basePrototype, function( key, val ) {
337// if ( $.isPlainObject(val) ) {
338// basePrototype[ key ] = $.extend( {}, val );
339// }
340// });
341 basePrototype.options = $.extend( true, {}, basePrototype.options );
342 $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
343 namespace: namespace,
344 widgetName: name,
345 widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
346 widgetBaseClass: fullName
347 }, prototype );
348
349 $.widget.bridge( name, $[ namespace ][ name ] );
350};
351
352$.widget.bridge = function( name, object ) {
353 $.fn[ name ] = function( options ) {
354 var isMethodCall = typeof options === "string",
355 args = Array.prototype.slice.call( arguments, 1 ),
356 returnValue = this;
357
358 // allow multiple hashes to be passed on init
359 options = !isMethodCall && args.length ?
360 $.extend.apply( null, [ true, options ].concat(args) ) :
361 options;
362
363 // prevent calls to internal methods
364 if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
365 return returnValue;
366 }
367
368 if ( isMethodCall ) {
369 this.each(function() {
370 var instance = $.data( this, name ),
371 methodValue = instance && $.isFunction( instance[options] ) ?
372 instance[ options ].apply( instance, args ) :
373 instance;
374 if ( methodValue !== instance && methodValue !== undefined ) {
375 returnValue = methodValue;
376 return false;
377 }
378 });
379 } else {
380 this.each(function() {
381 var instance = $.data( this, name );
382 if ( instance ) {
383 if ( options ) {
384 instance.option( options );
385 }
386 instance._init();
387 } else {
388 $.data( this, name, new object( options, this ) );
389 }
390 });
391 }
392
393 return returnValue;
394 };
395};
396
397$.Widget = function( options, element ) {
398 // allow instantiation without initializing for simple inheritance
399 if ( arguments.length ) {
400 this._createWidget( options, element );
401 }
402};
403
404$.Widget.prototype = {
405 widgetName: "widget",
406 widgetEventPrefix: "",
407 options: {
408 disabled: false
409 },
410 _createWidget: function( options, element ) {
411 // $.widget.bridge stores the plugin instance, but we do it anyway
412 // so that it's stored even before the _create function runs
413 $.data( element, this.widgetName, this );
414 this.element = $( element );
415 this.options = $.extend( true, {},
416 this.options,
417 $.metadata && $.metadata.get( element )[ this.widgetName ],
418 options );
419
420 var self = this;
421 this.element.bind( "remove." + this.widgetName, function() {
422 self.destroy();
423 });
424
425 this._create();
426 this._init();
427 },
428 _create: function() {},
429 _init: function() {},
430
431 destroy: function() {
432 this.element
433 .unbind( "." + this.widgetName )
434 .removeData( this.widgetName );
435 this.widget()
436 .unbind( "." + this.widgetName )
437 .removeAttr( "aria-disabled" )
438 .removeClass(
439 this.widgetBaseClass + "-disabled " +
440 "ui-state-disabled" );
441 },
442
443 widget: function() {
444 return this.element;
445 },
446
447 option: function( key, value ) {
448 var options = key,
449 self = this;
450
451 if ( arguments.length === 0 ) {
452 // don't return a reference to the internal hash
453 return $.extend( {}, self.options );
454 }
455
456 if (typeof key === "string" ) {
457 if ( value === undefined ) {
458 return this.options[ key ];
459 }
460 options = {};
461 options[ key ] = value;
462 }
463
464 $.each( options, function( key, value ) {
465 self._setOption( key, value );
466 });
467
468 return self;
469 },
470 _setOption: function( key, value ) {
471 this.options[ key ] = value;
472
473 if ( key === "disabled" ) {
474 this.widget()
475 [ value ? "addClass" : "removeClass"](
476 this.widgetBaseClass + "-disabled" + " " +
477 "ui-state-disabled" )
478 .attr( "aria-disabled", value );
479 }
480
481 return this;
482 },
483
484 enable: function() {
485 return this._setOption( "disabled", false );
486 },
487 disable: function() {
488 return this._setOption( "disabled", true );
489 },
490
491 _trigger: function( type, event, data ) {
492 var callback = this.options[ type ];
493
494 event = $.Event( event );
495 event.type = ( type === this.widgetEventPrefix ?
496 type :
497 this.widgetEventPrefix + type ).toLowerCase();
498 data = data || {};
499
500 // copy original event properties over to the new event
501 // this would happen if we could call $.event.fix instead of $.Event
502 // but we don't have a way to force an event to be fixed multiple times
503 if ( event.originalEvent ) {
504 for ( var i = $.event.props.length, prop; i; ) {
505 prop = $.event.props[ --i ];
506 event[ prop ] = event.originalEvent[ prop ];
507 }
508 }
509
510 this.element.trigger( event, data );
511
512 return !( $.isFunction(callback) &&
513 callback.call( this.element[0], event, data ) === false ||
514 event.isDefaultPrevented() );
515 }
516};
517
518})( jQuery );
519/*!
520 * jQuery UI Mouse 1.8.4
521 *
522 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
523 * Dual licensed under the MIT or GPL Version 2 licenses.
524 * http://jquery.org/license
525 *
526 * http://docs.jquery.com/UI/Mouse
527 *
528 * Depends:
529 * jquery.ui.widget.js
530 */
531(function( $, undefined ) {
532
533$.widget("ui.mouse", {
534 options: {
535 cancel: ':input,option',
536 distance: 1,
537 delay: 0
538 },
539 _mouseInit: function() {
540 var self = this;
541
542 this.element
543 .bind('mousedown.'+this.widgetName, function(event) {
544 return self._mouseDown(event);
545 })
546 .bind('click.'+this.widgetName, function(event) {
547 if(self._preventClickEvent) {
548 self._preventClickEvent = false;
549 event.stopImmediatePropagation();
550 return false;
551 }
552 });
553
554 this.started = false;
555 },
556
557 // TODO: make sure destroying one instance of mouse doesn't mess with
558 // other instances of mouse
559 _mouseDestroy: function() {
560 this.element.unbind('.'+this.widgetName);
561 },
562
563 _mouseDown: function(event) {
564 // don't let more than one widget handle mouseStart
565 // TODO: figure out why we have to use originalEvent
566 event.originalEvent = event.originalEvent || {};
567 if (event.originalEvent.mouseHandled) { return; }
568
569 // we may have missed mouseup (out of window)
570 (this._mouseStarted && this._mouseUp(event));
571
572 this._mouseDownEvent = event;
573
574 var self = this,
575 btnIsLeft = (event.which == 1),
576 elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
577 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
578 return true;
579 }
580
581 this.mouseDelayMet = !this.options.delay;
582 if (!this.mouseDelayMet) {
583 this._mouseDelayTimer = setTimeout(function() {
584 self.mouseDelayMet = true;
585 }, this.options.delay);
586 }
587
588 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
589 this._mouseStarted = (this._mouseStart(event) !== false);
590 if (!this._mouseStarted) {
591 event.preventDefault();
592 return true;
593 }
594 }
595
596 // these delegates are required to keep context
597 this._mouseMoveDelegate = function(event) {
598 return self._mouseMove(event);
599 };
600 this._mouseUpDelegate = function(event) {
601 return self._mouseUp(event);
602 };
603 $(document)
604 .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
605 .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
606
607 // preventDefault() is used to prevent the selection of text here -
608 // however, in Safari, this causes select boxes not to be selectable
609 // anymore, so this fix is needed
610 ($.browser.safari || event.preventDefault());
611
612 event.originalEvent.mouseHandled = true;
613 return true;
614 },
615
616 _mouseMove: function(event) {
617 // IE mouseup check - mouseup happened when mouse was out of window
618 if ($.browser.msie && !event.button) {
619 return this._mouseUp(event);
620 }
621
622 if (this._mouseStarted) {
623 this._mouseDrag(event);
624 return event.preventDefault();
625 }
626
627 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
628 this._mouseStarted =
629 (this._mouseStart(this._mouseDownEvent, event) !== false);
630 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
631 }
632
633 return !this._mouseStarted;
634 },
635
636 _mouseUp: function(event) {
637 $(document)
638 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
639 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
640
641 if (this._mouseStarted) {
642 this._mouseStarted = false;
643 this._preventClickEvent = (event.target == this._mouseDownEvent.target);
644 this._mouseStop(event);
645 }
646
647 return false;
648 },
649
650 _mouseDistanceMet: function(event) {
651 return (Math.max(
652 Math.abs(this._mouseDownEvent.pageX - event.pageX),
653 Math.abs(this._mouseDownEvent.pageY - event.pageY)
654 ) >= this.options.distance
655 );
656 },
657
658 _mouseDelayMet: function(event) {
659 return this.mouseDelayMet;
660 },
661
662 // These are placeholder methods, to be overriden by extending plugin
663 _mouseStart: function(event) {},
664 _mouseDrag: function(event) {},
665 _mouseStop: function(event) {},
666 _mouseCapture: function(event) { return true; }
667});
668
669})(jQuery);
670/*
671 * jQuery UI Position 1.8.4
672 *
673 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
674 * Dual licensed under the MIT or GPL Version 2 licenses.
675 * http://jquery.org/license
676 *
677 * http://docs.jquery.com/UI/Position
678 */
679(function( $, undefined ) {
680
681$.ui = $.ui || {};
682
683var horizontalPositions = /left|center|right/,
684 horizontalDefault = "center",
685 verticalPositions = /top|center|bottom/,
686 verticalDefault = "center",
687 _position = $.fn.position,
688 _offset = $.fn.offset;
689
690$.fn.position = function( options ) {
691 if ( !options || !options.of ) {
692 return _position.apply( this, arguments );
693 }
694
695 // make a copy, we don't want to modify arguments
696 options = $.extend( {}, options );
697
698 var target = $( options.of ),
699 collision = ( options.collision || "flip" ).split( " " ),
700 offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
701 targetWidth,
702 targetHeight,
703 basePosition;
704
705 if ( options.of.nodeType === 9 ) {
706 targetWidth = target.width();
707 targetHeight = target.height();
708 basePosition = { top: 0, left: 0 };
709 } else if ( options.of.scrollTo && options.of.document ) {
710 targetWidth = target.width();
711 targetHeight = target.height();
712 basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
713 } else if ( options.of.preventDefault ) {
714 // force left top to allow flipping
715 options.at = "left top";
716 targetWidth = targetHeight = 0;
717 basePosition = { top: options.of.pageY, left: options.of.pageX };
718 } else {
719 targetWidth = target.outerWidth();
720 targetHeight = target.outerHeight();
721 basePosition = target.offset();
722 }
723
724 // force my and at to have valid horizontal and veritcal positions
725 // if a value is missing or invalid, it will be converted to center
726 $.each( [ "my", "at" ], function() {
727 var pos = ( options[this] || "" ).split( " " );
728 if ( pos.length === 1) {
729 pos = horizontalPositions.test( pos[0] ) ?
730 pos.concat( [verticalDefault] ) :
731 verticalPositions.test( pos[0] ) ?
732 [ horizontalDefault ].concat( pos ) :
733 [ horizontalDefault, verticalDefault ];
734 }
735 pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : horizontalDefault;
736 pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : verticalDefault;
737 options[ this ] = pos;
738 });
739
740 // normalize collision option
741 if ( collision.length === 1 ) {
742 collision[ 1 ] = collision[ 0 ];
743 }
744
745 // normalize offset option
746 offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
747 if ( offset.length === 1 ) {
748 offset[ 1 ] = offset[ 0 ];
749 }
750 offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
751
752 if ( options.at[0] === "right" ) {
753 basePosition.left += targetWidth;
754 } else if (options.at[0] === horizontalDefault ) {
755 basePosition.left += targetWidth / 2;
756 }
757
758 if ( options.at[1] === "bottom" ) {
759 basePosition.top += targetHeight;
760 } else if ( options.at[1] === verticalDefault ) {
761 basePosition.top += targetHeight / 2;
762 }
763
764 basePosition.left += offset[ 0 ];
765 basePosition.top += offset[ 1 ];
766
767 return this.each(function() {
768 var elem = $( this ),
769 elemWidth = elem.outerWidth(),
770 elemHeight = elem.outerHeight(),
771 position = $.extend( {}, basePosition );
772
773 if ( options.my[0] === "right" ) {
774 position.left -= elemWidth;
775 } else if ( options.my[0] === horizontalDefault ) {
776 position.left -= elemWidth / 2;
777 }
778
779 if ( options.my[1] === "bottom" ) {
780 position.top -= elemHeight;
781 } else if ( options.my[1] === verticalDefault ) {
782 position.top -= elemHeight / 2;
783 }
784
785 // prevent fractions (see #5280)
786 position.left = parseInt( position.left );
787 position.top = parseInt( position.top );
788
789 $.each( [ "left", "top" ], function( i, dir ) {
790 if ( $.ui.position[ collision[i] ] ) {
791 $.ui.position[ collision[i] ][ dir ]( position, {
792 targetWidth: targetWidth,
793 targetHeight: targetHeight,
794 elemWidth: elemWidth,
795 elemHeight: elemHeight,
796 offset: offset,
797 my: options.my,
798 at: options.at
799 });
800 }
801 });
802
803 if ( $.fn.bgiframe ) {
804 elem.bgiframe();
805 }
806 elem.offset( $.extend( position, { using: options.using } ) );
807 });
808};
809
810$.ui.position = {
811 fit: {
812 left: function( position, data ) {
813 var win = $( window ),
814 over = position.left + data.elemWidth - win.width() - win.scrollLeft();
815 position.left = over > 0 ? position.left - over : Math.max( 0, position.left );
816 },
817 top: function( position, data ) {
818 var win = $( window ),
819 over = position.top + data.elemHeight - win.height() - win.scrollTop();
820 position.top = over > 0 ? position.top - over : Math.max( 0, position.top );
821 }
822 },
823
824 flip: {
825 left: function( position, data ) {
826 if ( data.at[0] === "center" ) {
827 return;
828 }
829 var win = $( window ),
830 over = position.left + data.elemWidth - win.width() - win.scrollLeft(),
831 myOffset = data.my[ 0 ] === "left" ?
832 -data.elemWidth :
833 data.my[ 0 ] === "right" ?
834 data.elemWidth :
835 0,
836 offset = -2 * data.offset[ 0 ];
837 position.left += position.left < 0 ?
838 myOffset + data.targetWidth + offset :
839 over > 0 ?
840 myOffset - data.targetWidth + offset :
841 0;
842 },
843 top: function( position, data ) {
844 if ( data.at[1] === "center" ) {
845 return;
846 }
847 var win = $( window ),
848 over = position.top + data.elemHeight - win.height() - win.scrollTop(),
849 myOffset = data.my[ 1 ] === "top" ?
850 -data.elemHeight :
851 data.my[ 1 ] === "bottom" ?
852 data.elemHeight :
853 0,
854 atOffset = data.at[ 1 ] === "top" ?
855 data.targetHeight :
856 -data.targetHeight,
857 offset = -2 * data.offset[ 1 ];
858 position.top += position.top < 0 ?
859 myOffset + data.targetHeight + offset :
860 over > 0 ?
861 myOffset + atOffset + offset :
862 0;
863 }
864 }
865};
866
867// offset setter from jQuery 1.4
868if ( !$.offset.setOffset ) {
869 $.offset.setOffset = function( elem, options ) {
870 // set position first, in-case top/left are set even on static elem
871 if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
872 elem.style.position = "relative";
873 }
874 var curElem = $( elem ),
875 curOffset = curElem.offset(),
876 curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
877 curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
878 props = {
879 top: (options.top - curOffset.top) + curTop,
880 left: (options.left - curOffset.left) + curLeft
881 };
882
883 if ( 'using' in options ) {
884 options.using.call( elem, props );
885 } else {
886 curElem.css( props );
887 }
888 };
889
890 $.fn.offset = function( options ) {
891 var elem = this[ 0 ];
892 if ( !elem || !elem.ownerDocument ) { return null; }
893 if ( options ) {
894 return this.each(function() {
895 $.offset.setOffset( this, options );
896 });
897 }
898 return _offset.call( this );
899 };
900}
901
902}( jQuery ));
903/*
904 * jQuery UI Draggable 1.8.4
905 *
906 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
907 * Dual licensed under the MIT or GPL Version 2 licenses.
908 * http://jquery.org/license
909 *
910 * http://docs.jquery.com/UI/Draggables
911 *
912 * Depends:
913 * jquery.ui.core.js
914 * jquery.ui.mouse.js
915 * jquery.ui.widget.js
916 */
917(function( $, undefined ) {
918
919$.widget("ui.draggable", $.ui.mouse, {
920 widgetEventPrefix: "drag",
921 options: {
922 addClasses: true,
923 appendTo: "parent",
924 axis: false,
925 connectToSortable: false,
926 containment: false,
927 cursor: "auto",
928 cursorAt: false,
929 grid: false,
930 handle: false,
931 helper: "original",
932 iframeFix: false,
933 opacity: false,
934 refreshPositions: false,
935 revert: false,
936 revertDuration: 500,
937 scope: "default",
938 scroll: true,
939 scrollSensitivity: 20,
940 scrollSpeed: 20,
941 snap: false,
942 snapMode: "both",
943 snapTolerance: 20,
944 stack: false,
945 zIndex: false
946 },
947 _create: function() {
948
949 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
950 this.element[0].style.position = 'relative';
951
952 (this.options.addClasses && this.element.addClass("ui-draggable"));
953 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
954
955 this._mouseInit();
956
957 },
958
959 destroy: function() {
960 if(!this.element.data('draggable')) return;
961 this.element
962 .removeData("draggable")
963 .unbind(".draggable")
964 .removeClass("ui-draggable"
965 + " ui-draggable-dragging"
966 + " ui-draggable-disabled");
967 this._mouseDestroy();
968
969 return this;
970 },
971
972 _mouseCapture: function(event) {
973
974 var o = this.options;
975
976 // among others, prevent a drag on a resizable-handle
977 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
978 return false;
979
980 //Quit if we're not on a valid handle
981 this.handle = this._getHandle(event);
982 if (!this.handle)
983 return false;
984
985 return true;
986
987 },
988
989 _mouseStart: function(event) {
990
991 var o = this.options;
992
993 //Create and append the visible helper
994 this.helper = this._createHelper(event);
995
996 //Cache the helper size
997 this._cacheHelperProportions();
998
999 //If ddmanager is used for droppables, set the global draggable
1000 if($.ui.ddmanager)
1001 $.ui.ddmanager.current = this;
1002
1003 /*
1004 * - Position generation -
1005 * This block generates everything position related - it's the core of draggables.
1006 */
1007
1008 //Cache the margins of the original element
1009 this._cacheMargins();
1010
1011 //Store the helper's css position
1012 this.cssPosition = this.helper.css("position");
1013 this.scrollParent = this.helper.scrollParent();
1014
1015 //The element's absolute position on the page minus margins
1016 this.offset = this.positionAbs = this.element.offset();
1017 this.offset = {
1018 top: this.offset.top - this.margins.top,
1019 left: this.offset.left - this.margins.left
1020 };
1021
1022 $.extend(this.offset, {
1023 click: { //Where the click happened, relative to the element
1024 left: event.pageX - this.offset.left,
1025 top: event.pageY - this.offset.top
1026 },
1027 parent: this._getParentOffset(),
1028 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1029 });
1030
1031 //Generate the original position
1032 this.originalPosition = this.position = this._generatePosition(event);
1033 this.originalPageX = event.pageX;
1034 this.originalPageY = event.pageY;
1035
1036 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1037 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1038
1039 //Set a containment if given in the options
1040 if(o.containment)
1041 this._setContainment();
1042
1043 //Trigger event + callbacks
1044 if(this._trigger("start", event) === false) {
1045 this._clear();
1046 return false;
1047 }
1048
1049 //Recache the helper size
1050 this._cacheHelperProportions();
1051
1052 //Prepare the droppable offsets
1053 if ($.ui.ddmanager && !o.dropBehaviour)
1054 $.ui.ddmanager.prepareOffsets(this, event);
1055
1056 this.helper.addClass("ui-draggable-dragging");
1057 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1058 return true;
1059 },
1060
1061 _mouseDrag: function(event, noPropagation) {
1062
1063 //Compute the helpers position
1064 this.position = this._generatePosition(event);
1065 this.positionAbs = this._convertPositionTo("absolute");
1066
1067 //Call plugins and callbacks and use the resulting position if something is returned
1068 if (!noPropagation) {
1069 var ui = this._uiHash();
1070 if(this._trigger('drag', event, ui) === false) {
1071 this._mouseUp({});
1072 return false;
1073 }
1074 this.position = ui.position;
1075 }
1076
1077 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
1078 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
1079 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
1080
1081 return false;
1082 },
1083
1084 _mouseStop: function(event) {
1085
1086 //If we are using droppables, inform the manager about the drop
1087 var dropped = false;
1088 if ($.ui.ddmanager && !this.options.dropBehaviour)
1089 dropped = $.ui.ddmanager.drop(this, event);
1090
1091 //if a drop comes from outside (a sortable)
1092 if(this.dropped) {
1093 dropped = this.dropped;
1094 this.dropped = false;
1095 }
1096
1097 //if the original element is removed, don't bother to continue
1098 if(!this.element[0] || !this.element[0].parentNode)
1099 return false;
1100
1101 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))) {
1102 var self = this;
1103 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1104 if(self._trigger("stop", event) !== false) {
1105 self._clear();
1106 }
1107 });
1108 } else {
1109 if(this._trigger("stop", event) !== false) {
1110 this._clear();
1111 }
1112 }
1113
1114 return false;
1115 },
1116
1117 cancel: function() {
1118
1119 if(this.helper.is(".ui-draggable-dragging")) {
1120 this._mouseUp({});
1121 } else {
1122 this._clear();
1123 }
1124
1125 return this;
1126
1127 },
1128
1129 _getHandle: function(event) {
1130
1131 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
1132 $(this.options.handle, this.element)
1133 .find("*")
1134 .andSelf()
1135 .each(function() {
1136 if(this == event.target) handle = true;
1137 });
1138
1139 return handle;
1140
1141 },
1142
1143 _createHelper: function(event) {
1144
1145 var o = this.options;
1146 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
1147
1148 if(!helper.parents('body').length)
1149 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
1150
1151 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
1152 helper.css("position", "absolute");
1153
1154 return helper;
1155
1156 },
1157
1158 _adjustOffsetFromHelper: function(obj) {
1159 if (typeof obj == 'string') {
1160 obj = obj.split(' ');
1161 }
1162 if ($.isArray(obj)) {
1163 obj = {left: +obj[0], top: +obj[1] || 0};
1164 }
1165 if ('left' in obj) {
1166 this.offset.click.left = obj.left + this.margins.left;
1167 }
1168 if ('right' in obj) {
1169 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1170 }
1171 if ('top' in obj) {
1172 this.offset.click.top = obj.top + this.margins.top;
1173 }
1174 if ('bottom' in obj) {
1175 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1176 }
1177 },
1178
1179 _getParentOffset: function() {
1180
1181 //Get the offsetParent and cache its position
1182 this.offsetParent = this.helper.offsetParent();
1183 var po = this.offsetParent.offset();
1184
1185 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1186 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1187 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1188 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1189 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
1190 po.left += this.scrollParent.scrollLeft();
1191 po.top += this.scrollParent.scrollTop();
1192 }
1193
1194 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
1195 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
1196 po = { top: 0, left: 0 };
1197
1198 return {
1199 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1200 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1201 };
1202
1203 },
1204
1205 _getRelativeOffset: function() {
1206
1207 if(this.cssPosition == "relative") {
1208 var p = this.element.position();
1209 return {
1210 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1211 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1212 };
1213 } else {
1214 return { top: 0, left: 0 };
1215 }
1216
1217 },
1218
1219 _cacheMargins: function() {
1220 this.margins = {
1221 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1222 top: (parseInt(this.element.css("marginTop"),10) || 0)
1223 };
1224 },
1225
1226 _cacheHelperProportions: function() {
1227 this.helperProportions = {
1228 width: this.helper.outerWidth(),
1229 height: this.helper.outerHeight()
1230 };
1231 },
1232
1233 _setContainment: function() {
1234
1235 var o = this.options;
1236 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
1237 if(o.containment == 'document' || o.containment == 'window') this.containment = [
1238 0 - this.offset.relative.left - this.offset.parent.left,
1239 0 - this.offset.relative.top - this.offset.parent.top,
1240 $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
1241 ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1242 ];
1243
1244 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
1245 var ce = $(o.containment)[0]; if(!ce) return;
1246 var co = $(o.containment).offset();
1247 var over = ($(ce).css("overflow") != 'hidden');
1248
1249 this.containment = [
1250 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
1251 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
1252 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,
1253 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
1254 ];
1255 } else if(o.containment.constructor == Array) {
1256 this.containment = o.containment;
1257 }
1258
1259 },
1260
1261 _convertPositionTo: function(d, pos) {
1262
1263 if(!pos) pos = this.position;
1264 var mod = d == "absolute" ? 1 : -1;
1265 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);
1266
1267 return {
1268 top: (
1269 pos.top // The absolute mouse position
1270 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1271 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
1272 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1273 ),
1274 left: (
1275 pos.left // The absolute mouse position
1276 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1277 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
1278 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1279 )
1280 };
1281
1282 },
1283
1284 _generatePosition: function(event) {
1285
1286 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);
1287 var pageX = event.pageX;
1288 var pageY = event.pageY;
1289
1290 /*
1291 * - Position constraining -
1292 * Constrain the position to a mix of grid, containment.
1293 */
1294
1295 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1296
1297 if(this.containment) {
1298 if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
1299 if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
1300 if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
1301 if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
1302 }
1303
1304 if(o.grid) {
1305 var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
1306 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;
1307
1308 var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
1309 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;
1310 }
1311
1312 }
1313
1314 return {
1315 top: (
1316 pageY // The absolute mouse position
1317 - this.offset.click.top // Click offset (relative to the element)
1318 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
1319 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
1320 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1321 ),
1322 left: (
1323 pageX // The absolute mouse position
1324 - this.offset.click.left // Click offset (relative to the element)
1325 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
1326 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
1327 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1328 )
1329 };
1330
1331 },
1332
1333 _clear: function() {
1334 this.helper.removeClass("ui-draggable-dragging");
1335 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
1336 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
1337 this.helper = null;
1338 this.cancelHelperRemoval = false;
1339 },
1340
1341 // From now on bulk stuff - mainly helpers
1342
1343 _trigger: function(type, event, ui) {
1344 ui = ui || this._uiHash();
1345 $.ui.plugin.call(this, type, [event, ui]);
1346 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
1347 return $.Widget.prototype._trigger.call(this, type, event, ui);
1348 },
1349
1350 plugins: {},
1351
1352 _uiHash: function(event) {
1353 return {
1354 helper: this.helper,
1355 position: this.position,
1356 originalPosition: this.originalPosition,
1357 offset: this.positionAbs
1358 };
1359 }
1360
1361});
1362
1363$.extend($.ui.draggable, {
1364 version: "1.8.4"
1365});
1366
1367$.ui.plugin.add("draggable", "connectToSortable", {
1368 start: function(event, ui) {
1369
1370 var inst = $(this).data("draggable"), o = inst.options,
1371 uiSortable = $.extend({}, ui, { item: inst.element });
1372 inst.sortables = [];
1373 $(o.connectToSortable).each(function() {
1374 var sortable = $.data(this, 'sortable');
1375 if (sortable && !sortable.options.disabled) {
1376 inst.sortables.push({
1377 instance: sortable,
1378 shouldRevert: sortable.options.revert
1379 });
1380 sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
1381 sortable._trigger("activate", event, uiSortable);
1382 }
1383 });
1384
1385 },
1386 stop: function(event, ui) {
1387
1388 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1389 var inst = $(this).data("draggable"),
1390 uiSortable = $.extend({}, ui, { item: inst.element });
1391
1392 $.each(inst.sortables, function() {
1393 if(this.instance.isOver) {
1394
1395 this.instance.isOver = 0;
1396
1397 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1398 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1399
1400 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
1401 if(this.shouldRevert) this.instance.options.revert = true;
1402
1403 //Trigger the stop of the sortable
1404 this.instance._mouseStop(event);
1405
1406 this.instance.options.helper = this.instance.options._helper;
1407
1408 //If the helper has been the original item, restore properties in the sortable
1409 if(inst.options.helper == 'original')
1410 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
1411
1412 } else {
1413 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1414 this.instance._trigger("deactivate", event, uiSortable);
1415 }
1416
1417 });
1418
1419 },
1420 drag: function(event, ui) {
1421
1422 var inst = $(this).data("draggable"), self = this;
1423
1424 var checkPos = function(o) {
1425 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
1426 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
1427 var itemHeight = o.height, itemWidth = o.width;
1428 var itemTop = o.top, itemLeft = o.left;
1429
1430 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
1431 };
1432
1433 $.each(inst.sortables, function(i) {
1434
1435 //Copy over some variables to allow calling the sortable's native _intersectsWith
1436 this.instance.positionAbs = inst.positionAbs;
1437 this.instance.helperProportions = inst.helperProportions;
1438 this.instance.offset.click = inst.offset.click;
1439
1440 if(this.instance._intersectsWith(this.instance.containerCache)) {
1441
1442 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1443 if(!this.instance.isOver) {
1444
1445 this.instance.isOver = 1;
1446 //Now we fake the start of dragging for the sortable instance,
1447 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1448 //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)
1449 this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
1450 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1451 this.instance.options.helper = function() { return ui.helper[0]; };
1452
1453 event.target = this.instance.currentItem[0];
1454 this.instance._mouseCapture(event, true);
1455 this.instance._mouseStart(event, true, true);
1456
1457 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1458 this.instance.offset.click.top = inst.offset.click.top;
1459 this.instance.offset.click.left = inst.offset.click.left;
1460 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1461 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1462
1463 inst._trigger("toSortable", event);
1464 inst.dropped = this.instance.element; //draggable revert needs that
1465 //hack so receive/update callbacks work (mostly)
1466 inst.currentItem = inst.element;
1467 this.instance.fromOutside = inst;
1468
1469 }
1470
1471 //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
1472 if(this.instance.currentItem) this.instance._mouseDrag(event);
1473
1474 } else {
1475
1476 //If it doesn't intersect with the sortable, and it intersected before,
1477 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1478 if(this.instance.isOver) {
1479
1480 this.instance.isOver = 0;
1481 this.instance.cancelHelperRemoval = true;
1482
1483 //Prevent reverting on this forced stop
1484 this.instance.options.revert = false;
1485
1486 // The out event needs to be triggered independently
1487 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
1488
1489 this.instance._mouseStop(event, true);
1490 this.instance.options.helper = this.instance.options._helper;
1491
1492 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1493 this.instance.currentItem.remove();
1494 if(this.instance.placeholder) this.instance.placeholder.remove();
1495
1496 inst._trigger("fromSortable", event);
1497 inst.dropped = false; //draggable revert needs that
1498 }
1499
1500 };
1501
1502 });
1503
1504 }
1505});
1506
1507$.ui.plugin.add("draggable", "cursor", {
1508 start: function(event, ui) {
1509 var t = $('body'), o = $(this).data('draggable').options;
1510 if (t.css("cursor")) o._cursor = t.css("cursor");
1511 t.css("cursor", o.cursor);
1512 },
1513 stop: function(event, ui) {
1514 var o = $(this).data('draggable').options;
1515 if (o._cursor) $('body').css("cursor", o._cursor);
1516 }
1517});
1518
1519$.ui.plugin.add("draggable", "iframeFix", {
1520 start: function(event, ui) {
1521 var o = $(this).data('draggable').options;
1522 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1523 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1524 .css({
1525 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1526 position: "absolute", opacity: "0.001", zIndex: 1000
1527 })
1528 .css($(this).offset())
1529 .appendTo("body");
1530 });
1531 },
1532 stop: function(event, ui) {
1533 $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
1534 }
1535});
1536
1537$.ui.plugin.add("draggable", "opacity", {
1538 start: function(event, ui) {
1539 var t = $(ui.helper), o = $(this).data('draggable').options;
1540 if(t.css("opacity")) o._opacity = t.css("opacity");
1541 t.css('opacity', o.opacity);
1542 },
1543 stop: function(event, ui) {
1544 var o = $(this).data('draggable').options;
1545 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
1546 }
1547});
1548
1549$.ui.plugin.add("draggable", "scroll", {
1550 start: function(event, ui) {
1551 var i = $(this).data("draggable");
1552 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
1553 },
1554 drag: function(event, ui) {
1555
1556 var i = $(this).data("draggable"), o = i.options, scrolled = false;
1557
1558 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
1559
1560 if(!o.axis || o.axis != 'x') {
1561 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
1562 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1563 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
1564 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1565 }
1566
1567 if(!o.axis || o.axis != 'y') {
1568 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
1569 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1570 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
1571 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1572 }
1573
1574 } else {
1575
1576 if(!o.axis || o.axis != 'x') {
1577 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
1578 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1579 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
1580 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1581 }
1582
1583 if(!o.axis || o.axis != 'y') {
1584 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
1585 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1586 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
1587 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1588 }
1589
1590 }
1591
1592 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
1593 $.ui.ddmanager.prepareOffsets(i, event);
1594
1595 }
1596});
1597
1598$.ui.plugin.add("draggable", "snap", {
1599 start: function(event, ui) {
1600
1601 var i = $(this).data("draggable"), o = i.options;
1602 i.snapElements = [];
1603
1604 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
1605 var $t = $(this); var $o = $t.offset();
1606 if(this != i.element[0]) i.snapElements.push({
1607 item: this,
1608 width: $t.outerWidth(), height: $t.outerHeight(),
1609 top: $o.top, left: $o.left
1610 });
1611 });
1612
1613 },
1614 drag: function(event, ui) {
1615
1616 var inst = $(this).data("draggable"), o = inst.options;
1617 var d = o.snapTolerance;
1618
1619 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1620 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1621
1622 for (var i = inst.snapElements.length - 1; i >= 0; i--){
1623
1624 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
1625 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
1626
1627 //Yes, I know, this is insane ;)
1628 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))) {
1629 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 })));
1630 inst.snapElements[i].snapping = false;
1631 continue;
1632 }
1633
1634 if(o.snapMode != 'inner') {
1635 var ts = Math.abs(t - y2) <= d;
1636 var bs = Math.abs(b - y1) <= d;
1637 var ls = Math.abs(l - x2) <= d;
1638 var rs = Math.abs(r - x1) <= d;
1639 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1640 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1641 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1642 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1643 }
1644
1645 var first = (ts || bs || ls || rs);
1646
1647 if(o.snapMode != 'outer') {
1648 var ts = Math.abs(t - y1) <= d;
1649 var bs = Math.abs(b - y2) <= d;
1650 var ls = Math.abs(l - x1) <= d;
1651 var rs = Math.abs(r - x2) <= d;
1652 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1653 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1654 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1655 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1656 }
1657
1658 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
1659 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1660 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1661
1662 };
1663
1664 }
1665});
1666
1667$.ui.plugin.add("draggable", "stack", {
1668 start: function(event, ui) {
1669
1670 var o = $(this).data("draggable").options;
1671
1672 var group = $.makeArray($(o.stack)).sort(function(a,b) {
1673 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1674 });
1675 if (!group.length) { return; }
1676
1677 var min = parseInt(group[0].style.zIndex) || 0;
1678 $(group).each(function(i) {
1679 this.style.zIndex = min + i;
1680 });
1681
1682 this[0].style.zIndex = min + group.length;
1683
1684 }
1685});
1686
1687$.ui.plugin.add("draggable", "zIndex", {
1688 start: function(event, ui) {
1689 var t = $(ui.helper), o = $(this).data("draggable").options;
1690 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
1691 t.css('zIndex', o.zIndex);
1692 },
1693 stop: function(event, ui) {
1694 var o = $(this).data("draggable").options;
1695 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
1696 }
1697});
1698
1699})(jQuery);
1700/*
1701 * jQuery UI Droppable 1.8.4
1702 *
1703 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1704 * Dual licensed under the MIT or GPL Version 2 licenses.
1705 * http://jquery.org/license
1706 *
1707 * http://docs.jquery.com/UI/Droppables
1708 *
1709 * Depends:
1710 * jquery.ui.core.js
1711 * jquery.ui.widget.js
1712 * jquery.ui.mouse.js
1713 * jquery.ui.draggable.js
1714 */
1715(function( $, undefined ) {
1716
1717$.widget("ui.droppable", {
1718 widgetEventPrefix: "drop",
1719 options: {
1720 accept: '*',
1721 activeClass: false,
1722 addClasses: true,
1723 greedy: false,
1724 hoverClass: false,
1725 scope: 'default',
1726 tolerance: 'intersect'
1727 },
1728 _create: function() {
1729
1730 var o = this.options, accept = o.accept;
1731 this.isover = 0; this.isout = 1;
1732
1733 this.accept = $.isFunction(accept) ? accept : function(d) {
1734 return d.is(accept);
1735 };
1736
1737 //Store the droppable's proportions
1738 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1739
1740 // Add the reference and positions to the manager
1741 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1742 $.ui.ddmanager.droppables[o.scope].push(this);
1743
1744 (o.addClasses && this.element.addClass("ui-droppable"));
1745
1746 },
1747
1748 destroy: function() {
1749 var drop = $.ui.ddmanager.droppables[this.options.scope];
1750 for ( var i = 0; i < drop.length; i++ )
1751 if ( drop[i] == this )
1752 drop.splice(i, 1);
1753
1754 this.element
1755 .removeClass("ui-droppable ui-droppable-disabled")
1756 .removeData("droppable")
1757 .unbind(".droppable");
1758
1759 return this;
1760 },
1761
1762 _setOption: function(key, value) {
1763
1764 if(key == 'accept') {
1765 this.accept = $.isFunction(value) ? value : function(d) {
1766 return d.is(value);
1767 };
1768 }
1769 $.Widget.prototype._setOption.apply(this, arguments);
1770 },
1771
1772 _activate: function(event) {
1773 var draggable = $.ui.ddmanager.current;
1774 if(this.options.activeClass) this.element.addClass(this.options.activeClass);
1775 (draggable && this._trigger('activate', event, this.ui(draggable)));
1776 },
1777
1778 _deactivate: function(event) {
1779 var draggable = $.ui.ddmanager.current;
1780 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1781 (draggable && this._trigger('deactivate', event, this.ui(draggable)));
1782 },
1783
1784 _over: function(event) {
1785
1786 var draggable = $.ui.ddmanager.current;
1787 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1788
1789 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1790 if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
1791 this._trigger('over', event, this.ui(draggable));
1792 }
1793
1794 },
1795
1796 _out: function(event) {
1797
1798 var draggable = $.ui.ddmanager.current;
1799 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
1800
1801 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1802 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1803 this._trigger('out', event, this.ui(draggable));
1804 }
1805
1806 },
1807
1808 _drop: function(event,custom) {
1809
1810 var draggable = custom || $.ui.ddmanager.current;
1811 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
1812
1813 var childrenIntersection = false;
1814 this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
1815 var inst = $.data(this, 'droppable');
1816 if(
1817 inst.options.greedy
1818 && !inst.options.disabled
1819 && inst.options.scope == draggable.options.scope
1820 && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
1821 && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
1822 ) { childrenIntersection = true; return false; }
1823 });
1824 if(childrenIntersection) return false;
1825
1826 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1827 if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
1828 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
1829 this._trigger('drop', event, this.ui(draggable));
1830 return this.element;
1831 }
1832
1833 return false;
1834
1835 },
1836
1837 ui: function(c) {
1838 return {
1839 draggable: (c.currentItem || c.element),
1840 helper: c.helper,
1841 position: c.position,
1842 offset: c.positionAbs
1843 };
1844 }
1845
1846});
1847
1848$.extend($.ui.droppable, {
1849 version: "1.8.4"
1850});
1851
1852$.ui.intersect = function(draggable, droppable, toleranceMode) {
1853
1854 if (!droppable.offset) return false;
1855
1856 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
1857 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
1858 var l = droppable.offset.left, r = l + droppable.proportions.width,
1859 t = droppable.offset.top, b = t + droppable.proportions.height;
1860
1861 switch (toleranceMode) {
1862 case 'fit':
1863 return (l <= x1 && x2 <= r
1864 && t <= y1 && y2 <= b);
1865 break;
1866 case 'intersect':
1867 return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
1868 && x2 - (draggable.helperProportions.width / 2) < r // Left Half
1869 && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
1870 && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
1871 break;
1872 case 'pointer':
1873 var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
1874 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
1875 isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
1876 return isOver;
1877 break;
1878 case 'touch':
1879 return (
1880 (y1 >= t && y1 <= b) || // Top edge touching
1881 (y2 >= t && y2 <= b) || // Bottom edge touching
1882 (y1 < t && y2 > b) // Surrounded vertically
1883 ) && (
1884 (x1 >= l && x1 <= r) || // Left edge touching
1885 (x2 >= l && x2 <= r) || // Right edge touching
1886 (x1 < l && x2 > r) // Surrounded horizontally
1887 );
1888 break;
1889 default:
1890 return false;
1891 break;
1892 }
1893
1894};
1895
1896/*
1897 This manager tracks offsets of draggables and droppables
1898*/
1899$.ui.ddmanager = {
1900 current: null,
1901 droppables: { 'default': [] },
1902 prepareOffsets: function(t, event) {
1903
1904 var m = $.ui.ddmanager.droppables[t.options.scope] || [];
1905 var type = event ? event.type : null; // workaround for #2317
1906 var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
1907
1908 droppablesLoop: for (var i = 0; i < m.length; i++) {
1909
1910 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
1911 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
1912 m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
1913
1914 m[i].offset = m[i].element.offset();
1915 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
1916
1917 if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
1918
1919 }
1920
1921 },
1922 drop: function(draggable, event) {
1923
1924 var dropped = false;
1925 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
1926
1927 if(!this.options) return;
1928 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
1929 dropped = dropped || this._drop.call(this, event);
1930
1931 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
1932 this.isout = 1; this.isover = 0;
1933 this._deactivate.call(this, event);
1934 }
1935
1936 });
1937 return dropped;
1938
1939 },
1940 drag: function(draggable, event) {
1941
1942 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
1943 if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
1944
1945 //Run through all droppables and check their positions based on specific tolerance options
1946 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
1947
1948 if(this.options.disabled || this.greedyChild || !this.visible) return;
1949 var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
1950
1951 var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
1952 if(!c) return;
1953
1954 var parentInstance;
1955 if (this.options.greedy) {
1956 var parent = this.element.parents(':data(droppable):eq(0)');
1957 if (parent.length) {
1958 parentInstance = $.data(parent[0], 'droppable');
1959 parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
1960 }
1961 }
1962
1963 // we just moved into a greedy child
1964 if (parentInstance && c == 'isover') {
1965 parentInstance['isover'] = 0;
1966 parentInstance['isout'] = 1;
1967 parentInstance._out.call(parentInstance, event);
1968 }
1969
1970 this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
1971 this[c == "isover" ? "_over" : "_out"].call(this, event);
1972
1973 // we just moved out of a greedy child
1974 if (parentInstance && c == 'isout') {
1975 parentInstance['isout'] = 0;
1976 parentInstance['isover'] = 1;
1977 parentInstance._over.call(parentInstance, event);
1978 }
1979 });
1980
1981 }
1982};
1983
1984})(jQuery);
1985/*
1986 * jQuery UI Resizable 1.8.4
1987 *
1988 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1989 * Dual licensed under the MIT or GPL Version 2 licenses.
1990 * http://jquery.org/license
1991 *
1992 * http://docs.jquery.com/UI/Resizables
1993 *
1994 * Depends:
1995 * jquery.ui.core.js
1996 * jquery.ui.mouse.js
1997 * jquery.ui.widget.js
1998 */
1999(function( $, undefined ) {
2000
2001$.widget("ui.resizable", $.ui.mouse, {
2002 widgetEventPrefix: "resize",
2003 options: {
2004 alsoResize: false,
2005 animate: false,
2006 animateDuration: "slow",
2007 animateEasing: "swing",
2008 aspectRatio: false,
2009 autoHide: false,
2010 containment: false,
2011 ghost: false,
2012 grid: false,
2013 handles: "e,s,se",
2014 helper: false,
2015 maxHeight: null,
2016 maxWidth: null,
2017 minHeight: 10,
2018 minWidth: 10,
2019 zIndex: 1000
2020 },
2021 _create: function() {
2022
2023 var self = this, o = this.options;
2024 this.element.addClass("ui-resizable");
2025
2026 $.extend(this, {
2027 _aspectRatio: !!(o.aspectRatio),
2028 aspectRatio: o.aspectRatio,
2029 originalElement: this.element,
2030 _proportionallyResizeElements: [],
2031 _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
2032 });
2033
2034 //Wrap the element if it cannot hold child nodes
2035 if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
2036
2037 //Opera fix for relative positioning
2038 if (/relative/.test(this.element.css('position')) && $.browser.opera)
2039 this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
2040
2041 //Create a wrapper element and set the wrapper to the new current internal element
2042 this.element.wrap(
2043 $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
2044 position: this.element.css('position'),
2045 width: this.element.outerWidth(),
2046 height: this.element.outerHeight(),
2047 top: this.element.css('top'),
2048 left: this.element.css('left')
2049 })
2050 );
2051
2052 //Overwrite the original this.element
2053 this.element = this.element.parent().data(
2054 "resizable", this.element.data('resizable')
2055 );
2056
2057 this.elementIsWrapper = true;
2058
2059 //Move margins to the wrapper
2060 this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
2061 this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
2062
2063 //Prevent Safari textarea resize
2064 this.originalResizeStyle = this.originalElement.css('resize');
2065 this.originalElement.css('resize', 'none');
2066
2067 //Push the actual element to our proportionallyResize internal array
2068 this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
2069
2070 // avoid IE jump (hard set the margin)
2071 this.originalElement.css({ margin: this.originalElement.css('margin') });
2072
2073 // fix handlers offset
2074 this._proportionallyResize();
2075
2076 }
2077
2078 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' });
2079 if(this.handles.constructor == String) {
2080
2081 if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
2082 var n = this.handles.split(","); this.handles = {};
2083
2084 for(var i = 0; i < n.length; i++) {
2085
2086 var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
2087 var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
2088
2089 // increase zIndex of sw, se, ne, nw axis
2090 //TODO : this modifies original option
2091 if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
2092
2093 //TODO : What's going on here?
2094 if ('se' == handle) {
2095 axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
2096 };
2097
2098 //Insert into internal handles object and append to element
2099 this.handles[handle] = '.ui-resizable-'+handle;
2100 this.element.append(axis);
2101 }
2102
2103 }
2104
2105 this._renderAxis = function(target) {
2106
2107 target = target || this.element;
2108
2109 for(var i in this.handles) {
2110
2111 if(this.handles[i].constructor == String)
2112 this.handles[i] = $(this.handles[i], this.element).show();
2113
2114 //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
2115 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
2116
2117 var axis = $(this.handles[i], this.element), padWrapper = 0;
2118
2119 //Checking the correct pad and border
2120 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
2121
2122 //The padding type i have to apply...
2123 var padPos = [ 'padding',
2124 /ne|nw|n/.test(i) ? 'Top' :
2125 /se|sw|s/.test(i) ? 'Bottom' :
2126 /^e$/.test(i) ? 'Right' : 'Left' ].join("");
2127
2128 target.css(padPos, padWrapper);
2129
2130 this._proportionallyResize();
2131
2132 }
2133
2134 //TODO: What's that good for? There's not anything to be executed left
2135 if(!$(this.handles[i]).length)
2136 continue;
2137
2138 }
2139 };
2140
2141 //TODO: make renderAxis a prototype function
2142 this._renderAxis(this.element);
2143
2144 this._handles = $('.ui-resizable-handle', this.element)
2145 .disableSelection();
2146
2147 //Matching axis name
2148 this._handles.mouseover(function() {
2149 if (!self.resizing) {
2150 if (this.className)
2151 var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
2152 //Axis, default = se
2153 self.axis = axis && axis[1] ? axis[1] : 'se';
2154 }
2155 });
2156
2157 //If we want to auto hide the elements
2158 if (o.autoHide) {
2159 this._handles.hide();
2160 $(this.element)
2161 .addClass("ui-resizable-autohide")
2162 .hover(function() {
2163 $(this).removeClass("ui-resizable-autohide");
2164 self._handles.show();
2165 },
2166 function(){
2167 if (!self.resizing) {
2168 $(this).addClass("ui-resizable-autohide");
2169 self._handles.hide();
2170 }
2171 });
2172 }
2173
2174 //Initialize the mouse interaction
2175 this._mouseInit();
2176
2177 },
2178
2179 destroy: function() {
2180
2181 this._mouseDestroy();
2182
2183 var _destroy = function(exp) {
2184 $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
2185 .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
2186 };
2187
2188 //TODO: Unwrap at same DOM position
2189 if (this.elementIsWrapper) {
2190 _destroy(this.element);
2191 var wrapper = this.element;
2192 wrapper.after(
2193 this.originalElement.css({
2194 position: wrapper.css('position'),
2195 width: wrapper.outerWidth(),
2196 height: wrapper.outerHeight(),
2197 top: wrapper.css('top'),
2198 left: wrapper.css('left')
2199 })
2200 ).remove();
2201 }
2202
2203 this.originalElement.css('resize', this.originalResizeStyle);
2204 _destroy(this.originalElement);
2205
2206 return this;
2207 },
2208
2209 _mouseCapture: function(event) {
2210 var handle = false;
2211 for (var i in this.handles) {
2212 if ($(this.handles[i])[0] == event.target) {
2213 handle = true;
2214 }
2215 }
2216
2217 return !this.options.disabled && handle;
2218 },
2219
2220 _mouseStart: function(event) {
2221
2222 var o = this.options, iniPos = this.element.position(), el = this.element;
2223
2224 this.resizing = true;
2225 this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
2226
2227 // bugfix for http://dev.jquery.com/ticket/1749
2228 if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
2229 el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
2230 }
2231
2232 //Opera fixing relative position
2233 if ($.browser.opera && (/relative/).test(el.css('position')))
2234 el.css({ position: 'relative', top: 'auto', left: 'auto' });
2235
2236 this._renderProxy();
2237
2238 var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
2239
2240 if (o.containment) {
2241 curleft += $(o.containment).scrollLeft() || 0;
2242 curtop += $(o.containment).scrollTop() || 0;
2243 }
2244
2245 //Store needed variables
2246 this.offset = this.helper.offset();
2247 this.position = { left: curleft, top: curtop };
2248 this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2249 this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
2250 this.originalPosition = { left: curleft, top: curtop };
2251 this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
2252 this.originalMousePosition = { left: event.pageX, top: event.pageY };
2253
2254 //Aspect Ratio
2255 this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
2256
2257 var cursor = $('.ui-resizable-' + this.axis).css('cursor');
2258 $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
2259
2260 el.addClass("ui-resizable-resizing");
2261 this._propagate("start", event);
2262 return true;
2263 },
2264
2265 _mouseDrag: function(event) {
2266
2267 //Increase performance, avoid regex
2268 var el = this.helper, o = this.options, props = {},
2269 self = this, smp = this.originalMousePosition, a = this.axis;
2270
2271 var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
2272 var trigger = this._change[a];
2273 if (!trigger) return false;
2274
2275 // Calculate the attrs that will be change
2276 var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
2277
2278 if (this._aspectRatio || event.shiftKey)
2279 data = this._updateRatio(data, event);
2280
2281 data = this._respectSize(data, event);
2282
2283 // plugins callbacks need to be called first
2284 this._propagate("resize", event);
2285
2286 el.css({
2287 top: this.position.top + "px", left: this.position.left + "px",
2288 width: this.size.width + "px", height: this.size.height + "px"
2289 });
2290
2291 if (!this._helper && this._proportionallyResizeElements.length)
2292 this._proportionallyResize();
2293
2294 this._updateCache(data);
2295
2296 // calling the user callback at the end
2297 this._trigger('resize', event, this.ui());
2298
2299 return false;
2300 },
2301
2302 _mouseStop: function(event) {
2303
2304 this.resizing = false;
2305 var o = this.options, self = this;
2306
2307 if(this._helper) {
2308 var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
2309 soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
2310 soffsetw = ista ? 0 : self.sizeDiff.width;
2311
2312 var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
2313 left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
2314 top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
2315
2316 if (!o.animate)
2317 this.element.css($.extend(s, { top: top, left: left }));
2318
2319 self.helper.height(self.size.height);
2320 self.helper.width(self.size.width);
2321
2322 if (this._helper && !o.animate) this._proportionallyResize();
2323 }
2324
2325 $('body').css('cursor', 'auto');
2326
2327 this.element.removeClass("ui-resizable-resizing");
2328
2329 this._propagate("stop", event);
2330
2331 if (this._helper) this.helper.remove();
2332 return false;
2333
2334 },
2335
2336 _updateCache: function(data) {
2337 var o = this.options;
2338 this.offset = this.helper.offset();
2339 if (isNumber(data.left)) this.position.left = data.left;
2340 if (isNumber(data.top)) this.position.top = data.top;
2341 if (isNumber(data.height)) this.size.height = data.height;
2342 if (isNumber(data.width)) this.size.width = data.width;
2343 },
2344
2345 _updateRatio: function(data, event) {
2346
2347 var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
2348
2349 if (data.height) data.width = (csize.height * this.aspectRatio);
2350 else if (data.width) data.height = (csize.width / this.aspectRatio);
2351
2352 if (a == 'sw') {
2353 data.left = cpos.left + (csize.width - data.width);
2354 data.top = null;
2355 }
2356 if (a == 'nw') {
2357 data.top = cpos.top + (csize.height - data.height);
2358 data.left = cpos.left + (csize.width - data.width);
2359 }
2360
2361 return data;
2362 },
2363
2364 _respectSize: function(data, event) {
2365
2366 var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
2367 ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
2368 isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
2369
2370 if (isminw) data.width = o.minWidth;
2371 if (isminh) data.height = o.minHeight;
2372 if (ismaxw) data.width = o.maxWidth;
2373 if (ismaxh) data.height = o.maxHeight;
2374
2375 var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
2376 var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
2377
2378 if (isminw && cw) data.left = dw - o.minWidth;
2379 if (ismaxw && cw) data.left = dw - o.maxWidth;
2380 if (isminh && ch) data.top = dh - o.minHeight;
2381 if (ismaxh && ch) data.top = dh - o.maxHeight;
2382
2383 // fixing jump error on top/left - bug #2330
2384 var isNotwh = !data.width && !data.height;
2385 if (isNotwh && !data.left && data.top) data.top = null;