Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
ecommerce-ui
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
ecommerce-ui
Commits
048f69a4
Commit
048f69a4
authored
Nov 29, 2013
by
Sven Franck
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added breadcrumbs, fixed infoFields and broken deeplink
parent
7a48da05
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
588 additions
and
489 deletions
+588
-489
js/erp5_loader.js
js/erp5_loader.js
+588
-489
No files found.
js/erp5_loader.js
View file @
048f69a4
...
@@ -658,15 +658,26 @@
...
@@ -658,15 +658,26 @@
content
,
content
,
container
,
container
,
promises
=
[],
promises
=
[],
path
=
content_dict
.
property_dict
.
path_dict
,
path_flag
=
content_dict
.
property_dict
.
path_dict
,
status_flag
=
content_dict
.
property_dict
.
status_dict
,
multi_language_flag
=
content_dict
.
property_dict
.
i18n
;
multi_language_flag
=
content_dict
.
property_dict
.
i18n
;
// setup loader
if
(
status_flag
)
{
switch
(
status_flag
.
type
)
{
case
"
loader
"
:
{
app
.
default_dict
.
loader
=
true
;
app
.
default_dict
.
loader_theme
=
status_flag
.
theme
;
}
};
}
// create path library
// create path library
// TODO: should this be optional?
// TODO: should this be optional?
if
(
path
)
{
if
(
path
_flag
)
{
for
(
single_path
in
path
)
{
for
(
single_path
in
path
_flag
)
{
if
(
path
.
hasOwnProperty
(
single_path
))
{
if
(
path
_flag
.
hasOwnProperty
(
single_path
))
{
app
.
default_dict
.
path_dict
[
single_path
]
=
path
[
single_path
];
app
.
default_dict
.
path_dict
[
single_path
]
=
path
_flag
[
single_path
];
}
}
}
}
}
else
{
}
else
{
...
@@ -795,7 +806,16 @@
...
@@ -795,7 +806,16 @@
if
(
page_dict
===
undefined
)
{
if
(
page_dict
===
undefined
)
{
util
.
errorHandler
({
"
error
"
:
"
Pageindex: Missing page definition
"
});
util
.
errorHandler
({
"
error
"
:
"
Pageindex: Missing page definition
"
});
}
else
{
}
else
{
return
app
.
setContent
(
page_dict
,
url_dict
,
create
);
// NOTE: 3rd parameter "create" needs to be set to undefined,
// if a page already exists. Setting to true generates a new page
// setting to false tries to update the content section of an
// existing page (whose contents may not exist at this moment)
return
app
.
setContent
(
page_dict
,
url_dict
,
document
.
getElementById
(
url_dict
.
id
)
?
undefined
:
true
);
}
}
return
;
return
;
},
},
...
@@ -2372,6 +2392,7 @@
...
@@ -2372,6 +2392,7 @@
factory
.
page
=
function
(
content_dict
,
url_dict
,
create
)
{
factory
.
page
=
function
(
content_dict
,
url_dict
,
create
)
{
var
i
,
var
i
,
j
,
j
,
k
,
last
,
last
,
element
,
element
,
wrapper
,
wrapper
,
...
@@ -2382,7 +2403,8 @@
...
@@ -2382,7 +2403,8 @@
if
(
content_dict
.
children
)
{
if
(
content_dict
.
children
)
{
for
(
i
=
0
;
i
<
content_dict
.
children
.
length
;
i
+=
1
)
{
for
(
i
=
0
;
i
<
content_dict
.
children
.
length
;
i
+=
1
)
{
promises
[
i
]
=
app
.
setContent
(
content_dict
.
children
[
i
]);
// NOTE: need to pass url and create info
promises
[
i
]
=
app
.
setContent
(
content_dict
.
children
[
i
],
url_dict
,
create
);
}
}
}
}
...
@@ -2420,7 +2442,15 @@
...
@@ -2420,7 +2442,15 @@
}
}
);
);
// assemble
// update header before changing to a new page
if
(
create
===
true
)
{
app
.
setPageTitle
(
content_dict
.
title_i18n
,
target
.
querySelectorAll
(
"
div.ui-header
"
)
);
}
wrapper
.
appendChild
(
app
.
breadcrumb
(
url_dict
));
wrapper
.
appendChild
(
target
);
wrapper
.
appendChild
(
target
);
container
.
appendChild
(
wrapper
);
container
.
appendChild
(
wrapper
);
...
@@ -3255,65 +3285,6 @@
...
@@ -3255,65 +3285,6 @@
init
=
{};
init
=
{};
/**
* Try fetching data from JIO. Fallback to loading fake data until accessible
* @method fetchData
* @param {object} parcel Storage, query options and baggage to return
* @return {object} promise object/baggage
*/
// NOTE: until we have real data we load fake data on application init!
init
.
fetchData
=
function
(
parcel
)
{
return
storage
[
parcel
.
storage
].
allDocs
(
parcel
.
query
)
.
then
(
function
(
response
)
{
return
{
"
response
"
:
response
,
"
baggage
"
:
parcel
.
baggage
};
});
};
/**
* parse a link into query-able parameters
* @method parseLink
* @param {string} url Url to go to
* @return {object} pointer object
*/
// TODO: renderJS should parse a link
init
.
parseLink
=
function
(
url
)
{
var
i
,
query
,
parameter
,
path
=
$
.
mobile
.
path
.
parseUrl
(
url
),
clean_hash
=
path
.
hash
.
replace
(
"
#
"
,
""
),
config
=
{
"
url
"
:
url
};
if
(
path
.
hash
===
""
)
{
config
.
id
=
config
.
layout_identifier
=
util
.
getActivePage
();
}
else
{
// do we have a mode?
query
=
clean_hash
.
split
(
"
?
"
);
for
(
i
=
0
;
i
<
query
.
length
;
i
+=
1
)
{
parameter
=
query
[
i
].
split
(
"
=
"
);
if
(
parameter
.
length
===
2
&&
parameter
[
0
]
===
"
mode
"
)
{
config
.
mode
=
parameter
[
1
];
}
}
config
.
fragment_list
=
clean_hash
.
split
(
"
::
"
);
config
.
id
=
clean_hash
;
config
.
layout_level
=
config
.
fragment_list
.
length
-
1
;
config
.
deeplink
=
true
;
config
.
layout_identifier
=
clean_hash
.
split
(
"
::
"
)[
0
];
}
return
config
;
};
/**
/**
* Update info fields (field displaying some sort of information
* Update info fields (field displaying some sort of information
* @method "
* @method "
...
@@ -3398,44 +3369,9 @@
...
@@ -3398,44 +3369,9 @@
}
}
break
;
break
;
}
}
info_field
.
textContent
=
info
;
}
};
/**
info_field
.
textContent
=
info
;
* Generate an action object (vs duplicate in every action call)
* @method generateActionObject
* @param {object} e Event triggering an action
* @return {object} action object
*/
// TODO: integrate in popup handler, make sure "pop" works!
// TODO: id ... is crap
init
.
generateActionObject
=
function
(
e
)
{
var
element
,
pop
,
id
,
gadget
;
switch
(
e
.
type
)
{
case
"
popupbeforeposition
"
:
element
=
undefined
;
id
=
e
.
target
.
id
;
gadget
=
e
.
target
;
break
;
default
:
element
=
e
.
target
||
e
;
pop
=
element
.
getAttribute
(
"
data-rel
"
)
===
null
;
id
=
pop
?
(
element
.
getAttribute
(
"
data-reference
"
)
||
util
.
getActivePage
())
:
(
element
.
href
===
undefined
?
util
.
getActivePage
()
:
element
.
href
.
split
(
"
#
"
)[
1
]);
gadget
=
document
.
getElementById
(
id
);
break
;
}
}
return
{
"
element
"
:
element
,
"
id
"
:
id
,
"
gadget
"
:
document
.
getElementById
(
id
),
"
state
"
:
gadget
.
state
};
};
};
/**
/**
...
@@ -3522,50 +3458,7 @@
...
@@ -3522,50 +3458,7 @@
);
);
};
};
/**
* Action handler, routing actions to specified method
* @method action
* @param {object} e Event that triggered the action
*/
init
.
action
=
function
(
e
)
{
var
type
,
tag
,
val
,
action
,
handler
;
type
=
e
.
type
;
tag
=
e
.
target
.
tagName
;
if
(
type
===
"
click
"
&&
e
.
target
.
getAttribute
(
"
data-rel
"
)
===
null
)
{
e
.
preventDefault
();
if
(
type
===
"
click
"
&&
(
tag
===
"
SELECT
"
||
(
tag
===
"
INPUT
"
&&
e
.
target
.
type
!==
"
submit
"
)))
{
return
;
}
}
if
(
type
===
"
change
"
&&
tag
===
"
SELECT
"
)
{
val
=
e
.
target
.
options
[
e
.
target
.
selectedIndex
].
value
;
}
// JQM bug on selects
// TODO: remove once fixed
if
(
tag
===
"
SPAN
"
||
tag
===
"
OPTION
"
)
{
return
;
}
// map
action
=
e
.
target
.
getAttribute
(
"
data-action
"
);
handler
=
factory
.
map_actions
[
action
];
if
(
action
)
{
if
(
handler
)
{
handler
(
init
.
generateActionObject
(
e
),
val
);
}
else
{
util
.
errorHandler
({
"
Error
"
:
"
Action: No method defined
"
});
}
}
else
{
util
.
errorHandler
({
"
Error
"
:
"
Action: No action defined for element
"
});
}
};
/**
/**
* Sort a selection of elements
* Sort a selection of elements
...
@@ -3639,7 +3532,11 @@
...
@@ -3639,7 +3532,11 @@
// give user half second to pick his state
// give user half second to pick his state
app
.
timer
=
window
.
setTimeout
(
function
()
{
app
.
timer
=
window
.
setTimeout
(
function
()
{
// update gadgets
// update gadgets
app
.
setContent
([{
"
children
"
:
[{
"
id
"
:
config
.
id
}]}],
{},
false
);
app
.
setContent
(
{
"
generate
"
:
"
gadget
"
,
"
id
"
:
config
.
id
,
"
href
"
:
config
.
id
},
{},
false
);
app
.
timer
=
0
;
app
.
timer
=
0
;
},
500
);
},
500
);
};
};
...
@@ -3672,7 +3569,7 @@
...
@@ -3672,7 +3569,7 @@
.
then
(
function
(
reply
)
{
.
then
(
function
(
reply
)
{
// update state
// update state
action_dict
.
state
.
query
=
init
.
generateQueryObject
(
action_dict
.
state
.
query
=
app
.
generateQueryObject
(
{
"
query
"
:
action_dict
.
state
.
initial_query
},
{
"
query
"
:
action_dict
.
state
.
initial_query
},
reply
.
baggage
.
portal_type
,
reply
.
baggage
.
portal_type
,
null
,
null
,
...
@@ -3681,178 +3578,59 @@
...
@@ -3681,178 +3578,59 @@
);
);
// update gadget
// update gadget
app
.
setContent
([{
"
children
"
:
[{
"
id
"
:
action_dict
.
id
}]}],
{},
false
);
app
.
setContent
(
{
"
generate
"
:
"
gadget
"
,
"
id
"
:
action_dict
.
id
,
"
href
"
:
action_dict
.
id
},
{},
false
);
})
})
.
fail
(
util
.
errorHandler
);
.
fail
(
util
.
errorHandler
);
};
};
/** Determine if a property exists in an object
* @method findProbertyInObject
* @param {object} Object The object to search
* @param {string} Pointer The nested object pointer
* @param {string} Key The key under which it is stored
* @param {string} Key The value to search
* @return {boolean} true/null
*/
// TODO: Storage specific method, move
init
.
findKey
=
function
(
object
,
pointer
,
key
,
value
)
{
var
i
,
obj
;
// simple query
if
(
object
[
key
]
===
value
)
{
return
true
;
}
if
(
object
[
pointer
])
{
for
(
i
=
0
;
i
<
object
[
pointer
].
length
;
i
+=
1
)
{
obj
=
object
[
pointer
][
i
];
if
(
obj
[
key
]
===
value
)
{
return
true
;
}
if
(
obj
[
pointer
])
{
init
.
findKey
(
obj
[
pointer
],
pointer
,
key
,
value
);
}
}
}
return
null
;
};
/**
/**
* Build a query object based on passed configuration
* Handler for form submission to add a new item
* @method generateQueryObject
* @method add
* @param {object} config JSON parameters for query object
* @param {object} config Action Object
* @param {string} type Portal Type to fetch
* @param {string} key Parameter to search single column
* @param {string} value Value to search for across one or all columns
* @param {object} field_list Items to search across
* @param {string} initial_query Intial query indicating blocked columns
* @return {object} query object
*/
*/
// WARNING: complex_queries dependency!
// NOTE: we always validate! to skip validation test for class on form
init
.
generateQueryObject
=
function
(
query
,
type
,
key
,
value
,
field_list
)
{
// TODO: generate common promise handler for add/remove/clone/export/etc
var
parameter
,
init
.
add
=
function
(
config
)
{
property
,
var
property
,
wrap
,
replace
,
query_object
,
i
,
query_clean
,
form_elements
,
query_input
=
query
||
{},
form_element
,
obj
=
{},
pass
=
false
,
is_value
=
value
&&
value
!==
""
;
test_full
,
test_empty
,
obj
.
query
=
''
;
test_time
,
form_to_submit
=
document
.
getElementById
(
config
.
id
),
anti_spam
=
document
.
getElementById
(
form_to_submit
.
id
+
"
_not_a_secret
"
),
formData
=
new
FormData
(),
valid
=
$
(
form_to_submit
).
triggerHandler
(
"
submitForm
"
);
// query string passed? parse it
if
(
valid
!==
false
)
{
if
(
query_input
.
query
)
{
query_clean
=
query_input
.
query
.
replace
(
/'/g
,
'
"
'
);
query_object
=
complex_queries
.
QueryFactory
.
create
(
query_clean
);
// missing portal type?
if
(
!
init
.
findKey
(
query_object
,
"
query_list
"
,
"
key
"
,
"
portal_type
"
))
{
obj
.
query
=
'
(portal_type:"%
'
+
type
+
'
" AND
'
;
}
// NOTE: json only allows single quotes inside double quotes. Reverse
// spam - thx http://nedbatchelder.com/text/stopbots.html
// and add passed query to querystring
if
(
anti_spam
)
{
obj
.
query
+=
query_clean
+
'
)
'
;
}
else
{
obj
.
query
=
'
(portal_type:"%
'
+
type
+
'
")
'
;
}
// search "foo" = "bar"
// fill form in < 1sec = spammer
if
(
is_value
&&
key
)
{
if
(
Date
.
now
()
-
parseInt
(
anti_spam
.
getAttribute
(
"
data-created
"
))
obj
.
query
+=
'
AND (
'
+
key
+
'
:"
'
+
value
+
'
")
'
;
>
1000
)
{
obj
.
start
=
0
;
test_time
=
true
;
obj
.
items
=
1
;
}
// search "bar"
// one empty, one filled secured field
}
else
if
(
is_value
)
{
form_elements
=
form_to_submit
.
getElementsByTagName
(
"
input
"
);
// we need to check an existing query for the fields we are already
for
(
i
=
0
;
i
<
form_elements
.
length
;
i
+=
1
)
{
// searching. These fields should not be set in the search
form_element
=
form_elements
[
i
];
if
(
query
&&
field_list
)
{
if
(
util
.
testForClass
(
form_element
.
className
,
"
secure_form
"
))
{
wrap
=
""
;
if
(
form_element
.
value
===
""
)
{
for
(
property
in
field_list
)
{
test_empty
=
true
;
if
(
field_list
.
hasOwnProperty
(
property
))
{
}
if
(
!
init
.
findKey
(
query_object
,
"
query_list
"
,
"
key
"
,
property
))
{
if
(
form_element
.
value
!==
""
)
{
wrap
+=
property
+
'
:"%
'
+
value
+
'
%" OR
'
;
test_full
=
true
;
}
}
}
obj
.
query
+=
'
AND (
'
+
wrap
.
slice
(
0
,
-
4
)
+
'
)
'
;
}
}
if
(
query_input
.
sort_on
)
{
obj
.
sort_on
=
[
query_input
.
sort
];
}
else
{
obj
.
sort_on
=
[];
}
if
(
query_input
.
include_docs
||
value
)
{
obj
.
include_docs
=
true
;
}
if
(
query_input
.
select_list
&&
query_input
.
select_list
.
length
>
0
)
{
obj
.
select_list
=
query_input
.
select_list
;
}
if
(
query_input
.
limit
)
{
obj
.
start
=
query_input
.
limit
[
0
];
obj
.
items
=
query_input
.
limit
[
1
];
}
if
(
obj
.
start
!==
undefined
)
{
obj
.
limit
=
[
obj
.
start
,
obj
.
items
];
}
else
{
obj
.
limit
=
[];
}
if
(
query_input
.
wildcard_character
)
{
obj
.
wildcard_character
=
query_input
.
wildcard_character
;
}
else
{
obj
.
wildcard_character
=
"
%
"
;
}
return
obj
;
};
/**
* Handler for form submission to add a new item
* @method add
* @param {object} config Action Object
*/
// NOTE: we always validate! to skip validation test for class on form
// TODO: generate common promise handler for add/remove/clone/export/etc
init
.
add
=
function
(
config
)
{
var
property
,
replace
,
i
,
form_elements
,
form_element
,
pass
=
false
,
test_full
,
test_empty
,
test_time
,
form_to_submit
=
document
.
getElementById
(
config
.
id
),
anti_spam
=
document
.
getElementById
(
form_to_submit
.
id
+
"
_not_a_secret
"
),
formData
=
new
FormData
(),
valid
=
$
(
form_to_submit
).
triggerHandler
(
"
submitForm
"
);
if
(
valid
!==
false
)
{
// spam - thx http://nedbatchelder.com/text/stopbots.html
if
(
anti_spam
)
{
// fill form in < 2sec = spammer
if
(
Date
.
now
()
-
parseInt
(
anti_spam
.
getAttribute
(
"
data-created
"
))
>
2000
)
{
test_time
=
true
;
}
// one empty, one filled secured field
form_elements
=
form_to_submit
.
getElementsByTagName
(
"
input
"
);
for
(
i
=
0
;
i
<
form_elements
.
length
;
i
+=
1
)
{
form_element
=
form_elements
[
i
];
if
(
util
.
testForClass
(
form_element
.
className
,
"
secure_form
"
))
{
if
(
form_element
.
value
===
""
)
{
test_empty
=
true
;
}
if
(
form_element
.
value
!==
""
)
{
test_full
=
true
;
}
}
}
}
}
}
...
@@ -3884,7 +3662,7 @@
...
@@ -3884,7 +3662,7 @@
"
data
"
:
formData
"
data
"
:
formData
})
})
.
then
(
function
()
{
.
then
(
function
()
{
$
.
mobile
.
changePage
(
"
#thanks
"
)
$
.
mobile
.
changePage
(
"
#thanks
"
)
;
})
})
.
fail
(
util
.
errorHandler
);
.
fail
(
util
.
errorHandler
);
}
}
...
@@ -3935,7 +3713,11 @@
...
@@ -3935,7 +3713,11 @@
config
.
state
.
query
.
limit
=
[
start
,
records
];
config
.
state
.
query
.
limit
=
[
start
,
records
];
// update gadget
// update gadget
app
.
setContent
([{
"
children
"
:
[{
"
id
"
:
config
.
id
}]}],
{},
false
);
app
.
setContent
(
{
"
generate
"
:
"
gadget
"
,
"
id
"
:
config
.
id
,
"
href
"
:
config
.
id
},
{},
false
);
}
else
{
}
else
{
util
.
errorHandler
({
util
.
errorHandler
({
"
Error
"
:
"
No state information stored for gadget
"
"
Error
"
:
"
No state information stored for gadget
"
...
@@ -3948,176 +3730,344 @@
...
@@ -3948,176 +3730,344 @@
}
}
};
};
/* ====================================================================== */
/* ====================================================================== */
/*
PAGE SETUP
*/
/*
APP
*/
/* ====================================================================== */
/* ====================================================================== */
/*
/**
* Object containing application related methods
* Update a page (which may already be in the DOM)
* @method updatePage
* @param {object} e Event (pagebeforechange)
* @param {object} data Data passed along with this (JQM) event
*/
*/
// TODO: awful selector!
app
=
{};
// NOTE: removed data for JSLINT form parameters
init
.
updatePage
=
function
(
e
)
{
var
page
=
e
.
target
;
app
.
fetchConfiguration
({
/*
"
storage
"
:
app
.
default_dict
.
storage_dict
.
settings
,
* Object containing default names, which can be overridden
"
file
"
:
"
pages
"
,
*/
"
attachment
"
:
page
.
id
,
app
.
default_dict
=
{
"
baggage
"
:
undefined
"
storage_dict
"
:
{
})
"
settings
"
:
"
settings
"
,
.
then
(
function
(
reply
)
{
"
items
"
:
"
items
"
,
// TODO: bad, updating a page... based on state?
"
configuration
"
:
"
configuration
"
,
if
(
!
page
.
querySelectorAll
(
"
div.ui-content
"
)[
0
]
"
gadgets
"
:
"
gagdets
"
,
.
getAttribute
(
"
data-bound
"
))
{
"
data_type
"
:
"
portal_types
"
return
app
.
setContent
(
reply
,
{
"
id
"
:
page
.
id
},
undefined
);
},
}
"
path_dict
"
:
{
})
"
data
"
:
"
data
"
,
.
fail
(
util
.
errorHandler
);
"
home
"
:
"
home
"
}
};
};
/**
/**
* Set the page title
* Timer for 500ms delayed actions
* @method setPageTitle
* @param {string} page_i18n Lookup value for title
*/
*/
init
.
setPageTitle
=
function
(
page_i18n
)
{
app
.
timer
=
0
;
var
title
,
value
=
factory
.
map_actions
.
translateLookup
(
page_i18n
),
header
=
document
.
getElementById
(
"
global_header
"
)
||
// WARNING: IE8- children() retrieves comments, too
document
.
getElementById
(
util
.
getActivePage
()).
children
()[
0
];
if
(
util
.
testForClass
(
header
.
className
,
"
ui-header
"
))
{
/**
title
=
header
.
getElementsByTagName
(
"
h1
"
)[
0
];
* parse a link into query-able parameters
title
.
setAttribute
(
"
data-i18n
"
,
(
page_i18n
||
""
));
* @method parseLink
title
.
removeChild
(
title
.
childNodes
[
0
]);
* @param {string} url Url to go to
title
.
appendChild
(
document
.
createTextNode
((
value
||
"
\
u00A0
"
)));
* @return {object} pointer object
*/
// TODO: renderJS should parse a link
app
.
parseLink
=
function
(
url
)
{
var
i
,
query
,
parameter
,
path
=
$
.
mobile
.
path
.
parseUrl
(
url
),
clean_hash
=
path
.
hash
.
replace
(
"
#
"
,
""
),
config
=
{
"
url
"
:
url
};
if
(
path
.
hash
===
""
)
{
config
.
id
=
config
.
layout_identifier
=
util
.
getActivePage
();
}
else
{
// do we have a mode?
query
=
clean_hash
.
split
(
"
?
"
);
for
(
i
=
0
;
i
<
query
.
length
;
i
+=
1
)
{
parameter
=
query
[
i
].
split
(
"
=
"
);
if
(
parameter
.
length
===
2
&&
parameter
[
0
]
===
"
mode
"
)
{
config
.
mode
=
parameter
[
1
];
}
}
config
.
fragment_list
=
clean_hash
.
split
(
"
::
"
);
config
.
id
=
clean_hash
;
config
.
layout_level
=
config
.
fragment_list
.
length
-
1
;
config
.
deeplink
=
true
;
config
.
layout_identifier
=
clean_hash
.
split
(
"
::
"
)[
0
];
}
}
document
.
title
=
value
;
return
config
;
};
};
/**
/**
*
Prevent filterable from triggering
*
Action handler, routing actions to specified method
* @method
preventFilterableTrigger
* @method
action
* @param
s {object} element Filterable element
* @param
{object} e Event that triggered the action
*/
*/
// TODO: make sure this triggers only once!
app
.
action
=
function
(
e
)
{
init
.
preventFilterableTrigger
=
function
(
element
)
{
var
type
,
tag
,
val
,
action
,
handler
;
$
(
element
).
on
(
"
filterablebeforefilter
"
,
function
(
e
)
{
type
=
e
.
type
;
tag
=
e
.
target
.
tagName
;
if
(
type
===
"
click
"
&&
e
.
target
.
getAttribute
(
"
data-rel
"
)
===
null
)
{
e
.
preventDefault
();
e
.
preventDefault
();
});
if
(
type
===
"
click
"
&&
(
tag
===
"
SELECT
"
||
(
tag
===
"
INPUT
"
&&
e
.
target
.
type
!==
"
submit
"
)))
{
return
;
}
}
if
(
type
===
"
change
"
&&
tag
===
"
SELECT
"
)
{
val
=
e
.
target
.
options
[
e
.
target
.
selectedIndex
].
value
;
}
// JQM bug on selects
// TODO: remove once fixed
if
(
tag
===
"
SPAN
"
||
tag
===
"
OPTION
"
)
{
return
;
}
// map
action
=
e
.
target
.
getAttribute
(
"
data-action
"
);
handler
=
factory
.
map_actions
[
action
];
if
(
action
)
{
if
(
handler
)
{
handler
(
app
.
generateActionObject
(
e
),
val
);
}
else
{
util
.
errorHandler
({
"
Error
"
:
"
Action: No method defined
"
});
}
}
else
{
util
.
errorHandler
({
"
Error
"
:
"
Action: No action defined for element
"
});
}
};
};
/**
/**
* Set bindings on page specific elements after content has been appended
* Build a query object based on passed configuration
* @method setPageBindings
* @method generateQueryObject
* @param {object} config JSON parameters for query object
* @param {string} type Portal Type to fetch
* @param {string} key Parameter to search single column
* @param {string} value Value to search for across one or all columns
* @param {object} field_list Items to search across
* @param {string} initial_query Intial query indicating blocked columns
* @return {object} query object
*/
*/
// TODO: add local popups!
// WARNING: complex_queries dependency!
init
.
setPageBindings
=
function
()
{
app
.
generateQueryObject
=
function
(
query
,
type
,
key
,
value
,
field_list
)
{
var
i
,
var
parameter
,
j
,
property
,
form_element
,
wrap
,
filterable
,
query_object
,
captcha
,
query_clean
,
result
,
query_input
=
query
||
{},
form_list
=
document
.
getElementsByTagName
(
"
form
"
),
obj
=
{},
filter_list
=
document
.
querySelectorAll
(
"
[data-filter]
"
);
is_value
=
value
&&
value
!==
""
;
// disable default filtering of JQM filterable
for
(
i
=
0
;
i
<
filter_list
.
length
;
i
+=
1
)
{
filterable
=
filter_list
[
i
];
if
(
filterable
.
getAttribute
(
"
data-bound
"
)
===
null
)
{
obj
.
query
=
''
;
filterable
.
setAttribute
(
"
data-bound
"
,
true
);
init
.
preventFilterableTrigger
(
filterable
);
}
}
//
add validation to all forms
//
query string passed? parse it
for
(
j
=
0
;
j
<
form_list
.
length
;
j
+=
1
)
{
if
(
query_input
.
query
)
{
form_element
=
form_list
[
j
]
;
query_clean
=
query_input
.
query
.
replace
(
/'/g
,
'
"
'
)
;
captcha
=
document
.
getElementById
(
form_element
.
id
+
"
_captcha
"
);
query_object
=
complex_queries
.
QueryFactory
.
create
(
query_clean
);
// captcha
// missing portal type?
if
(
captcha
)
{
if
(
!
util
.
findKey
(
query_object
,
"
query_list
"
,
"
key
"
,
"
portal_type
"
))
{
util
.
declareJS
(
obj
.
query
=
'
(portal_type: "%
'
+
type
+
'
" AND
'
;
"
http://www.google.com/recaptcha/api/js/recaptcha_ajax.js
"
).
then
(
function
(
e
)
{
Recaptcha
.
create
(
captcha
.
getAttribute
(
"
data-key
"
),
captcha
.
id
,
{
"
theme
"
:
"
red
"
,
"
callback
"
:
Recaptcha
.
focus_response_field
}
);
}).
fail
(
util
.
errorHandler
);
}
}
if
(
form_element
.
getAttribute
(
"
data-bound
"
)
===
null
)
{
// NOTE: json only allows single quotes inside double quotes. Reverse
form_element
.
setAttribute
(
"
data-bound
"
,
true
);
// and add passed query to querystring
obj
.
query
+=
query_clean
+
'
)
'
;
}
else
{
obj
.
query
=
'
(portal_type: "%
'
+
type
+
'
")
'
;
}
// TODO: javascript-able?
// search "foo" = "bar"
// NOTE: the script is mapped to validval, so replacing it
if
(
is_value
&&
key
)
{
// requires it to add a different plugin here as well as
obj
.
query
+=
'
AND (
'
+
key
+
'
:"
'
+
value
+
'
")
'
;
// updating all data-vv fields being set in mapFormField() and
obj
.
start
=
0
;
// generateFormElement
obj
.
items
=
1
;
$
(
form_element
).
validVal
({
validate
:
{
// search "bar"
onKeyup
:
"
valid
"
,
}
else
if
(
is_value
)
{
onBlur
:
"
valid
"
// we need to check an existing query for the fields we are already
},
// searching. These fields should not be set in the search
form
:
{
if
(
query
&&
field_list
)
{
onInvalid
:
util
.
return_out
wrap
=
""
;
for
(
property
in
field_list
)
{
if
(
field_list
.
hasOwnProperty
(
property
))
{
if
(
!
util
.
findKey
(
query_object
,
"
query_list
"
,
"
key
"
,
property
))
{
wrap
+=
property
+
'
: "%
'
+
value
+
'
%" OR
'
;
}
}
}
});
}
obj
.
query
+=
'
AND (
'
+
wrap
.
slice
(
0
,
-
4
)
+
'
)
'
;
}
}
}
}
};
if
(
query_input
.
sort_on
)
{
obj
.
sort_on
=
[
query_input
.
sort
];
}
else
{
obj
.
sort_on
=
[];
}
if
(
query_input
.
include_docs
||
value
)
{
obj
.
include_docs
=
true
;
}
if
(
query_input
.
select_list
&&
query_input
.
select_list
.
length
>
0
)
{
obj
.
select_list
=
query_input
.
select_list
;
}
if
(
query_input
.
limit
)
{
obj
.
start
=
query_input
.
limit
[
0
];
obj
.
items
=
query_input
.
limit
[
1
];
}
if
(
obj
.
start
!==
undefined
)
{
obj
.
limit
=
[
obj
.
start
,
obj
.
items
];
}
else
{
obj
.
limit
=
[];
}
if
(
query_input
.
wildcard_character
)
{
obj
.
wildcard_character
=
query_input
.
wildcard_character
;
}
else
{
obj
.
wildcard_character
=
"
%
"
;
}
return
obj
;
};
/**
* Generate an action object (vs duplicate in every action call)
* @method generateActionObject
* @param {object} e Event triggering an action
* @return {object} action object
*/
// TODO: integrate in popup handler, make sure "pop" works!
// TODO: id ... is crap
app
.
generateActionObject
=
function
(
e
)
{
var
element
,
pop
,
id
,
gadget
;
switch
(
e
.
type
)
{
case
"
popupbeforeposition
"
:
element
=
undefined
;
id
=
e
.
target
.
id
;
gadget
=
e
.
target
;
break
;
default
:
element
=
e
.
target
||
e
;
pop
=
element
.
getAttribute
(
"
data-rel
"
)
===
null
;
id
=
pop
?
(
element
.
getAttribute
(
"
data-reference
"
)
||
util
.
getActivePage
())
:
(
element
.
href
===
undefined
?
util
.
getActivePage
()
:
element
.
href
.
split
(
"
#
"
)[
1
]);
gadget
=
document
.
getElementById
(
id
);
break
;
}
return
{
"
element
"
:
element
,
"
id
"
:
id
,
"
gadget
"
:
document
.
getElementById
(
id
),
"
state
"
:
gadget
.
state
};
};
/**
* Make a breadcrumb url for easy navigation
* @method breadcrumb
* @param {object} url_dict Current URL object
* @return {object} HTML fragment
*/
app
.
breadcrumb
=
function
(
url_dict
)
{
var
i
,
crumb
,
indicator
,
breadcrumb
=
factory
.
element
(
"
span
"
,
{
"
className
"
:
"
crumbs
"
},
{
"
data-info
"
:
"
url
"
,
"
data-reference
"
:
url_dict
.
id
}
),
makeLink
=
function
(
path
,
title
,
home
)
{
return
factory
.
element
(
"
a
"
,
{
"
href
"
:
path
||
"
#
"
,
"
className
"
:
"
translate
"
+
(
home
?
"
ui-btn ui-btn-icon-notext ui-icon-home
"
+
"
ui-shadow ui-corner-all
"
:
"
ui-link
"
)
},
{
"
data-enhanced
"
:
"
true
"
},
{
"
text
"
:
home
?
"
Home
"
:
(
title
||
""
),
"
data-i18n
"
:
home
?
"
pages.home.title
"
:
(
title
?
"
pages.
"
+
title
.
split
(
"
?
"
)[
0
]
+
"
.title
"
:
null
)
}
);
};
// home
breadcrumb
.
appendChild
(
makeLink
(
app
.
default_dict
.
path_dict
.
home
,
undefined
,
true
)
);
// fragments
for
(
i
=
0
;
i
<
url_dict
.
fragment_list
.
length
;
i
+=
1
)
{
crumb
=
url_dict
.
fragment_list
[
i
];
indicator
=
i
===
0
?
"
#
"
:
"
::
"
;
breadcrumb
.
appendChild
(
document
.
createTextNode
(
"
|
\
u00A0
"
));
breadcrumb
.
appendChild
(
makeLink
(
indicator
+
crumb
,
crumb
)
);
breadcrumb
.
appendChild
(
document
.
createTextNode
(
"
\
u00A0
"
));
}
// translate
if
(
i18n
)
{
factory
.
map_actions
.
translateNodeList
(
breadcrumb
);
}
return
breadcrumb
;
};
/*
====================================================================== */
/*
*
/* APP */
* Set the page title
/* ====================================================================== */
* @method setPageTitle
/*
* @param {string} page_i18n Lookup value for title
*
Object containing application related methods
*
@param {object} page_header Header, in case it's still being generated
*/
*/
app
=
{};
app
.
setPageTitle
=
function
(
page_i18n
,
page_header
)
{
var
title
,
value
=
factory
.
map_actions
.
translateLookup
(
page_i18n
),
header
=
page_header
.
length
?
page_header
[
0
]
:
(
document
.
getElementById
(
"
global-header
"
)
||
// WARNING: IE8- children() retrieves comments, too
document
.
getElementById
(
util
.
getActivePage
()).
children
[
0
]);
/*
if
(
util
.
testForClass
(
header
.
className
,
"
ui-header
"
))
{
* Object containing default names, which can be overridden
title
=
header
.
getElementsByTagName
(
"
h1
"
)[
0
];
*/
title
.
setAttribute
(
"
data-i18n
"
,
(
page_i18n
||
""
));
app
.
default_dict
=
{
title
.
removeChild
(
title
.
childNodes
[
0
]);
"
storage_dict
"
:
{
title
.
appendChild
(
document
.
createTextNode
((
value
||
"
\
u00A0
"
)));
"
settings
"
:
"
settings
"
,
"
items
"
:
"
items
"
,
"
configuration
"
:
"
configuration
"
,
"
gadgets
"
:
"
gagdets
"
,
"
data_type
"
:
"
portal_types
"
},
"
path_dict
"
:
{
"
data
"
:
"
data
"
}
}
document
.
title
=
value
;
};
};
/**
* Timer for 500ms delayed actions
*/
app
.
timer
=
0
;
/**
/**
* Create/update elements
* Create/update elements
* @method setupPageElements
* @method setupPageElements
...
@@ -4128,7 +4078,6 @@
...
@@ -4128,7 +4078,6 @@
app
.
setContent
=
function
(
content_dict
,
url_dict
,
create
)
{
app
.
setContent
=
function
(
content_dict
,
url_dict
,
create
)
{
var
i
,
skip
,
container
,
target
,
baggage
,
spec
;
var
i
,
skip
,
container
,
target
,
baggage
,
spec
;
// TODO: make sure 3rd parameter does not interfere in any widget method!
// TODO: make sure formElement parameters can always be set like this
// TODO: make sure formElement parameters can always be set like this
// when generating a form, all parameters for formElement are used, when
// when generating a form, all parameters for formElement are used, when
// doing it from here, they are not!
// doing it from here, they are not!
...
@@ -4154,9 +4103,8 @@
...
@@ -4154,9 +4103,8 @@
};
};
}
else
{
}
else
{
// prepare to fetch dynamic data
// prepare to fetch dynamic data
// TODO: check if still needed to pass all page related info
baggage
=
{
baggage
=
{
"
title
"
:
content_dict
.
title
||
""
,
"
title_i18n
"
:
content_dict
.
title_i18n
||
""
,
"
mode
"
:
spec
.
mode
||
null
,
"
mode
"
:
spec
.
mode
||
null
,
"
create
"
:
create
,
"
create
"
:
create
,
"
layout_level
"
:
spec
.
layout_level
||
null
,
"
layout_level
"
:
spec
.
layout_level
||
null
,
...
@@ -4179,7 +4127,7 @@
...
@@ -4179,7 +4127,7 @@
"
attachment
"
:
baggage
.
id
||
app
.
default_dict
.
storage_dict
.
configuration
,
"
attachment
"
:
baggage
.
id
||
app
.
default_dict
.
storage_dict
.
configuration
,
"
baggage
"
:
baggage
"
baggage
"
:
baggage
})
})
.
then
(
app
.
parse
Gadget
Configuration
)
.
then
(
app
.
parseConfiguration
)
// TODO: remove once working with live data
// TODO: remove once working with live data
// ===== SAMPLE DATA ========
// ===== SAMPLE DATA ========
.
then
(
app
.
testStorageForData
)
.
then
(
app
.
testStorageForData
)
...
@@ -4253,7 +4201,6 @@
...
@@ -4253,7 +4201,6 @@
}
}
};
};
/**
/**
* Generate gadget content based on query data and passed config
* Generate gadget content based on query data and passed config
* @method generateGadgetContent
* @method generateGadgetContent
...
@@ -4285,11 +4232,6 @@
...
@@ -4285,11 +4232,6 @@
(
baggage
.
create
===
false
?
true
:
null
)
(
baggage
.
create
===
false
?
true
:
null
)
);
);
// set header
if
(
baggage
.
create
)
{
init
.
setPageTitle
(
baggage
.
title_i18n
);
}
// translate
// translate
factory
.
map_actions
.
translateNodeList
(
element
);
factory
.
map_actions
.
translateNodeList
(
element
);
...
@@ -4309,6 +4251,7 @@
...
@@ -4309,6 +4251,7 @@
baggage
.
state
.
constructor
=
baggage
.
constructor
;
baggage
.
state
.
constructor
=
baggage
.
constructor
;
baggage
.
state
.
selected
=
baggage
.
create
===
false
?
baggage
.
state
.
selected
=
baggage
.
create
===
false
?
(
baggage
.
state
.
selected
)
:
undefined
;
(
baggage
.
state
.
selected
)
:
undefined
;
// WARNING: this should use data(), it is bad practice to store like this
selector
.
state
=
baggage
.
state
;
selector
.
state
=
baggage
.
state
;
init
.
updateInfoFields
(
init
.
updateInfoFields
(
...
@@ -4333,6 +4276,7 @@
...
@@ -4333,6 +4276,7 @@
if
(
baggage
.
skip
===
undefined
)
{
if
(
baggage
.
skip
===
undefined
)
{
// single item query
// single item query
// TODO: more levels? how to generalize and not only search by _id?
// TODO: more levels? how to generalize and not only search by _id?
// TODO: if this is just setting content_dict? then make it work without!
if
(
baggage
.
layout_level
>
0
)
{
if
(
baggage
.
layout_level
>
0
)
{
baggage
.
value
=
baggage
.
fragments
[
baggage
.
layout_level
];
baggage
.
value
=
baggage
.
fragments
[
baggage
.
layout_level
];
}
}
...
@@ -4344,7 +4288,7 @@
...
@@ -4344,7 +4288,7 @@
baggage
.
state
.
query
.
limit
=
baggage
.
store_limit
;
baggage
.
state
.
query
.
limit
=
baggage
.
store_limit
;
delete
baggage
.
store_limit
;
delete
baggage
.
store_limit
;
}
else
{
}
else
{
baggage
.
state
.
query
=
init
.
generateQueryObject
(
baggage
.
state
.
query
=
app
.
generateQueryObject
(
baggage
.
config
.
initial_query
,
baggage
.
config
.
initial_query
,
baggage
.
type
,
baggage
.
type
,
'
_id
'
,
'
_id
'
,
...
@@ -4354,7 +4298,7 @@
...
@@ -4354,7 +4298,7 @@
// get an item?
// get an item?
if
(
baggage
.
mode
!==
"
new
"
||
baggage
.
config
.
initial_query
!==
undefined
)
{
if
(
baggage
.
mode
!==
"
new
"
||
baggage
.
config
.
initial_query
!==
undefined
)
{
return
init
.
fetchData
({
return
app
.
fetchData
({
"
storage
"
:
"
items
"
,
"
storage
"
:
"
items
"
,
"
query
"
:
baggage
.
state
.
query
,
"
query
"
:
baggage
.
state
.
query
,
"
baggage
"
:
baggage
"
baggage
"
:
baggage
...
@@ -4385,16 +4329,17 @@
...
@@ -4385,16 +4329,17 @@
baggage
.
state
=
{};
baggage
.
state
=
{};
baggage
.
state
.
method
=
baggage
.
constructor
;
baggage
.
state
.
method
=
baggage
.
constructor
;
if
(
baggage
.
config
.
initial_query
)
{
if
(
baggage
.
config
.
initial_query
)
{
baggage
.
state
.
query
=
init
.
generateQueryObject
(
baggage
.
state
.
query
=
app
.
generateQueryObject
(
{
"
query
"
:
baggage
.
config
.
initial_query
.
query
},
{
"
query
"
:
baggage
.
config
.
initial_query
.
query
},
baggage
.
type
baggage
.
type
);
);
}
}
}
}
// skip total for single item layouts!
// skip total for single item layouts!
if
(
baggage
.
config
.
initial_query
)
{
if
(
baggage
.
config
.
initial_query
)
{
baggage
.
state
.
initial_query
=
baggage
.
config
.
initial_query
.
query
;
baggage
.
state
.
initial_query
=
baggage
.
config
.
initial_query
.
query
;
return
init
.
fetchData
({
return
app
.
fetchData
({
"
baggage
"
:
baggage
,
"
baggage
"
:
baggage
,
"
storage
"
:
"
items
"
,
"
storage
"
:
"
items
"
,
"
query
"
:
baggage
.
state
.
query
"
query
"
:
baggage
.
state
.
query
...
@@ -4505,9 +4450,9 @@
...
@@ -4505,9 +4450,9 @@
// try to get 1 record
// try to get 1 record
if
(
baggage
.
create
!==
false
&&
baggage
.
config
.
initial_query
)
{
if
(
baggage
.
create
!==
false
&&
baggage
.
config
.
initial_query
)
{
return
init
.
fetchData
({
return
app
.
fetchData
({
"
storage
"
:
"
items
"
,
"
storage
"
:
"
items
"
,
"
query
"
:
init
.
generateQueryObject
({
"
limit
"
:
[
0
,
1
]},
baggage
.
type
),
"
query
"
:
app
.
generateQueryObject
({
"
limit
"
:
[
0
,
1
]},
baggage
.
type
),
"
baggage
"
:
baggage
"
baggage
"
:
baggage
});
});
}
}
...
@@ -4519,11 +4464,11 @@
...
@@ -4519,11 +4464,11 @@
// ======================== SAMPLE DATA ==================================
// ======================== SAMPLE DATA ==================================
/**
/**
* parses a gadget configuration file
* parses a gadget configuration file
* @method parse
Gadget
Configuration
* @method parseConfiguration
* @param {object} reply gadget configuration
* @param {object} reply gadget configuration
* @return {object} response object/promise
* @return {object} response object/promise
*/
*/
app
.
parse
Gadget
Configuration
=
function
(
reply
)
{
app
.
parseConfiguration
=
function
(
reply
)
{
var
parsed
,
baggage
=
reply
.
baggage
;
var
parsed
,
baggage
=
reply
.
baggage
;
if
(
baggage
.
skip
===
undefined
)
{
if
(
baggage
.
skip
===
undefined
)
{
...
@@ -4600,31 +4545,20 @@
...
@@ -4600,31 +4545,20 @@
};
};
/**
/**
*
Store a configuration file in storag
e
*
Try fetching data from JIO. Fallback to loading fake data until accessibl
e
* @method
storeConfigurationFile
* @method
fetchData
* @param {object}
e Response event
* @param {object}
parcel Storage, query options and baggage to return
* @return {object} promise object
* @return {object} promise object
/baggage
*/
*/
// NOTE: configuration is stored as attachment
// NOTE: until we have real data we load fake data on application init!
app
.
storeConfigurationFile
=
function
(
response
)
{
app
.
fetchData
=
function
(
parcel
)
{
var
storage_location
=
property_dict
.
store
||
return
storage
[
parcel
.
storage
].
allDocs
(
parcel
.
query
)
(
storage
?
storage
[
app
.
default_dict
.
storage_dict
.
settings
]
:
undefined
);
.
then
(
function
(
response
)
{
return
{
if
(
storage_location
===
undefined
)
{
"
response
"
:
response
,
util
.
errorHandler
({
"
error
"
:
"
getFromDisk: no storage defined
"
});
"
baggage
"
:
parcel
.
baggage
return
RSVP
.
all
([]);
};
}
return
storage_location
.
put
(
{
"
_id
"
:
(
property_dict
.
file
||
app
.
default_dict
.
storage_dict
.
settings
)}
).
then
(
function
()
{
return
storage_location
.
putAttachment
({
"
_id
"
:
(
property_dict
.
file
||
app
.
default_dict
.
storage_dict
.
settings
),
"
_attachment
"
:
(
property_dict
.
attachment
||
app
.
default_dict
.
storage_dict
.
configuration
),
"
_data
"
:
JSON
.
stringify
(
response
),
"
_mimetype
"
:
"
application/json
"
});
});
})
.
fail
(
util
.
errorHandler
);
};
};
/**
/**
...
@@ -4714,7 +4648,7 @@
...
@@ -4714,7 +4648,7 @@
}
}
if
(
typeof
raw_url
===
"
string
"
)
{
if
(
typeof
raw_url
===
"
string
"
)
{
config
=
init
.
parseLink
(
raw_url
);
config
=
app
.
parseLink
(
raw_url
);
if
(
e
)
{
if
(
e
)
{
if
(
document
.
getElementById
(
raw_url
.
split
(
"
#
"
).
pop
())
||
if
(
document
.
getElementById
(
raw_url
.
split
(
"
#
"
).
pop
())
||
...
@@ -4730,7 +4664,7 @@
...
@@ -4730,7 +4664,7 @@
e
.
preventDefault
();
e
.
preventDefault
();
}
else
{
}
else
{
//
HACK:
overwrite JQM history, so deeplinks are correctly handled
// overwrite JQM history, so deeplinks are correctly handled
if
(
$
.
mobile
.
navigate
.
history
.
initialDst
&&
if
(
$
.
mobile
.
navigate
.
history
.
initialDst
&&
window
.
location
.
hash
!==
""
)
{
window
.
location
.
hash
!==
""
)
{
...
@@ -4768,11 +4702,42 @@
...
@@ -4768,11 +4702,42 @@
.
then
(
function
(
reply
)
{
.
then
(
function
(
reply
)
{
return
app
.
setContent
(
reply
,
config
,
create
);
return
app
.
setContent
(
reply
,
config
,
create
);
})
})
.
then
(
init
.
setPageBindings
)
.
then
(
app
.
setPageBindings
)
.
fail
(
util
.
errorHandler
);
.
fail
(
util
.
errorHandler
);
}
}
};
};
/**
* Update a page (which may already be in the DOM)
* @method updatePage
* @param {object} e Event (pagebeforechange)
* @param {object} data Data passed along with this (JQM) event
*/
// TODO: awful selector!
// NOTE: removed data for JSLINT form parameters
app
.
updatePage
=
function
(
e
)
{
var
page
=
e
.
target
;
app
.
fetchConfiguration
({
"
storage
"
:
app
.
default_dict
.
storage_dict
.
settings
,
"
file
"
:
"
pages
"
,
"
attachment
"
:
page
.
id
,
"
baggage
"
:
undefined
})
.
then
(
function
(
reply
)
{
// TODO: bad, updating a page... based on state?
if
(
!
page
.
querySelectorAll
(
"
div.ui-content
"
)[
0
]
.
getAttribute
(
"
data-bound
"
))
{
return
app
.
setContent
(
reply
,
{
"
id
"
:
page
.
id
,
"
href
"
:
window
.
location
.
href
},
undefined
);
}
})
.
fail
(
util
.
errorHandler
);
};
/**
/**
* Set global bindings for all application elements
* Set global bindings for all application elements
* @method setGlobalBindings
* @method setGlobalBindings
...
@@ -4790,7 +4755,7 @@
...
@@ -4790,7 +4755,7 @@
// update
// update
.
on
(
"
pagebeforeshow
"
,
"
div.ui-page
"
,
function
(
e
,
data
)
{
.
on
(
"
pagebeforeshow
"
,
"
div.ui-page
"
,
function
(
e
,
data
)
{
init
.
updatePage
(
e
,
data
);
app
.
updatePage
(
e
,
data
);
})
})
// clean dynamic pages on hide
// clean dynamic pages on hide
...
@@ -4820,7 +4785,7 @@
...
@@ -4820,7 +4785,7 @@
case
"
button
"
:
case
"
button
"
:
case
"
checkbox
"
:
case
"
checkbox
"
:
case
"
reset
"
:
case
"
reset
"
:
init
.
action
(
e
);
app
.
action
(
e
);
break
;
break
;
default
:
default
:
val
=
element
.
value
;
val
=
element
.
value
;
...
@@ -4836,23 +4801,100 @@
...
@@ -4836,23 +4801,100 @@
// give user half second to pick his state
// give user half second to pick his state
app
.
timer
=
window
.
setTimeout
(
function
()
{
app
.
timer
=
window
.
setTimeout
(
function
()
{
element
.
setAttribute
(
"
data-last
"
,
val
);
element
.
setAttribute
(
"
data-last
"
,
val
);
init
.
action
(
e
);
app
.
action
(
e
);
app
.
timer
=
0
;
app
.
timer
=
0
;
},
500
);
},
500
);
break
;
break
;
}
}
}
else
{
}
else
{
init
.
action
(
e
);
app
.
action
(
e
);
}
}
})
})
// popup content loading
// popup content loading
.
find
(
"
#global_popup
"
)
.
find
(
"
#global_popup
"
)
.
on
(
"
popupbeforeposition
"
,
function
(
e
)
{
.
on
(
"
popupbeforeposition
"
,
function
(
e
)
{
factory
.
util
.
loadPopupContents
(
init
.
generateActionObject
(
e
));
factory
.
util
.
loadPopupContents
(
app
.
generateActionObject
(
e
));
});
});
};
};
/**
* Prevent filterable from triggering
* @method preventFilterableTrigger
* @params {object} element Filterable element
*/
// TODO: make sure this triggers only once!
app
.
preventFilterableTrigger
=
function
(
element
)
{
$
(
element
).
on
(
"
filterablebeforefilter
"
,
function
(
e
)
{
e
.
preventDefault
();
});
};
/**
* Set bindings on page specific elements after content has been appended
* @method setPageBindings
*/
// TODO: add local popups!
app
.
setPageBindings
=
function
()
{
var
i
,
j
,
form_element
,
filterable
,
captcha
,
result
,
form_list
=
document
.
getElementsByTagName
(
"
form
"
),
filter_list
=
document
.
querySelectorAll
(
"
[data-filter]
"
);
// disable default filtering of JQM filterable
// TODO: does not work! JQM still runs
for
(
i
=
0
;
i
<
filter_list
.
length
;
i
+=
1
)
{
filterable
=
filter_list
[
i
];
if
(
filterable
.
getAttribute
(
"
data-bound
"
)
===
null
)
{
filterable
.
setAttribute
(
"
data-bound
"
,
true
);
app
.
preventFilterableTrigger
(
filterable
);
}
}
// add validation to all forms
for
(
j
=
0
;
j
<
form_list
.
length
;
j
+=
1
)
{
form_element
=
form_list
[
j
];
captcha
=
document
.
getElementById
(
form_element
.
id
+
"
_captcha
"
);
// captcha
if
(
captcha
)
{
util
.
declareJS
(
"
http://www.google.com/recaptcha/api/js/recaptcha_ajax.js
"
).
then
(
function
(
e
)
{
Recaptcha
.
create
(
captcha
.
getAttribute
(
"
data-key
"
),
captcha
.
id
,
{
"
theme
"
:
"
red
"
,
"
callback
"
:
Recaptcha
.
focus_response_field
}
);
}).
fail
(
util
.
errorHandler
);
}
if
(
form_element
.
getAttribute
(
"
data-bound
"
)
===
null
)
{
form_element
.
setAttribute
(
"
data-bound
"
,
true
);
// TODO: javascript-able?
// NOTE: the script is mapped to validval, so replacing it
// requires it to add a different plugin here as well as
// updating all data-vv fields being set in mapFormField() and
// generateFormElement
$
(
form_element
).
validVal
({
validate
:
{
onKeyup
:
"
valid
"
,
onBlur
:
"
valid
"
},
form
:
{
onInvalid
:
util
.
return_out
}
});
}
}
};
/* ====================================================================== */
/* ====================================================================== */
/* UTILS */
/* UTILS */
/* ====================================================================== */
/* ====================================================================== */
...
@@ -4921,6 +4963,63 @@
...
@@ -4921,6 +4963,63 @@
return
undefined
;
return
undefined
;
};
};
/**
* Show and hide the jQuery Mobile loader passing a status message
* @method loader
* @param {boolean} show Whether to show or hide the loader
* @param {message} message The message to display in the loader
* @param {icon} icon Which icon to display when overriding the loader
*/
util
.
showStatus
=
function
(
show
,
message
,
icon
)
{
// hm... jQuery...
if
(
app
.
default_dict
.
loader
)
{
$
.
mobile
.
loading
(
show
?
"
show
"
:
"
hide
"
,
{
"
theme
"
:
app
.
default_dict
.
loader_theme
,
"
text
"
:
icon
===
undefined
?
message
:
""
,
"
html
"
:
icon
===
undefined
?
""
:
'
<span class="ui-icon-
'
+
icon
+
'
loader_icon> </span><h1 class="loader_message">
'
+
message
+
'
</h1>
'
}
);
}
else
{
util
.
errorHandler
({
"
error
"
:
"
showStatus: Loader not enabled
"
});
}
};
/** Determine if a property exists in an object
* @method findKey
* @param {object} Object The object to search
* @param {string} Pointer The nested object pointer
* @param {string} Key The key under which it is stored
* @param {string} Key The value to search
* @return {boolean} true/null
*/
// TODO: Storage specific method, move
util
.
findKey
=
function
(
object
,
pointer
,
key
,
value
)
{
var
i
,
obj
;
// simple query
if
(
object
[
key
]
===
value
)
{
return
true
;
}
if
(
object
[
pointer
])
{
for
(
i
=
0
;
i
<
object
[
pointer
].
length
;
i
+=
1
)
{
obj
=
object
[
pointer
][
i
];
if
(
obj
[
key
]
===
value
)
{
return
true
;
}
if
(
obj
[
pointer
])
{
util
.
findKey
(
obj
[
pointer
],
pointer
,
key
,
value
);
}
}
}
return
null
;
};
/**
/**
* Promise-optimized Ajax (same as used in JIO, thx
* Promise-optimized Ajax (same as used in JIO, thx
* http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/jio.js
* http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/jio.js
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment