diff --git a/index.html b/index.html
index 63575124..be1f29a0 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,7 @@
+
@@ -35,7 +36,6 @@
-
diff --git a/src/assets/groups.json b/src/assets/groups.json
index bb05bd01..b8c6ed6e 100644
--- a/src/assets/groups.json
+++ b/src/assets/groups.json
@@ -21,10 +21,27 @@
"Boolean": ["#0._hidden_node.jqm_input_boolean"]
}
],
- "List Views": [
- "List", "OrderedList", "ListItem", "ListDivider", "ListButton"
- ],
- "Image": ["Image"]
+ "Image": ["Image"],
+ "List Views": [ {
+ "Single Lists": [
+ "SimpleListItem",
+ "ListDivider",
+ "SimpleList",
+ "ButtonList",
+ "TextList",
+ "IconList",
+ "ThumbnailList"
+ ],
+ "Split Lists": [
+ "SimpleListItem",
+ "ListDivider",
+ "ButtonSplitList",
+ "TextSplitList",
+ "IconSplitList",
+ "ThumbnailSplitList"
+ ]
+ }
+ ]
}
],
diff --git a/src/css/builder.css b/src/css/builder.css
index 6ee74f20..a01c0329 100644
--- a/src/css/builder.css
+++ b/src/css/builder.css
@@ -340,7 +340,7 @@ body.ui-tabs.ui-widget {
position: relative;
display: inline-block;
zoom: 1;
- width: 102px;
+ width: 115px;
z-index: 999;
}
.view.live .deviceSelect > a {
@@ -418,6 +418,10 @@ body.ui-tabs.ui-widget {
left: 120px;
top: 0px;
}
+#deviceSelectMenu li ul li {
+ overflow: hidden;
+ text-overflow: ellipsis
+}
.view.live .deviceSelect .arrow b {
display: block;
}
@@ -929,7 +933,7 @@ div.propertyItems > div > * {
display: table-cell;
padding-bottom: 8px;
}
-table#selectOption {
+table.selectTable {
width: 100%;
}
div.propertyItems label[for] {
diff --git a/src/css/composer.css b/src/css/composer.css
index 5663474c..40b1d136 100644
--- a/src/css/composer.css
+++ b/src/css/composer.css
@@ -48,7 +48,7 @@
/*************************/
/* Class specific tweaks */
/*************************/
-.nrc-sortable-container.ui-content .adm-node {
+.nrc-sortable-container.ui-content > * {
margin-top: 10px;
margin-bottom: 10px;
}
diff --git a/src/css/images/widgets/jqm_button_list.svg b/src/css/images/widgets/jqm_button_list.svg
new file mode 100644
index 00000000..647be337
--- /dev/null
+++ b/src/css/images/widgets/jqm_button_list.svg
@@ -0,0 +1,44 @@
+
+
+
diff --git a/src/css/images/widgets/jqm_button_split_list.svg b/src/css/images/widgets/jqm_button_split_list.svg
new file mode 100644
index 00000000..edd6c5ce
Binary files /dev/null and b/src/css/images/widgets/jqm_button_split_list.svg differ
diff --git a/src/css/images/widgets/jqm_icon_list.svg b/src/css/images/widgets/jqm_icon_list.svg
new file mode 100644
index 00000000..af655fe4
Binary files /dev/null and b/src/css/images/widgets/jqm_icon_list.svg differ
diff --git a/src/css/images/widgets/jqm_icon_list_button.svg b/src/css/images/widgets/jqm_icon_list_button.svg
new file mode 100644
index 00000000..53c6e1f2
--- /dev/null
+++ b/src/css/images/widgets/jqm_icon_list_button.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/src/css/images/widgets/jqm_icon_split_list.svg b/src/css/images/widgets/jqm_icon_split_list.svg
new file mode 100644
index 00000000..7293b6df
Binary files /dev/null and b/src/css/images/widgets/jqm_icon_split_list.svg differ
diff --git a/src/css/images/widgets/jqm_list.svg b/src/css/images/widgets/jqm_list.svg
index ff9dd70b..4481b865 100644
--- a/src/css/images/widgets/jqm_list.svg
+++ b/src/css/images/widgets/jqm_list.svg
@@ -32,7 +32,7 @@
-Unordered List
+Simple List
diff --git a/src/css/images/widgets/jqm_list_split_button.svg b/src/css/images/widgets/jqm_list_split_button.svg
deleted file mode 100644
index 2411051f..00000000
--- a/src/css/images/widgets/jqm_list_split_button.svg
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
diff --git a/src/css/images/widgets/jqm_text_list.svg b/src/css/images/widgets/jqm_text_list.svg
new file mode 100644
index 00000000..a093acef
--- /dev/null
+++ b/src/css/images/widgets/jqm_text_list.svg
@@ -0,0 +1,44 @@
+
+
+
diff --git a/src/css/images/widgets/jqm_text_list_button.svg b/src/css/images/widgets/jqm_text_list_button.svg
new file mode 100644
index 00000000..7bee4b5c
Binary files /dev/null and b/src/css/images/widgets/jqm_text_list_button.svg differ
diff --git a/src/css/images/widgets/jqm_text_split_list.svg b/src/css/images/widgets/jqm_text_split_list.svg
new file mode 100644
index 00000000..f9faa178
Binary files /dev/null and b/src/css/images/widgets/jqm_text_split_list.svg differ
diff --git a/src/css/images/widgets/jqm_thumbnail_list.svg b/src/css/images/widgets/jqm_thumbnail_list.svg
new file mode 100644
index 00000000..a1da7b67
Binary files /dev/null and b/src/css/images/widgets/jqm_thumbnail_list.svg differ
diff --git a/src/css/images/widgets/jqm_thumbnail_list_button.svg b/src/css/images/widgets/jqm_thumbnail_list_button.svg
new file mode 100644
index 00000000..2dfb18aa
--- /dev/null
+++ b/src/css/images/widgets/jqm_thumbnail_list_button.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/src/css/images/widgets/jqm_thumbnail_split_list.svg b/src/css/images/widgets/jqm_thumbnail_split_list.svg
new file mode 100644
index 00000000..309ee419
Binary files /dev/null and b/src/css/images/widgets/jqm_thumbnail_split_list.svg differ
diff --git a/src/js/adm.js b/src/js/adm.js
index 99b1f1bf..cf0c6f11 100644
--- a/src/js/adm.js
+++ b/src/js/adm.js
@@ -359,7 +359,7 @@ ADM.endTransaction = function () {
* @return {ADMNode} The child object, on success; null, on failure.
*/
ADM.addChild = function (parentRef, childRef, dryrun) {
- var parent, child;
+ var parent, child, oldParent, oldType, oldZone, oldZoneIndex;
parent = ADM.toNode(parentRef);
if (!parent) {
@@ -379,6 +379,12 @@ ADM.addChild = function (parentRef, childRef, dryrun) {
return null;
}
+ oldParent = child.getParent();
+ if (oldParent) {
+ oldType = child.getType();
+ oldZone = child.getZone();
+ oldZoneIndex = child.getZoneIndex();
+ }
if (parent.addChild(child, dryrun)) {
if (dryrun) {
return true;
@@ -388,7 +394,11 @@ ADM.addChild = function (parentRef, childRef, dryrun) {
ADM.transaction({
type: "add",
parent: child.getParent(),
- child: child
+ child: child,
+ oldParent: oldParent,
+ oldType: oldType,
+ oldZone: oldZone,
+ oldZoneIndex: oldZoneIndex
});
return child;
}
@@ -475,7 +485,7 @@ ADM.addChildRecursive = function (parentRef, childRef, dryrun) {
* @private
*/
ADM.insertChildRelative = function (siblingRef, childRef, offset, dryrun) {
- var sibling, child;
+ var sibling, child, oldParent, oldType, oldZone, oldZoneIndex;
sibling = ADM.toNode(siblingRef);
if (!sibling) {
@@ -496,6 +506,12 @@ ADM.insertChildRelative = function (siblingRef, childRef, offset, dryrun) {
childRef);
}
+ oldParent = child.getParent();
+ if (oldParent) {
+ oldType = child.getType();
+ oldZone = child.getZone();
+ oldZoneIndex = child.getZoneIndex();
+ }
if (sibling.insertChildRelative(child, offset, dryrun)) {
if (dryrun) {
return true;
@@ -504,7 +520,11 @@ ADM.insertChildRelative = function (siblingRef, childRef, offset, dryrun) {
type: "insertRelative",
sibling: sibling,
child: child,
- offset: offset
+ offset: offset,
+ oldParent: oldParent,
+ oldType: oldType,
+ oldZone: oldZone,
+ oldZoneIndex: oldZoneIndex
});
return child;
}
@@ -760,21 +780,21 @@ ADM.transaction = function (obj) {
*/
ADM.undo = function () {
var obj, undo = function (obj) {
- if (obj.type === "add") {
+ if ( ["add", "insertRelative", "move"].indexOf(obj.type) !== -1) {
ADM.ensurePageInactive(obj.child);
- obj.parent.removeChild(obj.child);
+ if (obj.oldParent) {
+ if (obj.oldType !== obj.child.getType())
+ obj.child.morphTo(obj.oldType);
+ obj.child.moveNode(obj.oldParent, obj.oldZone, obj.oldZoneIndex);
+ ADM.setSelected(obj.child);
+ }
+ else
+ obj.child.getParent().removeChild(obj.child);
}
else if (obj.type === "remove") {
obj.parent.insertChildInZone(obj.child, obj.zone, obj.zoneIndex);
ADM.setSelected(obj.child);
}
- else if (obj.type === "move") {
- obj.node.moveNode(obj.oldParent, obj.oldZone, obj.oldZoneIndex);
- ADM.setSelected(obj.node);
- }
- else if (obj.type === "insertRelative") {
- obj.sibling.getParent().removeChild(obj.child);
- }
else if (obj.type === "propertyChange") {
// TODO: this could require deeper copy of complex properties
obj.node.setProperty(obj.property, obj.oldValue, obj.data);
@@ -1008,18 +1028,8 @@ function ADMNode(widgetType) {
var currentType = widgetType, widget, zones, length, i, func;
this._valid = false;
- this._inheritance = [];
-
- while (currentType) {
- widget = BWidgetRegistry[currentType];
- if (typeof widget === "object") {
- this._inheritance.push(currentType);
- currentType = widget.parent;
- } else {
- console.error("Error: invalid type hierarchy creating ADM node");
- return;
- }
- }
+ this._inheritance = [widgetType];
+ $.merge(this._inheritance, BWidget.getAncestors(widgetType));
this._uid = ++ADMNode.prototype._lastUid;
@@ -1452,6 +1462,18 @@ ADMNode.prototype.hasUserVisibleDescendants = function () {
return false;
};
+/**
+ * Change this node to another type
+ *
+ * @param {String} type The type this node will morph to.
+ * @return {ADMNode} The morphed node.
+ */
+ADMNode.prototype.morphTo = function (type) {
+ var morphedChild = ADM.createNode(type);
+ this._inheritance = morphedChild._inheritance;
+ this._zones = morphedChild._zones;
+ return this;
+};
/**
* Adds given child object to this object, generally at the end of the first
* zone that accepts the child.
@@ -1519,11 +1541,19 @@ ADMNode.prototype.addChild = function (child, dryrun) {
ADMNode.prototype.addChildToZone = function (child, zoneName, zoneIndex,
dryrun) {
// requires: assumes cardinality is "N", or a numeric string
- var add = false, myType, childType, zone, cardinality, limit;
+ var add = false, myType, childType, zone, cardinality, limit, morph,
+ morphedChildType, morphedChild;
myType = this.getType();
childType = child.getType();
zone = this._zones[zoneName];
+ morph = BWidget.getZone(myType, zoneName).morph;
+ if (morph) {
+ morphedChildType = morph(childType, myType);
+ if (morphedChildType !== childType) {
+ childType = morphedChildType;
+ }
+ }
if (!BWidget.zoneAllowsChild(myType, zoneName, childType)) {
if (!dryrun) {
console.warn("Warning: zone " + zoneName +
@@ -1546,6 +1576,7 @@ ADMNode.prototype.addChildToZone = function (child, zoneName, zoneIndex,
return false;
}
+ cardinality = cardinality.max || cardinality;
if (cardinality !== "N") {
limit = parseInt(cardinality, 10);
if (zone.length >= limit) {
@@ -1630,7 +1661,8 @@ ADMNode.prototype.insertChildInZone = function (child, zoneName, index,
}
}
- var zone = this._zones[zoneName];
+ var zone = this._zones[zoneName], oldParent,
+ myType, childType, morph, morphedChildType;
if (!zone) {
console.error("Error: zone not found in insertChildInZone: " +
zoneName);
@@ -1641,7 +1673,22 @@ ADMNode.prototype.insertChildInZone = function (child, zoneName, index,
return false;
}
if (child instanceof ADMNode) {
+ oldParent = child.getParent();
+ if (oldParent) {
+ if (oldParent === this && child.getZone() === zoneName
+ && child.getZoneIndex() < index)
+ index --;
+ return child.moveNode(this, zoneName, index, dryrun);
+ }
if (!dryrun) {
+ myType = this.getType();
+ childType = child.getType();
+ morph = BWidget.getZone(myType, zoneName).morph;
+ if (morph) {
+ morphedChildType = morph(childType, myType);
+ if (morphedChildType != childType)
+ child.morphTo(morphedChildType);
+ }
zone.splice(index, 0, child);
setRootRecursive(child, this._root);
@@ -1757,12 +1804,30 @@ ADMNode.prototype.removeChild = function (child, dryrun) {
* @return {ADMNode} The removed child, or null if not found.
*/
ADMNode.prototype.removeChildFromZone = function (zoneName, index, dryrun) {
- var zone, removed, child, parentNode, parent;
+ var zone, removed, child, parent, cardinality, min;
zone = this._zones[zoneName];
if (!zone) {
console.error("Error: no such zone found while removing child: " +
zoneName);
}
+ cardinality = BWidget.getZoneCardinality(this.getType(), zoneName);
+ if (!cardinality) {
+ console.warn("Warning: no cardinality found for zone " + zoneName);
+ return false;
+ }
+
+ if (cardinality.min) {
+ min = parseInt(cardinality.min, 10);
+
+ if (zone.length <= min) {
+ alert("At least "
+ + cardinality.min + " "
+ + BWidget.getDisplayLabel(zone[index].getType())
+ + (min === 1 ? " " : "s ")
+ + (min === 1 ? "is": "are") + " required and cannot be deleted!");
+ return false;
+ }
+ }
if (dryrun) {
removed = [zone[index]];
diff --git a/src/js/composer.js b/src/js/composer.js
index 36618177..8fded684 100644
--- a/src/js/composer.js
+++ b/src/js/composer.js
@@ -279,58 +279,6 @@ $(function() {
}
};
- var addNewNodeOnDrop = function (domParent, domChildren, adm, pid,
- nodeRef, role, domNode) {
- var newNode, domSibling, sid, admChildren, domIndex, debug;
-
- debug = (window.top.$.rib && window.top.$.rib.debug());
- domIndex = domChildren.index(domNode);
-
- // Append first(only)/last child to this container
- if (domIndex >= domChildren.length-1 || role === 'page') {
- if (adm.addChild(pid, nodeRef, true)) {
- newNode = adm.addChild(pid, nodeRef);
- debug && console.log('Appended node',role);
- newNode && adm.setSelected(newNode);
- } else {
- console.warn('Append child failed:',role);
- }
- } else if (domIndex > 0) {
- // Insert nth child into this container
- domSibling = $(domNode, domParent).prev('.adm-node');
- sid = domSibling.attr('data-uid');
- if (adm.insertChildAfter(sid, nodeRef, true)) {
- newNode = adm.insertChildAfter(sid, nodeRef);
- debug && console.log('Inserted nth node',role);
- newNode && adm.setSelected(newNode);
- } else {
- console.warn('Insert nth child failed:',role);
- }
- } else {
- // Add 1st child into an empty container
- if (domChildren.length-1 <= 0) {
- if (adm.addChild(pid, nodeRef, true)) {
- newNode = adm.addChild(pid, nodeRef);
- debug && console.log('Added 1st node',role);
- newNode && adm.setSelected(newNode);
- } else {
- console.warn('Add 1st child failed:',role);
- }
- } else {
- // Insert 1st child into non-empty container
- admChildren = adm.toNode(pid).getChildren();
- sid = admChildren.length && admChildren[0].getUid();
- if (adm.insertChildBefore(sid, nodeRef, true)) {
- newNode = adm.insertChildBefore(sid, nodeRef);
- debug && console.log('Inserted 1st node', role);
- newNode && adm.setSelected(newNode);
- } else {
- console.warn('Insert 1st child failed:', role);
- }
- }
- }
- };
-
window.top.$.rib = window.top.$.rib || {};
window.top.$.rib.dndfilter = dndfilter;
@@ -385,7 +333,7 @@ $(function() {
}
};
$(e.target).subtree().add(document)
- .unbind('click vmousedown vmousecancel vmouseup vmouseover focus'
+ .unbind('click vmousedown vmousecancel vmouseup vmouseover focus focusin'
+ ' vmouseout blur mousedown touchmove');
$(e.target).subtree('.adm-node:not(.delegation),.orig-adm-node').each(
@@ -557,7 +505,8 @@ $(function() {
// Collapsible's items are under .ui-collapsible-content
'> .ui-collapsible-content > .adm-node,' +
'> ul > li.adm-node,' +
- '> div > .adm-node,' +
+ '> div > div > a > .adm-node,' +
+ '> div.customHeader > .adm-node,' +
'> *.orig-adm-node:not(.ui-header,.ui-content,.ui-footer)',
start: function(event, ui){
trackOffsets('start: ',ui,$(this).data('sortable'));
@@ -642,16 +591,14 @@ $(function() {
},
stop: function(event, ui){
trackOffsets('stop: ',ui,$(this).data('sortable'));
- var type, isDrop,
+ var isDrop,
pid = $(this).attr('data-uid'),
- node = null,
adm = window.parent.ADM,
- bw = window.parent.BWidget,
root = adm.getDesignRoot(),
- node, zones, newParent, newZone,
- rdx, idx, cid, pid, sid,
+ nodeRef, newParent, newNode,
+ cid, pid,
sibling, children, parent,
- role, card;
+ role, prevItem = ui.item, nextItem = ui.item;
role = $(this).attr('data-role') || '';
@@ -702,87 +649,53 @@ $(function() {
ui.item.remove();
return false;
}
-
- // Drop from palette: add a node
if (isDrop) {
if (ui.item.data('adm-node')) {
- type = ui.item.data('adm-node').type;
+ nodeRef = ui.item.data('adm-node').type;
}
- if (!type) {
+ if (!nodeRef) {
console.warn('Drop failed: Missing node type');
ui.item.remove();
return false;
}
-
- children = $($(this).sortable('option', 'items'), this)
- .add(ui.item);
- addNewNodeOnDrop(this, children, adm, pid, type,
- role, ui.item);
- ui.item.remove();
- return;
-
- // Sorted from layoutView: move a node
- } else {
- children = ui.item.parent().children('.adm-node')
- .add(ui.item);
- idx = children.index(ui.item);
+ }
+ else {
cid = ui.item.attr('data-uid');
// closest() will select current element if it matches
// the selector, so we start with its parent.
+ nodeRef = cid && root.findNodeByUid(cid);
+ if (event && event.ctrlKey) {
+ nodeRef = adm.copySubtree(nodeRef);
+ }
+ }
+
+ while (prevItem[0] || nextItem[0]) {
+ prevItem = prevItem.prev('.adm-node');
+ if (prevItem[0] && (newNode = adm.insertChildAfter
+ (prevItem.attr('data-uid'), nodeRef)))
+ break;
+ nextItem = nextItem.next('.adm-node');
+ if (nextItem[0] && (newNode = adm.insertChildBefore
+ (nextItem.attr('data-uid'), nodeRef)))
+ break;
+ }
+ if (!prevItem[0] && !nextItem[0]) {
pid = ui.item.parent()
.closest('.adm-node.ui-sortable,'+
'.orig-adm-node.ui-sortable')
.attr('data-uid');
- node = cid && root.findNodeByUid(cid);
- if (event && event.ctrlKey) {
- node = adm.copySubtree(node); // Clone it
- addNewNodeOnDrop(this, children, adm, pid, node,
- role, ui.item);
- } else {
- newParent = pid && root.findNodeByUid(pid);
- zones = newParent && bw.getZones(newParent.getType());
- card = newParent && zones &&
- bw.getZoneCardinality(newParent.getType(),
- zones[0]);
-
- // Notify the ADM that element has been moved
- if ((zones && zones.length===1 && card !== '1')) {
- if (!node ||
- !adm.moveNode(node, newParent, zones[0],
- idx)) {
- console.warn('Move node failed');
- ui.item.remove();
- return false;
- } else {
- debug && console.log('Move node worked');
- if (node) adm.setSelected(node.getUid());
- }
- } else if (node && newParent &&
- newParent.getType() === 'Header') {
- for (var z=0; z < zones.length; z++) {
- if (adm.moveNode(node, newParent, zones[z],
- 0, true)) {
- newZone = zones[z];
- break;
- }
- }
- if (newZone) {
- adm.moveNode(node, newParent, newZone, 0);
- debug && console.log('Move node worked');
- if (node) adm.setSelected(node.getUid());
- } else {
- console.warn('Move node failed');
- ui.item.remove();
- return false;
- }
- } else {
- console.warn('Move node failed: invalid zone');
+ newParent = pid && root.findNodeByUid(pid);
+ if (!(newNode = adm.addChild(newParent, nodeRef))) {
+ if (isDrop) {
ui.item.remove();
- return false;
+ return true;
}
+ return false;
}
}
+ if (newNode)
+ adm.setSelected(newNode);
}
})
.bind('mousedown.composer', function(event) {
diff --git a/src/js/projects.js b/src/js/projects.js
index df82845f..7209ba88 100644
--- a/src/js/projects.js
+++ b/src/js/projects.js
@@ -27,7 +27,7 @@ $(function () {
// Filter to find sandbox resources
relativeFilter:{
type: "url-uploadable",
- value: /^(?!(https?|ftp):\/+).+/i
+ value: /^(?!((https?|ftp):|src)\/+).+/i
},
// Object to save refernce count for sandbox resource
resourceRef: {},
diff --git a/src/js/serialize.js b/src/js/serialize.js
index 533b1cab..555ec92f 100644
--- a/src/js/serialize.js
+++ b/src/js/serialize.js
@@ -425,11 +425,23 @@ $(function () {
}
function getDesignHeaders(design, useSandboxUrl) {
- var i, props, el, designRoot, headers;
+ var i, props, el, designRoot, headers, toCorrectPath;
designRoot = design || ADM.getDesignRoot();
headers = [];
props = designRoot.getProperty('metas');
+ toCorrectPath = function (header) {
+ var path = header.value;
+ // If need to use sandbox url
+ if (header.inSandbox) {
+ if (useSandboxUrl) {
+ path = toSandboxUrl(path);
+ } else {
+ path = path.replace(/^\//, '');
+ }
+ }
+ return path;
+ };
for (i in props) {
// Skip design only header properties
if (props[i].hasOwnProperty('designOnly') && props[i].designOnly) {
@@ -444,12 +456,7 @@ $(function () {
if ((typeof props[i].value !== 'string') || (props[i].value.length <= 0)) {
continue;
}
- // If need to use sandbox url
- if (useSandboxUrl && props[i].inSandbox) {
- el = el + '="' + toSandboxUrl(props[i].value) + '"';
- } else {
- el = el + '="' + props[i].value + '"';
- }
+ el = el + '="' + toCorrectPath(props[i]) + '"';
if (props[i].hasOwnProperty('content')) {
el = el + ' content="' + props[i].content + '"';
}
@@ -470,11 +477,7 @@ $(function () {
}
el = '';
headers.push(el);
}
@@ -492,11 +495,7 @@ $(function () {
}
el = '';
headers.push(el);
}
diff --git a/src/js/views/code.js b/src/js/views/code.js
index ce22af5a..bef6497d 100644
--- a/src/js/views/code.js
+++ b/src/js/views/code.js
@@ -47,6 +47,8 @@
selected = activePage.getUid();
}
}
+ if (selected)
+ widget._selectCode(widget._htmlDoc.doc, selected);
},
refresh: function(event, widget) {
diff --git a/src/js/views/layout.js b/src/js/views/layout.js
index e925c36c..98fa2f0f 100644
--- a/src/js/views/layout.js
+++ b/src/js/views/layout.js
@@ -428,15 +428,6 @@
// these lines.
$(domNode).removeAttr('disabled');
$(domNode).children().removeAttr('disabled');
-
- // Set default src for empty image to make them show up
- // TODO: This case may need to improve, such as we can show a box
- // with "empty image" message to notice the user
- if ((admNode.getType() === "Image") && (!admNode.getProperty('src'))) {
- if ($(domNode).is('img')) {
- $(domNode).attr('src', "src/css/images/widgets/tizen_image.svg");
- }
- }
}
});
})(jQuery);
diff --git a/src/js/views/live.js b/src/js/views/live.js
index 195657f7..075479ce 100644
--- a/src/js/views/live.js
+++ b/src/js/views/live.js
@@ -17,7 +17,9 @@
options: {
iframe: null,
- contentDocument: null
+ contentDocument: null,
+ maxDeviceSize: 10000,
+ minDeviceSize: 240,
},
_create: function() {
@@ -31,7 +33,9 @@
rotateDeviceButton,
widget = this,
screenCoordElement = function (name, min, className) {
- return $('')
+ return $('')
.attr("name", name)
.addClass(className);
},
@@ -72,6 +76,7 @@
buttonSet.append($('').click(function () {
var type = widget._projectDevice.type;
delete widget._userDevices[widget._projectDevice.name];
+ widget._findOptionByText(widget._recentDevices, widget._projectDevice.name).remove();
applyDeviceChange(deviceForm, type);
}));
deviceForm
@@ -168,7 +173,8 @@
.append("")
.appendTo(devicePanel);
- widget._recentDevices = $('').appendTo(deviceToolbar)
+ widget._recentDevices = $('')
+ .appendTo(deviceToolbar)
.append('')
.change(function() {
$("option:selected", this).each(function () {
@@ -273,6 +279,18 @@
.addClass("rotateDevice separated")
.appendTo(deviceToolbar)
.click( function () {
+ if (widget._screenWidth.val() < widget.options.minDeviceSize ||
+ widget._screenHeight.val() < widget.options.minDeviceSize) {
+ alert("Device size should not be less than "
+ + widget.options.minDeviceSize);
+ return;
+ }
+ else if (widget._screenWidth.val() > widget.options.maxDeviceSize ||
+ widget._screenHeight.val() > widget.options.maxDeviceSize) {
+ alert("Device size should not be greater than "
+ + widget.options.maxDeviceSize);
+ return;
+ }
widget._projectDevice.rotating = !widget._projectDevice.rotating;
widget._projectDevice.screenWidth = widget._screenHeight.val();
widget._projectDevice.screenHeight = widget._screenWidth.val();
@@ -423,7 +441,7 @@
$('').append( key )
.attr('id', key)
.append(
- $('>').addClass('fr')
+ $('').addClass('fr')
)
.append(
$('')
@@ -457,9 +475,11 @@
var scaleW = screenWidth/deviceInfo.screen.width,
scaleH = screenHeight/deviceInfo.screen.height;
deviceInfo.screen.width *= scaleW;
+ deviceInfo.screen.width = Math.round(deviceInfo.screen.width);
deviceInfo.screen.offset.left *= scaleW;
deviceInfo.screen.offset.right *= scaleW;
deviceInfo.screen.height *= scaleH;
+ deviceInfo.screen.height = Math.round(deviceInfo.screen.height);
deviceInfo.screen.offset.top *= scaleH;
deviceInfo.screen.offset.bottom *= scaleH;
diff --git a/src/js/views/property.js b/src/js/views/property.js
index 2f3eaa45..0495e433 100644
--- a/src/js/views/property.js
+++ b/src/js/views/property.js
@@ -85,35 +85,73 @@
widget.refresh(event,widget);
},
- _setProperty: function(property, value) {
- var viewId = property + '-value';
- if (typeof(value) === 'boolean') {
- this.element.find("#" + viewId).attr('checked', value);
- } else {
- this.element.find("#" + viewId).val(value);
+ _setProperty: function(property, propType, value) {
+ var element = this.element.find("#" + property + '-value');
+ switch (propType) {
+ case "boolean":
+ element.attr('checked', value);
+ break;
+ default:
+ element.val(value);
}
},
+ _redrawProperty: function(property, newElment) {
+ var element = this.element.find("#" + property + '-value');
+ element.replaceWith(newElment);
+ },
+
_modelUpdatedHandler: function(event, widget) {
- var affectedWidget, id, value;
+ var affectedWidget, id, value, propType;
widget = widget || this;
if (event && event.type === "propertyChanged") {
if (event.node.getType() !== 'Design') {
id = event.property + '-value';
affectedWidget = widget.element.find('#' + id);
- if (affectedWidget.attr('type') !== 'checkbox') {
- value = affectedWidget.val();
- } else {
- value = affectedWidget.attr('checked')?true:false;
+ propType = BWidget.getPropertyType(event.node.getType(), event.property);
+
+ // Get value to for commparation
+ switch (propType) {
+ case "boolean":
+ value = affectedWidget.attr('checked')?true:false;
+ break;
+ case "record-array":
+ value = event.oldValue; // FIXME: oldValue can't passed here.
+ break;
+ default:
+ value = affectedWidget.val();
+ break;
}
- if(event.newValue != value) {
- affectedWidget[0].scrollIntoViewIfNeeded();
- if(typeof(event.newValue) === 'boolean') {
- affectedWidget.effect('pulsate', { times:3 }, 200);
- } else {
- affectedWidget.effect('highlight', {}, 1000);
- }
- widget._setProperty(event.property, event.newValue);
+
+ // Compare the newValue is equal with value, then return directly.
+ if (event.newValue == value)
+ return
+
+ // Update ADM and apply effects.
+ switch (propType) {
+ case "boolean":
+ if (affectedWidget) {
+ affectedWidget[0].scrollIntoViewIfNeeded();
+ affectedWidget.effect('pulsate', { times:3 }, 200);
+ }
+ widget._setProperty(event.property, propType, event.newValue);
+ break;
+ case "record-array":
+ value = event.oldValue;
+ // TODO: Do something with affectedWidget
+ // affectedWidget = event.element;
+ widget._redrawProperty(
+ event.property,
+ widget._generateRecordArraryTable(widget, event.node, event.property)
+ );
+ break;
+ default:
+ if (affectedWidget) {
+ affectedWidget[0].scrollIntoViewIfNeeded();
+ affectedWidget.effect('highlight', {}, 1000);
+ }
+ widget._setProperty(event.property, propType, event.newValue);
+ break;
}
return;
} else if (event.property !== 'css') {
@@ -138,6 +176,183 @@
return;
},
+ _generateSelectMenuOption: function(node, property, child, index, props) {
+ var changeCallback = function(event) {
+ var newValue, self = $(this);
+ props[property].children[index][event.data.key] = self.val();
+ newValue = props[property];
+ node.fireEvent("modelUpdated", {
+ type: "propertyChanged",
+ node: node,
+ property: property,
+ element: self,
+ newValue: newValue,
+ index: index
+ });
+ };
+
+ if (!props)
+ props = node.getProperties();
+ return $('
').data('index', index)
+ .addClass("options")
+ .append(' | ')
+ .children().eq(0)
+ .append('
')
+ .children(':first')
+ .attr('src', "src/css/images/propertiesDragIconSmall.png")
+ .end()
+ .end().end()
+ .append(' | ')
+ .children().eq(1)
+ .append('')
+ .children().eq(0)
+ .val(child.text)
+ .addClass('title optionInput')
+ .change({key: 'text'}, changeCallback)
+ .end().end()
+ .end().end()
+ .append(' | ')
+ .children().eq(2)
+ .append('')
+ .children().eq(0)
+ .val(child.value)
+ .addClass('title optionInput')
+ .change({key: 'value'}, changeCallback)
+ .end().end()
+ .end().end()
+ .append(' | ')
+ .children().eq(3)
+ .append('Delete
')
+ .children(':first')
+ // add delete option handler
+ .click(function(e) {
+ try {
+ var newValue, self = $(this);
+ // Generate ADM properties
+ index = self.parent().parent().data('index');
+ props[property].children.splice(index, 1);
+ // Instead by draw, so comment following lines.
+ /*
+ // Remove the row element after clicked delete button
+ self.parent().parent().remove();
+ */
+ newValue = props[property];
+ // Trigger the modelUpdated event.
+ node.fireEvent("modelUpdated", {
+ type: "propertyChanged",
+ node: node,
+ property: property,
+ element: self,
+ newValue: newValue,
+ index: index
+ });
+ }
+ catch (err) {
+ console.error(err.message);
+ }
+ e.stopPropagation();
+ return false;
+ })
+ .end()
+ .end().end();
+ },
+
+ _generateRecordArraryTable: function(widget, node, property, props) {
+ var child, table = $('')
+ .attr('id', property + '-value')
+ .addClass('selectTable')
+ .attr('cellspacing', '5');
+
+ if (!props)
+ props = node.getProperties();
+
+ $('
')
+ .append(' | ')
+ .append(' Text | ')
+ .children().eq(1)
+ .addClass('title')
+ .end().end()
+ .append(' Value | ')
+ .children().eq(2)
+ .addClass('title')
+ .end().end()
+ .append(' | ')
+ .appendTo(table);
+ for (var i = 0; i< props[property].children.length; i++){
+ child = props[property].children[i];
+ table.append(
+ this._generateSelectMenuOption(node, property, child, i, props)
+ );
+ }
+
+ // add add items handler
+ $('| + add item |
')
+ .children(':first')
+ .addClass('rightLabel title')
+ .attr('id', 'addOptionItem')
+ .end()
+ .click(function(e) {
+ var newValue, self = $(this);
+ try {
+ var rowElement, index = props[property].children.length,
+ optionItem = {
+ 'text': 'Option',
+ 'value': 'Value'
+ };
+ props[property].children.push(optionItem);
+ // Instead by draw, so comment following lines.
+ /*
+ rowElement = widget._generateSelectMenuOption(
+ node, property, optionItem, index, props
+ );
+ */
+ table.append(rowElement);
+ $(this).insertAfter(rowElement);
+ newValue = props[property];
+ node.fireEvent("modelUpdated", {
+ type: "propertyChanged",
+ node: node,
+ property: property,
+ element: self,
+ newValue: newValue,
+ index: index
+ });
+ } catch (err) {
+ console.error(err.message);
+ }
+ e.stopPropagation();
+ return false;
+ })
+ .appendTo(table);
+
+ // make option sortable
+ table.sortable({
+ axis: 'y',
+ items: '.options',
+ containment: table.find('tbody'),
+ start: function(event, ui) {
+ widget.origRowIndex = ui.item.index() - 1;
+ },
+ stop: function(event, ui) {
+ var optionItem, curIndex = ui.item.index() - 1,
+ origIndex = widget.origRowIndex;
+ optionItem = props[property].children.splice(origIndex,1)[0];
+
+ props[property].children.splice(curIndex, 0, optionItem);
+ node.fireEvent("modelUpdated", {
+ type: "propertyChanged",
+ node: node,
+ property: property,
+ element: table,
+ newValue: props[property],
+ index: ui.item.index()
+ });
+ }
+ });
+
+ return table;
+ },
+
_showProperties: function(node) {
var labelId, labelVal, valueId, valueVal, count,
widget = this, type, i, child, index, propType,
@@ -145,7 +360,8 @@
design = ADM.getDesignRoot(),
title = this.element.parent().find('.property_title'),
content = this.element.find('.property_content'),
- continueToDelete, buttonsContainer, container, prerequisite;
+ continueToDelete, buttonsContainer, container, prerequisite,
+ range, min, max;
// Clear the properties pane when nothing is selected
if (node === null || node === undefined) {
@@ -216,6 +432,31 @@
value.find("#" + valueId).attr("checked", "checked");
}
break;
+ case "integer":
+ range = BWidget.getPropertyRange(type, p);
+ if (range) {
+ min = range.split('-')[0];
+ max = range.split('-')[1];
+ $('')
+ .addClass('title labelInput')
+ .attr({
+ id: valueId,
+ min: min,
+ max: max
+ })
+ .change(function(event) {
+ if( parseInt(this.value) > parseInt(this.max) ||
+ parseInt(this.value) < parseInt(this.min)) {
+ $(this).effect("highlight", {color: "red"}, 1000);
+ event.stopImmediatePropagation();
+ this.value = valueVal;
+ }
+ })
+ .appendTo(value);
+ //set default value
+ value.find('#' + valueId).val(valueVal);
+ }
+ break;
case "url-uploadable":
var array, datalist, uploadClick;
uploadClick = function (e) {
@@ -225,7 +466,7 @@
textInput = optionsWrapper.prev('input');
saveDir = $.rib.pmUtils.ProjectDir + "/" + $.rib.pmUtils.getActive() + "/images/";
- $.rib.fsUtils.upload("image", $(this).parent(), function(file) {
+ $.rib.fsUtils.upload("image", null, function(file) {
// Write uploaded file to sandbox
$.rib.fsUtils.write(saveDir + file.name, file, function (newFile) {
textInput.val("images/" + newFile.name).change();
@@ -251,137 +492,7 @@
break;
case "record-array":
- $('')
- .attr('id', 'selectOption')
- .attr('cellspacing', '5')
- .appendTo(value);
- var selectOption = value.find('#selectOption');
- $('
')
- .append(' | ')
- .append(' Text | ')
- .children().eq(1)
- .addClass('title')
- .end().end()
- .append(' Value | ')
- .children().eq(2)
- .addClass('title')
- .end().end()
- .append(' | ')
- .appendTo(selectOption);
- for (i = 0; i< props[p].children.length; i ++){
- child = props[p].children[i];
- $('
').data('index', i)
- .addClass("options")
- .append(' | ')
- .children().eq(0)
- .append('
')
- .children(':first')
- .attr('src', "src/css/images/propertiesDragIconSmall.png")
- .end()
- .end().end()
- .append(' | ')
- .children().eq(1)
- .append('')
- .children().eq(0)
- .val(child.text)
- .addClass('title optionInput')
- .change(node, function (event) {
- index = $(this).parent().parent().data('index');
- props['options'].children[index].text = $(this).val();
- node.fireEvent("modelUpdated",
- {type: "propertyChanged",
- node: node,
- property: 'options'});
- })
- .end().end()
- .end().end()
- .append(' | ')
- .children().eq(2)
- .append('')
- .children().eq(0)
- .val(child.value)
- .addClass('title optionInput')
- .change(node, function (event) {
- index = $(this).parent().parent().data('index');
- props['options'].children[index].value = $(this).val();
- node.fireEvent("modelUpdated",
- {type: "propertyChanged",
- node: node,
- property: 'options'});
- })
- .end().end()
- .end().end()
- .append(' | ')
- .children().eq(3)
- .append('Delete
')
- .children(':first')
- // add delete option handler
- .click(function(e) {
- try {
- index = $(this).parent().parent().data('index');
- props['options'].children.splice(index, 1);
- node.fireEvent("modelUpdated",
- {type: "propertyChanged",
- node: node,
- property: 'options'});
- }
- catch (err) {
- console.error(err.message);
- }
- e.stopPropagation();
- return false;
- })
- .end()
- .end().end()
- .appendTo(selectOption);
- }
-
- // add add items handler
- $('')
- .children(':first')
- .addClass('rightLabel title')
- .attr('id', 'addOptionItem')
- .end()
- .appendTo(value);
- value.find('#addOptionItem')
- .click(function(e) {
- try {
- var optionItem = {};
- optionItem.text = "Option";
- optionItem.value = "Value";
- props['options'].children.push(optionItem);
- node.fireEvent("modelUpdated",
- {type: "propertyChanged",
- node: node,
- property: 'options'});
- }
- catch (err) {
- console.error(err.message);
- }
- e.stopPropagation();
- return false;
- });
-
- // make option sortable
- value.find('#selectOption tbody').sortable({
- axis: 'y',
- items: '.options',
- containment: value.find('#selectOption tbody'),
- start: function(event, ui) {
- widget.origRowIndex = ui.item.index() - 1;
- },
- stop: function(event, ui) {
- var optionItem, curIndex = ui.item.index() - 1,
- origIndex = widget.origRowIndex;
- optionItem = props['options'].children.splice(origIndex,1)[0];
-
- props['options'].children.splice(curIndex, 0, optionItem);
- node.fireEvent("modelUpdated",
- {type: "propertyChanged",
- node: node,
- property: 'options'});
- }
- });
+ value.append(this._generateRecordArraryTable(widget, node, p, props));
break;
case "targetlist":
container = node.getParent();
diff --git a/src/js/views/tree.js b/src/js/views/tree.js
index 23d5560c..72bb5916 100644
--- a/src/js/views/tree.js
+++ b/src/js/views/tree.js
@@ -72,7 +72,6 @@
$(items[focusing]).addClass("focused");
items[focusing].scrollIntoViewIfNeeded();
}
- return false;
},
_setOption: function(key, value) {
diff --git a/src/js/widgets.js b/src/js/widgets.js
index de12b02d..aacc4dd8 100644
--- a/src/js/widgets.js
+++ b/src/js/widgets.js
@@ -25,6 +25,11 @@ var BCommonProperties = {
defaultValue: false,
htmlAttribute: "disabled"
},
+ filter: {
+ type: "boolean",
+ defaultValue: false,
+ htmlAttribute: "data-filter"
+ },
icon: {
type: "datalist",
options: [ "none", "alert", "arrow-d", "arrow-l", "arrow-r",
@@ -64,6 +69,10 @@ var BCommonProperties = {
defaultValue: "default",
htmlAttribute: "data-position"
},
+ text: {
+ type: "string",
+ defaultValue: "Button"
+ },
theme: {
type: "string",
options: function() {
@@ -88,6 +97,16 @@ var BCommonProperties = {
options: [ "default", "a", "b", "c", "d", "e" ],
defaultValue: "default",
htmlAttribute: "data-track-theme"
+ },
+ inset: {
+ type: "boolean",
+ defaultValue: true,
+ htmlAttribute: "data-inset",
+ // because data-inset="false" is the real default, do this:
+ forceAttribute: true
+ // FIXME: would be better to distinguish from the default that
+ // occurs if you leave it off, vs. the default we think
+ // the user is most likely to want
}
};
@@ -135,7 +154,8 @@ var BCommonProperties = {
* Each zone description in the array should be an object with:
* 1) name identifying the zone point
* 2) cardinality, either "1" or "N" representing number of contained items
- * 3) allow: string or array of string names of allowable widgets
+ * 3) allow: string or array of string names or a function that returns
+ * a string or an array of allowable widgets
* (all others will be denied)
* 4) deny: string or array of string names of disallowed widgets
* (all others will be allowed)
@@ -144,6 +164,8 @@ var BCommonProperties = {
* of this zone.
* 6) itemWrapper: an HTML tag used to wrapp a child node before appending
* to the zone
+ * 7) morph: function used to change the child type to another type before
+ * adding it to the zone
*
* The "properties" of each widget definition is an object, each property of
* which names a property of the widget. These are objects with the following
@@ -591,7 +613,11 @@ var BWidgetRegistry = {
locator: '> div',
cardinality: "N"
}
- ]
+ ],
+ delegate: function (domNode, admNode) {
+ $('> div', domNode).addClass('customHeader');
+ return domNode;
+ },
},
/**
@@ -831,26 +857,11 @@ var BWidgetRegistry = {
},
/**
- * Represents a button. A "text" string property holds the button text.
+ * Represents a base button widget
*/
- Button: {
+ ButtonBase: {
parent: "Base",
- paletteImageName: "jqm_button.svg",
- editable: {
- selector: "span > .ui-btn-text",
- propertyName: "text"
- },
properties: {
- text: {
- type: "string",
- defaultValue: "Button"
- },
- right: {
- displayName: "align right",
- validIn: "Header",
- type: "boolean",
- defaultValue: false
- },
target: {
type: "targetlist",
defaultValue: "",
@@ -878,6 +889,29 @@ var BWidgetRegistry = {
defaultValue: "slide",
htmlAttribute: "data-transition"
},
+ },
+ template: '%TEXT%'
+ },
+
+ /**
+ * Represents a button. A "text" string property holds the button text.
+ */
+
+ Button: {
+ parent: "ButtonBase",
+ paletteImageName: "jqm_button.svg",
+ editable: {
+ selector: "span > .ui-btn-text",
+ propertyName: "text"
+ },
+ properties: {
+ text: BCommonProperties.text,
+ right: {
+ displayName: "align right",
+ validIn: "Header",
+ type: "boolean",
+ defaultValue: false
+ },
icon: BCommonProperties.icon,
iconpos: $.extend({}, BCommonProperties.iconpos, {
invalidIn: "Navbar"
@@ -930,7 +964,7 @@ var BWidgetRegistry = {
properties: {
src: {
type: "url-uploadable",
- defaultValue: "",
+ defaultValue: "src/css/images/widgets/tizen_image.svg",
htmlAttribute: "src",
forceAttribute: true
},
@@ -1550,34 +1584,10 @@ var BWidgetRegistry = {
/**
* Represents a unordered list element.
*/
- List: {
- parent: "Base",
+ SimpleList: {
+ parent: "ListBase",
paletteImageName: "jqm_list.svg",
- dragHeader: true,
properties: {
- inset: {
- type: "boolean",
- defaultValue: true,
- htmlAttribute: "data-inset",
- // because data-inset="false" is the real default, do this:
- forceAttribute: true
- // FIXME: would be better to distinguish from the default that
- // occurs if you leave it off, vs. the default we think
- // the user is most likely to want
- },
- theme: BCommonProperties.theme,
- divider: {
- displayName: "divider theme",
- type: "string",
- options: [ "default", "a", "b", "c", "d", "e" ],
- defaultValue: "default",
- htmlAttribute: "data-divider-theme"
- },
- filter: {
- type: "boolean",
- defaultValue: false,
- htmlAttribute: "data-filter"
- },
filter_theme: $.extend({}, BCommonProperties.theme, {
displayName: "filter theme",
htmlAttribute: "data-filter-theme"
@@ -1589,12 +1599,80 @@ var BWidgetRegistry = {
htmlAttribute: "data-filter-placeholder"
}
},
- template: '',
+ },
+
+ /**
+ * Represents a unordered button list element.
+ */
+ ButtonList: {
+ parent: "SimpleList",
+ paletteImageName: "jqm_button_list.svg",
+ },
+
+ /**
+ * Represents a unordered button list element.
+ */
+ TextList: {
+ parent: "SimpleList",
+ paletteImageName: "jqm_text_list.svg",
+ },
+
+ /**
+ * Represents a icon list element.
+ */
+ IconList: {
+ parent: "SimpleList",
+ paletteImageName: "jqm_icon_list.svg",
+ },
+
+ /**
+ * Represents a Thumbnail list element.
+ */
+ ThumbnailList: {
+ parent: "SimpleList",
+ paletteImageName: "jqm_thumbnail_list.svg",
+ },
+ /**
+ * Represents a base List widget
+ */
+ ListBase: {
+ parent: "Base",
+ dragHeader: true,
+ properties: {
+ inset: BCommonProperties.inset,
+ filter: BCommonProperties.filter,
+ theme: BCommonProperties.theme,
+ ordered: {
+ type: "boolean",
+ defaultValue: false,
+ },
+ divider_theme: $.extend({}, BCommonProperties.theme, {
+ displayName: "divider theme",
+ htmlAttribute: "data-divider-theme"
+ })
+ },
+ getItemType: function (listType) {
+ if (listType !== "OrderedList")
+ return listType + "Item";
+ else
+ return "SimpleListItem";
+ },
zones: [
{
name: "default",
cardinality: "N",
- allow: [ "ListItem", "ListDivider", "ListButton" ]
+ morph: function (childType, thisType) {
+ if (childType === "SimpleListItem") {
+ if (thisType === "OrderedList")
+ return "SimpleListItem";
+ else
+ return thisType + "Item";
+ }
+ return childType;
+ },
+ allow: function (type) {
+ return [ BWidgetRegistry.ListBase.getItemType(type), "ListDivider" ];
+ }
}
],
delegate: function (domNode, admNode) {
@@ -1612,26 +1690,109 @@ var BWidgetRegistry = {
);
// Reconstruct the domNode.
return filterForm.wrap(newNode).parent().append(domNode);
+ },
+ template: function (node) {
+ if (node.getProperty("ordered"))
+ return $('');
+ else
+ return $('');
+ },
+ init: function (node) {
+ // initial state is three button ListItem
+ var i;
+ for (i = 0; i < 3; i++) {
+ var listType = node.getType(), itemName = "SimpleListItem";
+ if (listType !== "OrderedList")
+ itemName = listType + "Item";
+ node.addChild(ADM.createNode(itemName));
+ }
}
},
/**
- * Represents an ordered list element.
+ * Represents a Split list element.
*/
- OrderedList: {
- parent: "List",
- paletteImageName: "jqm_ordered_list.svg",
- template: ''
+ ThumbnailSplitList: {
+ parent: "ButtonSplitList",
+ paletteImageName: "jqm_thumbnail_split_list.svg",
},
/**
- * Represents a list item element.
+ * Represents a Split list element.
*/
- ListItem: {
+ ButtonSplitList: {
+ parent: "ListBase",
+ paletteImageName: "jqm_button_split_list.svg",
+ properties: {
+ theme: {
+ type: "string",
+ options: [ "default", "a", "b", "c", "d", "e" ],
+ defaultValue: "default",
+ htmlAttribute: "data-split-theme"
+ },
+ split_icon: {
+ displayName: "split icon",
+ type: "string",
+ options: [ "none", "alert", "arrow-d", "arrow-l", "arrow-r",
+ "arrow-u", "back", "check", "delete", "forward",
+ "gear", "grid", "home", "info", "minus", "plus",
+ "refresh", "search", "star" ],
+ defaultValue: "none",
+ htmlAttribute: "data-split-icon"
+ }
+ },
+ },
+
+ /**
+ * Represents a Split list element.
+ */
+ TextSplitList: {
+ parent: "ButtonSplitList",
+ paletteImageName: "jqm_text_split_list.svg",
+ },
+
+ /**
+ * Represents a Split list element.
+ */
+ IconSplitList: {
+ parent: "ButtonSplitList",
+ paletteImageName: "jqm_icon_split_list.svg",
+ },
+
+ /**
+ * Represents a base ListItem element.
+ */
+ ListItemBase: {
parent: "Base",
+ properties: {
+ countbubble: {
+ type: "string",
+ displayName: "count bubble",
+ defaultValue: ""
+ }
+ },
+ template: function(node) {
+ var prop, countBubble, code = $('');
+ prop = node.getProperty("countbubble");
+ // Add the count bubble if countbubble property is not blank
+ if (prop.trim() != '') {
+ countBubble = $('')
+ .attr('class', 'ui-li-count')
+ .html(prop);
+ code.append(countBubble);
+ };
+ return code;
+ }
+ },
+
+ /**
+ * Represents a generic list item element.
+ */
+ SimpleListItem: {
+ parent: "ListItemBase",
displayLabel: "List Item",
paletteImageName: "jqm_list_item.svg",
- allowIn: [ "List", "OrderedList" ],
+ allowIn: [ "ListBase" ],
editable: {
selector: "",
propertyName: "text"
@@ -1639,7 +1800,7 @@ var BWidgetRegistry = {
properties: {
text: {
type: "string",
- defaultValue: "List Item"
+ defaultValue: "List Item",
},
theme: BCommonProperties.theme,
filtertext: {
@@ -1649,27 +1810,240 @@ var BWidgetRegistry = {
htmlAttribute: "data-filtertext"
}
},
- template: '- %TEXT%
'
+ template: function (node) {
+ return BWidgetRegistry.ListItemBase.template(node).append(node.getProperty('text'));
+ },
},
/**
- * Represents a list divider element.
+ * Represents a Clickable ListItem element.
*/
- ListDivider: {
- parent: "Base",
- displayLabel: "List Divider",
- paletteImageName: "jqm_list_divider.svg",
- allowIn: [ "List", "OrderedList" ],
+ ClickableListItem: {
+ parent: [ "ListItemBase", "ButtonBase" ],
+ defaultHtmlSelector: "a",
+ properties: {
+ theme: $.extend(true, {}, BCommonProperties.theme, {
+ htmlSelector: ""
+ })
+ },
+ template: function (node) {
+ var code = BWidgetRegistry.ListItemBase.template(node);
+ return code.append($('').append(code.find('span.ui-li-count')));
+ },
+ },
+
+ /**
+ * Represents a list button item element.
+ */
+ ButtonListItem: {
+ parent: "ClickableListItem",
+ allowIn: [ "ButtonList" ],
editable: {
- selector: "",
+ selector: ".ui-btn-text > a",
propertyName: "text"
},
properties: {
text: {
type: "string",
+ defaultValue: "Button List Item",
+ }
+ },
+ template: function (node) {
+ return BWidgetRegistry.ClickableListItem.template(node)
+ .find('a').append(node.getProperty('text')).end();
+ },
+ },
+
+ /**
+ * Represents a list text item element.
+ */
+ TextListItem: {
+ parent: "ClickableListItem",
+ allowIn: [ "TextList" ],
+ zones: [
+ {
+ name: "default",
+ cardinality: "N",
+ locator: "a",
+ allow: [ "Text" ]
+ },
+ ],
+ init: function (node) {
+ var widgit;
+ widgit = ADM.createNode("Text");
+ widgit.setProperty("type", "h3");
+ widgit.setProperty("text", "Text List Item");
+ node.addChild(widgit);
+ widgit = ADM.createNode("Text");
+ widgit.setProperty("type", "p");
+ widgit.setProperty("text", "Text List Item");
+ node.addChild(widgit);
+ }
+ },
+
+ /**
+ * Represents a IconListItem element.
+ */
+ IconListItem: {
+ parent: "ButtonListItem",
+ allowIn: [ "IconList" ],
+ properties: {
+ text: {
+ defaultValue: "Icon List Item"
+ },
+ iconsrc: {
+ displayName: "icon source",
+ type: "url-uploadable",
+ defaultValue: "src/css/images/widgets/tizen_image.svg",
+ htmlSelector: "img",
+ htmlAttribute: "src",
+ forceAttribute: true
+ },
+ },
+ template: function(node) {
+ var prop, iconsrc,
+ code = BWidgetRegistry.ButtonListItem.template(node);
+ prop = node.getProperty("iconsrc");
+ // Add the icon if iconsrc property is not blank
+ if (prop.trim() != '') {
+ iconsrc = $('
')
+ .attr('width','16')
+ .attr('class', 'ui-li-icon');
+ code.find('a').append(iconsrc);
+ };
+ return code;
+ }
+ },
+
+ /**
+ * Represents a ThumbnailListItem element.
+ */
+ ThumbnailListItem: {
+ parent: "ClickableListItem",
+ allowIn: [ "ThumbnailList"],
+ zones: [
+ {
+ name: "left",
+ cardinality: "1",
+ locator: "a",
+ allow: [ "Image" ]
+ },
+ {
+ name: "right",
+ cardinality: "N",
+ locator: "a",
+ allow: [ "Text" ]
+ }
+ ],
+ init: function (node) {
+ var image = ADM.createNode("Image");
+ var text = ADM.createNode("Text");
+ text.setProperty("type", "h3");
+ text.setProperty("text", "Thumbnail List Item");
+ node.addChild(text);
+ text = ADM.createNode("Text");
+ text.setProperty("type", "p");
+ text.setProperty("text", "Thumbnail List Item");
+ node.addChild(text);
+ node.addChild(image);
+ }
+ },
+
+ /**
+ * Represents a SplitListItem element.
+ */
+ ThumbnailSplitListItem: {
+ parent: "SplitListItemBase",
+ allowIn: [ "ThumbnailSplitList" ],
+ zones: [
+ {
+ name: "left",
+ cardinality: "1",
+ locator: "a",
+ allow: [ "Image" ]
+ },
+ {
+ name: "right",
+ cardinality: "N",
+ locator: "a",
+ allow: [ "Text" ]
+ }
+ ],
+ init: function (node) {
+ // initial state is three Image and Texts
+ var image = ADM.createNode("Image");
+ var text = ADM.createNode("Text");
+ text.setProperty("type", "h3");
+ text.setProperty("text", "Thumbnail List Item");
+ node.addChild(text);
+ text = ADM.createNode("Text");
+ text.setProperty("type", "p");
+ text.setProperty("text", "Thumbnail List Item");
+ node.addChild(text);
+ node.addChild(image);
+
+ node.addChild(ADM.createNode("ListButton"));
+ }
+ },
+
+ /**
+ * Represents a SplitListItem element.
+ */
+ SplitListItemBase: {
+ parent: "ClickableListItem",
+ zones: [
+ {
+ name: "extra",
+ cardinality: {min: "1", max: "1"},
+ allow: [ "ListButton" ]
+ },
+ ],
+ init: function (node) {
+ // initial state is three buttons
+ node.addChild(ADM.createNode("ListButton"));
+ }
+ },
+
+ /**
+ * Represents a SplitListItem element.
+ */
+ ButtonSplitListItem: {
+ parent: [ "ButtonListItem", "SplitListItemBase" ],
+ allowIn: [ "ButtonSplitList" ],
+ },
+
+ /**
+ * Represents a SplitListItem element.
+ */
+ IconSplitListItem: {
+ parent: [ "IconListItem", "ButtonSplitListItem" ],
+ allowIn: [ "IconSplitList" ],
+ },
+
+ /**
+ * Represents a SplitListItem element.
+ */
+ TextSplitListItem: {
+ parent: [ "TextListItem", "SplitListItemBase" ],
+ allowIn: [ "TextSplitList" ],
+ init: function (node) {
+ BWidgetRegistry.TextListItem.init(node);
+ BWidgetRegistry.SplitListItemBase.init(node);
+ }
+ },
+
+ /**
+ * Represents a list divider element.
+ */
+ ListDivider: {
+ parent: "SimpleListItem",
+ displayLabel: "List Divider",
+ paletteImageName: "jqm_list_divider.svg",
+ allowIn: [ "ListBase" ],
+ properties: {
+ text: {
defaultValue: "List Divider"
},
- theme: BCommonProperties.theme
},
template: '- %TEXT%
'
},
@@ -1678,52 +2052,16 @@ var BWidgetRegistry = {
* Represents a button. A "text" string property holds the button text.
*/
ListButton: {
- parent: "Base",
- displayLabel: "List Button",
- paletteImageName: "jqm_list_button.svg",
- allowIn: [ "List", "OrderedList" ],
- editable: {
- selector: "a",
- propertyName: "text"
- },
+ parent: "ButtonBase",
+ allowIn: [ "SplitListItemBase"],
properties: {
- text: {
- type: "string",
- defaultValue: "Button"
- },
- target: {
- type: "string",
- defaultValue: "",
- htmlAttribute: "href",
- htmlSelector: "a"
- },
icon: $.extend({}, BCommonProperties.icon, {
options: BCommonProperties.icon.options.slice(1),
defaultValue: "arrow-r"
}),
theme: BCommonProperties.theme,
- countbubble: {
- type: "string",
- displayName: "count bubble",
- defaultValue: ""
- }
},
- template: function (node) {
- var prop, countBubble, code = $('- %TEXT%
');
- var anchor = code.find('a');
-
- prop = node.getProperty("countbubble");
- // Add the count bubble if countbubble property is not blank
- if (prop.trim() != '') {
- countBubble = $('')
- .attr('class', 'ui-li-count')
- .html(prop);
- anchor.append(countBubble);
- };
-
- return code;
- }
-
+ template: '',
},
/**
@@ -1754,18 +2092,12 @@ var BWidgetRegistry = {
rows: {
type: "integer",
defaultValue: 1,
+ range: "1-100",
setPropertyHook: function (node, value, transactionData) {
var rows, columns, i, block, map, children, blocks, count,
blockIndex, root;
rows = node.getProperty("rows");
columns = node.getProperty("columns");
-
- // FIXME: really this should be enforced in the property
- // pane, or elsewhere; this won't really work
- if (value < 1) {
- value = 1;
- }
-
root = node.getDesign();
root.suppressEvents(true);
@@ -1819,8 +2151,8 @@ var BWidgetRegistry = {
},
columns: {
type: "integer",
- options: [ 2, 3, 4, 5 ],
defaultValue: 2,
+ range: "2-5",
setPropertyHook: function (node, value, transactionData) {
var rows, columns, i, block, map, children, blocks, count,
index, blockIndex, root;
@@ -2164,7 +2496,7 @@ var BWidget = {
init: function () {
// effects: add the type and displayLabel properties to widget
// registry objects
- var type, parentName;
+ var type, newZones, descendantZone, descendantZoneIndex;
for (type in BWidgetRegistry) {
if (BWidgetRegistry.hasOwnProperty(type)) {
BWidgetRegistry[type].type = type;
@@ -2200,11 +2532,29 @@ var BWidget = {
if (type === "OptionHeader") {
BWidgetRegistry[type].displayLabel = "Option Header";
}
- parentName = BWidgetRegistry[type].parent;
- while (parentName) {
+ $.each(BWidget.getAncestors(type), function (i, parentName) {
+ if (BWidgetRegistry[parentName].zones) {
+ newZones = [];
+ $.each(BWidgetRegistry[parentName].zones, function (i, pZone) {
+ descendantZone = null;
+ if (BWidgetRegistry[type].zones)
+ $.each(BWidgetRegistry[type].zones, function (i, zone) {
+ if (pZone.name === zone.name) {
+ descendantZone = zone;
+ descendantZoneIndex = i;
+ return false;
+ }
+ });
+ if (descendantZone)
+ BWidgetRegistry[type].zones[descendantZoneIndex] = $.extend(true, true, {}, pZone, descendantZone);
+ else
+ newZones.push(pZone);
+ });
+ if(BWidgetRegistry[type].zones)
+ $.merge(BWidgetRegistry[type].zones, newZones);
+ }
BWidgetRegistry[type] = $.extend(true, true, {}, BWidgetRegistry[parentName], BWidgetRegistry[type]);
- parentName = BWidgetRegistry[parentName].parent;
- }
+ });
}
}
},
@@ -2560,6 +2910,21 @@ var BWidget = {
return schema;
},
+ /**
+ * Gets the range for a given property.
+ *
+ * @param {String} widgetType The type of the widget.
+ * @param {String} property The name of the requested property.
+ * @return {String} The range for the given property
+ */
+ getPropertyRange: function (widgetType, property) {
+ var schema = BWidget.getPropertySchema(widgetType, property);
+ if (schema) {
+ return schema.range;
+ }
+ return schema;
+ },
+
/**
* Applies any value mapping on the given value that would occur during
* serialization if this value were found in the given widget type and
@@ -2621,7 +2986,8 @@ var BWidget = {
getPropertyHTMLSelector: function (widgetType, property) {
var schema = BWidget.getPropertySchema(widgetType, property);
if (schema) {
- return schema.htmlSelector;
+ return schema.htmlSelector !== undefined ? schema.htmlSelector :
+ BWidget.getWidgetAttribute(widgetType, "defaultHtmlSelector");
}
return schema;
},
@@ -2920,21 +3286,39 @@ var BWidget = {
zoneName);
},
+ // helper function
+ getAncestors: function (type) {
+ var ancestors = [],
+ parents = BWidgetRegistry[type].parent;
+ if (!parents)
+ return [];
+ if ( typeof parents === "string")
+ parents = [parents];
+ ancestors = $.merge(ancestors, parents);
+ $.each(parents, function (i, parentName) {
+ $.merge(ancestors, BWidget.getAncestors(parentName));
+ })
+ return ancestors;
+ },
// helper function
isTypeInList: function (type, list) {
// requires: list can be an array, a string, or invalid
// returns: true, if type is the list string, or type is one of the
// strings in list
// false, otherwise, or if list is invalid
- var i;
+ var i, parentType = type,
+ isType = function (srcType, targetType) {
+ if (srcType === targetType)
+ return true;
+ return BWidget.getAncestors(srcType).indexOf(targetType) !== -1;
+ };
if (list) {
- if (type === list) {
+ if (isType(type, list)) {
return true;
} else if (list.length > 0) {
for (i = list.length - 1; i >= 0; i--) {
- if (type === list[i]) {
+ if (isType(type, list[i]))
return true;
- }
}
}
}
@@ -2983,7 +3367,7 @@ var BWidget = {
* found.
*/
zoneAllowsChild: function (parentType, zone, childType) {
- var parent, child, zones, i, allow, deny;
+ var parent, child, zones, i, allow, deny, morph;
parent = BWidgetRegistry[parentType];
child = BWidgetRegistry[childType];
if ((typeof parent !== "object") || (typeof child !== "object")) {
@@ -2994,8 +3378,13 @@ var BWidget = {
if (zones && zones.length > 0) {
for (i = zones.length - 1; i >= 0; i--) {
if (zones[i].name === zone) {
+ morph = BWidget.getZone(parentType, zone).morph;
+ if (morph)
+ childType = morph(childType, parentType);
allow = zones[i].allow;
if (allow) {
+ if (typeof allow === "function")
+ allow = allow(parentType);
return BWidget.isTypeInList(childType, allow);
}
deny = zones[i].deny;