      1 /*
      2  *  Copyright (C) 2008-2009 WaveMaker Software, Inc.
      3  *
      4  *  This file is part of the WaveMaker Client Runtime.
      5  *
      6  *  Licensed under the Apache License, Version 2.0 (the "License");
      7  *  you may not use this file except in compliance with the License.
      8  *  You may obtain a copy of the License at
      9  *
     10  *      http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  *  Unless required by applicable law or agreed to in writing, software
     13  *  distributed under the License is distributed on an "AS IS" BASIS,
     14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  *  See the License for the specific language governing permissions and
     16  *  limitations under the License.
     17  */
     18 dojo.provide("wm.base.components.LiveView");
     19 dojo.require("wm.base.Component");
     20 dojo.require('wm.base.lib.data');
     21
     22 // View related utitlities
     23 wm.getViewField = function(inTypeSchema, inPropName) {
     24 	if (inTypeSchema) {
     25 		var propInfo = wm.typeManager.getPropertyInfoFromSchema(inTypeSchema, inPropName);
     26 		return {
     27 			caption: wm.capitalize(inPropName.split(".").pop()),
     28 			sortable: true,
     29 			dataIndex: inPropName,
     30 			type: propInfo.type,
     31 			displayType: wm.getPrimitiveDisplayType(propInfo.type),
     32 			required: propInfo.required,
     33 			readonly: dojo.indexOf(propInfo.noChange, "read") >= 0,
     34 			includeLists: true,
     35 			includeForms: true
     36 		};
     37 	}
     38 }
     39
     40 wm.getDefaultView = function(inTypeName, inPropertyPath) {
     41 	inPropertyPath = inPropertyPath || "";
     42 	var
     43 		v = [], tm = wm.typeManager,
     44 		schema = tm.getTypeSchema(inTypeName),
     45 		propSchema = inPropertyPath ? tm.getTypeSchema(tm.getPropertyInfoFromSchema(schema, inPropertyPath).type) : schema,
     46 		fields = wm.typeManager.getSimplePropNames(propSchema);
     47 	wm.forEach(fields, function(f) {
     48 		v.push(wm.getViewField(schema, (inPropertyPath ? inPropertyPath + "." : "") + f));
     49 	});
     50 	return v;
     51 }
     52
     53 /**
     54 	Component that provides information about live service, datatype,
     55 	related objects, and field information.
     56 	@name wm.LiveView
     57 	@class
     58 	@extends wm.Component
     59 */
     60 dojo.declare("wm.LiveView", wm.Component, {
     61 	/** @lends wm.LiveView.prototype */
     62 	/** Name of the service on which this view operates. */
     63 	service: "",
     64 	/** Fully qualified data type we operate on. */
     65 	dataType: "",
     66 	/** Fields to fetch */
     67 	related: [],
     68 	/** Fields to display */
     69 	view: [],
     70 	constructor: function() {
     71 		this.related = [];
     72 		this.view = [];
     73 	},
     74 	init: function() {
     75 		this.inherited(arguments);
     76 		this.setDataType(this.dataType);
     77 	},
     78 	loaded: function() {
     79 		this.inherited(arguments);
     80 		this.viewChanged();
     81 	},
     82 	viewChanged: function() {
     83 		dojo.publish(this.getRuntimeId() + "-viewChanged", [this.getId()]);
     84 	},
     85 	createDefaultView: function() {
     86 		this.setFields(this.related || [], wm.getDefaultView(this.dataType));
     87 	},
     88 	setFields: function(inRelated, inView) {
     89 		this.related = inRelated;
     90 		this._sortView(inView);
     91 		this.view = inView;
     92 	},
     93 	getFieldIndex: function(inField) {
     94 		var di = dojo.isObject(inField) ? inField.dataIndex : inField;
     95 		for (var i=0, view=this.view, f; f=view[i]; i++)
     96 			if (f.dataIndex == di)
     97 				return i;
     98 		return -1;
     99 	},
    100 	hasField: function(inField) {
    101 		return (this.getFieldIndex(inField) > -1);
    102 	},
    103 	getRelatedIndex: function(inRelated) {
    104 		for (var i=0, related=this.related, r; r=related[i]; i++)
    105 			if (r == inRelated)
    106 				return i;
    107 		return -1;
    108 	},
    109 	hasRelated: function(inRelated) {
    110 		return (this.getRelatedIndex(inRelated) > -1);
    111 	},
    112 	addField: function(inField) {
    113 		var f = inField && wm.getViewField(wm.typeManager.getTypeSchema(this.dataType), inField);
    114 		if (f && !this.hasField(f)) {
    115 			this.view.push(f)
    116 			this._sortView(this.view);
    117 		}
    118 		return f;
    119 	},
    120 	removeField: function(inField) {
    121 		var i = this.getFieldIndex(inField);
    122 		if (i > -1)
    123 			this.view.splice(i, 1);
    124 	},
    125 	addRelated: function(inRelated) {
    126 		if (inRelated && !this.hasRelated(inRelated)) {
    127 			this.related.push(inRelated);
    128 			this.addRelatedDefaultView(inRelated);
    129 		}
    130 	},
    131 	removeRelated: function(inRelated) {
    132 		var i = this.getRelatedIndex(inRelated);
    133 		if (i > -1)
    134 			this.related.splice(i, 1);
    135 	},
    136 	addRelatedDefaultView: function(inRelated) {
    137 		var relatedFields = wm.getDefaultView(this.dataType, inRelated);
    138 		dojo.forEach(relatedFields, function(f) {
    139 			if (!this.hasField(f))
    140 				this.view.push(f);
    141 		}, this);
    142 		this._sortView();
    143 	},
    144
    145 	_sortView: function(inView) {
    146 		if (dojo.isArray(inView)) {
    147 			var t = this.dataType;
    148 			// sort view by order or alpha place property chain
    149 			inView.sort(function(a, b) {
    150 				// if either has order, compare by order
    151 				if (wm.isNumber(a.order) || wm.isNumber(b.order)) {
    152 					return wm.compareNumbers(a.order, b.order);
    153 				// otherwise compare by "shallowest" or alpha
    154 				} else {
    155 					a = a.dataIndex;
    156 					b = b.dataIndex;
    157 					var al = a.split(".").length, bl = b.split(".").length;
    158 					return al == bl ? wm.data.compare(a, b) : wm.compareNumbers(al, bl);
    159 				}
    160 			});
    161 		}
    162 	},
    163 	_copyView: function(inView) {
    164 		var view = [];
    165 		for (var i=0, v; (v=inView[i]); i++)
    166 			view.push(dojo.mixin({}, v));
    167 		return view;
    168 	},
    169 	getViewById: function(inLiveViewId) {
    170 		if (inLiveViewId instanceof wm.LiveView)
    171 			return inLiveViewId;
    172 		else if (inLiveViewId)
    173 			return this.getRoot().app.getValueById(inLiveViewId);
    174 	},
    175 	copyLiveView: function(inLiveView) {
    176 		var lv = this.getViewById(inLiveView);
    177 		if (lv) {
    178 			this.setService(lv.service);
    179 			this.setDataType(lv.dataType);
    180 			var v = this._copyView(lv.view);
    181 			this.setFields(lv.related, v);
    182 		} else
    183 			this.clearView();
    184 	},
    185 	clearView: function() {
    186 		this.setService("");
    187 		this.setDataType("");
    188 		this.setFields([], []);
    189 	},
    190 	setService: function(inService) {
    191 		this.service = inService;
    192 	},
    193 	//$ Set the dataType for the dataView. This is a type that supports crud operations.
    194 	setDataType: function(inType) {
    195 		var t = this.dataType;
    196 		this.dataType = inType;
    197 		if (t != this.dataType)
    198 			this.dataTypeChanged();
    199 		if (this._defaultView)
    200 			this.createDefaultView();
    201 	},
    202 	dataTypeChanged: function() {
    203 		// FIXME: we need to do something smart here. changing the datatype should probably zot
    204 		// the view info and may need to inform things bound to this and/or update.
    205 		this.related = [];
    206 		this.view = [];
    207 	},
    208 	hasRelatedProp: function(inRelatedProp) {
    209 		for (var i=0, related=this.related, r; (r=related[i]); i++)
    210 			if (r == inRelatedProp)
    211 				return true;
    212 	},
    213 	getListView: function(inPropPath) {
    214 		var schema = wm.typeManager.getTypeSchema(this.getSubType(inPropPath));
    215 		return dojo.filter(this.getSubView(inPropPath), function(v) {
    216 			return !wm.typeManager.isPropInList(schema, v.dataIndex);
    217 		})
    218 	},
    219 	// get the type of a property path from our dataType
    220 	getSubType: function(inPropPath) {
    221 		if (inPropPath) {
    222 			var schema = wm.typeManager.getTypeSchema(this.dataType);
    223 			return (schema && (wm.typeManager.getPropertyInfoFromSchema(schema, inPropPath) || 0).type) || this.dataType;
    224 		} else
    225 			return this.dataType;
    226 	},
    227 	// get a related list starting at inPropPath
    228 	getSubRelated: function(inPropPath) {
    229 		inPropPath = inPropPath ? inPropPath + "." : "";
    230 		if (inPropPath) {
    231 			var list = [], l = inPropPath.length;
    232 			dojo.forEach(this.related, function(r) {
    233 				if (r.indexOf(inPropPath) == 0)
    234 					list.push(r.substring(l));
    235 			});
    236 			return list;
    237 		} else
    238 			return this.related;
    239 	},
    240 	// get a view starting at inPropPath
    241 	getSubView: function(inPropPath) {
    242 		inPropPath = inPropPath ? inPropPath + "." : "";
    243 		var view = this._copyView(this.view);
    244 		if (inPropPath) {
    245 			var list = [], l = inPropPath.length;
    246 			dojo.forEach(view, function(v) {
    247 				if (v.dataIndex.indexOf(inPropPath) == 0) {
    248 					v.dataIndex = v.dataIndex.substring(l);
    249 					list.push(v);
    250 				}
    251 			});
    252 			return list;
    253 		} else
    254 			return view;
    255 	}
    256 });
    257
    258 wm.Object.extendSchema(wm.LiveView, {
    259 	related: { ignore: 1, writeonly: 1 },
    260 	view: { ignore: 1, writeonly: 1 },
    261 	service: { ignore: 1, writeonly: 1 },
    262 	dataType: {ignore: 1, writeonly: 1}
    263 });
    264