<commit_before>'use strict';

var memoize               = require('memoizee/plain')
  , validDb               = require('dbjs/valid-dbjs')
  , defineStringLine      = require('dbjs-ext/string/string-line')
  , defineFormSectionBase = require('./form-section-base');

module.exports = memoize(function (db) {
	var StringLine, FormSectionBase;
	validDb(db);
	StringLine      = defineStringLine(db);
	FormSectionBase = defineFormSectionBase(db);
	FormSectionBase.extend('FormSection', {
		formPropertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var props, resolved;
			props = this.constructor.propertyNames.copy();
			props.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getFormApplicablePropName(resolved.key)])
						=== false) {
					props.delete(name);
				}
			}, this);
			return props;
		} },
		propertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var props, resolved;
			props = this.formPropertyNames.copy();
			props.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getApplicablePropName(resolved.key)])
						=== false) {
					props.delete(name);
				}
			}, this);

			return props;
		} },
		status: { value: function (_observe) {
			var resolved, valid = 0, total = 0, isResolventExcluded, isOwn;
			if (this.constructor.resolventProperty) {
				resolved = this.master.resolveSKeyPath(this.constructor.resolventProperty, _observe);
				if (!resolved) {
					return total;
				}
				isResolventExcluded = this.isPropertyExcludedFromStatus(resolved, _observe);
				if (_observe(resolved.observable) !== _observe(this.resolventValue)) {
					if (isResolventExcluded) return 1;
					if (resolved.descriptor.multiple) {
						if (_observe(resolved.observable).size) return 1;
					} else {
						if (_observe(resolved.observable) != null) return 1;
					}
					return 0;
				}
				if (!isResolventExcluded) {
					++total;
					++valid;
				}
			}

			this.propertyNames.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					++total;
					return;
				}
				if (this.isPropertyExcludedFromStatus(resolved, _observe)) {
					return;
				}
				++total;
				if (resolved.descriptor.requireOwn) {
					_observe(resolved.observable);
					if (resolved.descriptor.multiple) {
						isOwn = typeof resolved.descriptor._value_ !== 'function';
					} else {
						isOwn = resolved.descriptor._hasOwnValue_(resolved.object);
					}
					if (!isOwn) return;
				}
				if (resolved.descriptor.multiple && !_observe(resolved.observable).size) {
					return;
				}
				if (_observe(resolved.observable) != null) valid++;
			}, this);
			return total === 0 ? 1 : valid / total;
		} },
		weight: { value: function (_observe) {
			var resolved, total = 0, isResolventExcluded;
			if (this.constructor.resolventProperty) {
				resolved = this.master.resolveSKeyPath(this.constructor.resolventProperty, _observe);
				if (!resolved) {
					return 0;
				}
				isResolventExcluded = this.isPropertyExcludedFromStatus(resolved, _observe);
				if (_observe(resolved.observable) !== _observe(this.resolventValue)) {
					return isResolventExcluded ? 0 : 1;
				}
				if (!isResolventExcluded) {
					++total;
				}
			}
			this.propertyNames.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					++total;
					return;
				}
				if (this.isPropertyExcludedFromStatus(resolved, _observe)) {
					return;
				}
				++total;
			}, this);
			return total;
		} },
		inputOptions: {
			type: db.Object,
			nested: true
		},
		lastEditDate: {
			value: function (_observe) {
				var res = 0, resolved, lastModified;
				this.constructor.propertyNames.forEach(function (name) {
					resolved = this.master.resolveSKeyPath(name, _observe);
					if (!resolved) {
						return;
					}
					lastModified = _observe(resolved.object['_' + name]._lastModified);
					if (lastModified > res) res = lastModified;
				}, this);

				return res / 1000;
			}
		}
	}, {
		propertyNames: { type: StringLine, multiple: true }
	});
	db.FormSection.prototype.inputOptions._descriptorPrototype_.nested = true;
	db.FormSection.prototype.inputOptions._descriptorPrototype_.type   = db.Object;

	return db.FormSection;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<commit_msg>#570 fixed lastEditDate<commit_after>'use strict';

var memoize               = require('memoizee/plain')
  , validDb               = require('dbjs/valid-dbjs')
  , defineStringLine      = require('dbjs-ext/string/string-line')
  , defineFormSectionBase = require('./form-section-base');

module.exports = memoize(function (db) {
	var StringLine, FormSectionBase;
	validDb(db);
	StringLine      = defineStringLine(db);
	FormSectionBase = defineFormSectionBase(db);
	FormSectionBase.extend('FormSection', {
		formPropertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var props, resolved;
			props = this.constructor.propertyNames.copy();
			props.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getFormApplicablePropName(resolved.key)])
						=== false) {
					props.delete(name);
				}
			}, this);
			return props;
		} },
		propertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var props, resolved;
			props = this.formPropertyNames.copy();
			props.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getApplicablePropName(resolved.key)])
						=== false) {
					props.delete(name);
				}
			}, this);

			return props;
		} },
		status: { value: function (_observe) {
			var resolved, valid = 0, total = 0, isResolventExcluded, isOwn;
			if (this.constructor.resolventProperty) {
				resolved = this.master.resolveSKeyPath(this.constructor.resolventProperty, _observe);
				if (!resolved) {
					return total;
				}
				isResolventExcluded = this.isPropertyExcludedFromStatus(resolved, _observe);
				if (_observe(resolved.observable) !== _observe(this.resolventValue)) {
					if (isResolventExcluded) return 1;
					if (resolved.descriptor.multiple) {
						if (_observe(resolved.observable).size) return 1;
					} else {
						if (_observe(resolved.observable) != null) return 1;
					}
					return 0;
				}
				if (!isResolventExcluded) {
					++total;
					++valid;
				}
			}

			this.propertyNames.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					++total;
					return;
				}
				if (this.isPropertyExcludedFromStatus(resolved, _observe)) {
					return;
				}
				++total;
				if (resolved.descriptor.requireOwn) {
					_observe(resolved.observable);
					if (resolved.descriptor.multiple) {
						isOwn = typeof resolved.descriptor._value_ !== 'function';
					} else {
						isOwn = resolved.descriptor._hasOwnValue_(resolved.object);
					}
					if (!isOwn) return;
				}
				if (resolved.descriptor.multiple && !_observe(resolved.observable).size) {
					return;
				}
				if (_observe(resolved.observable) != null) valid++;
			}, this);
			return total === 0 ? 1 : valid / total;
		} },
		weight: { value: function (_observe) {
			var resolved, total = 0, isResolventExcluded;
			if (this.constructor.resolventProperty) {
				resolved = this.master.resolveSKeyPath(this.constructor.resolventProperty, _observe);
				if (!resolved) {
					return 0;
				}
				isResolventExcluded = this.isPropertyExcludedFromStatus(resolved, _observe);
				if (_observe(resolved.observable) !== _observe(this.resolventValue)) {
					return isResolventExcluded ? 0 : 1;
				}
				if (!isResolventExcluded) {
					++total;
				}
			}
			this.propertyNames.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					++total;
					return;
				}
				if (this.isPropertyExcludedFromStatus(resolved, _observe)) {
					return;
				}
				++total;
			}, this);
			return total;
		} },
		inputOptions: {
			type: db.Object,
			nested: true
		},
		lastEditDate: {
			value: function (_observe) {
				var res = 0, resolved, lastModified;
				this.constructor.propertyNames.forEach(function (name) {
					resolved = this.master.resolveSKeyPath(name, _observe);
					if (!resolved) {
						return;
					}
					lastModified = _observe(resolved.object['_' + resolved.key]._lastModified);
					if (lastModified > res) res = lastModified;
				}, this);

				return res / 1000;
			}
		}
	}, {
		propertyNames: { type: StringLine, multiple: true }
	});
	db.FormSection.prototype.inputOptions._descriptorPrototype_.nested = true;
	db.FormSection.prototype.inputOptions._descriptorPrototype_.type   = db.Object;

	return db.FormSection;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<|endoftext|>


<commit_before>(function (ko, io, _, util) {
    'use strict';

    var viewmodel = util.namespace('kpp.viewmodel'),
        model = util.namespace('kpp.model'),
        stageTypeKeys = model.stageTypeKeys,
        stages = model.stageTypes,
        DraggableIssueList = viewmodel.DraggableIssueList;

    viewmodel.Kanban = viewmodel.Kanban || Kanban;

    function Kanban(o) {
        var that = this;

        that.members = null;

        that.issues = null;

        that.project = null;

        // that.stages[name] = 各ステージのIssues
        that.stages = null;

        // that.draggableList[stage] = DraggableIssueList
        // that.draggableList[stage][UserID] = DraggableIssueList
        that.draggableList = {};

        // 追加するユーザの名前
        that.addMemberUserName = ko.observable();

        // 追加するIssueのタイトル
        that.addIssueTitle = ko.observable();

        // 追加するIssueの説明
        that.addIssueBody = ko.observable();

        // アサイン先のユーザ名
        that.assignUserName = ko.observable();

        // 選択しているIssue
        that.selectedIssue = ko.observable();

        // Issueの更新後のタイトル
        that.updateIssueDetailTitle = ko.observable();

        // Issueの更新後のBody
        that.updateIssueDetailBody = ko.observable();

        that.selectedIssue.subscribe(function (issue) {
            that.updateIssueDetailTitle(issue ? issue.title() : null);
            that.updateIssueDetailBody(issue ? issue.body() : null);
        });

        // 選択しているメンバー
        that.selectedMember = ko.observable();

        // 編集用の仮のWIP制限
        that.settingsWipLimit = ko.observable();

        // IssueListが表示されているか
        that.openedIssueList = ko.observable(false);

        // IssueListの表示/非表示の切り替え
        that.toggleOpenedIssueList = function () {
            that.openedIssueList(!that.openedIssueList());
        };

        that.selectedMember.subscribe(function (member) {
            if (member) {
                that.settingsWipLimit(member.wipLimit());
            }
        });

        // アサイン可能なメンバー
        that.canAssignMembers = ko.computed(function () {
            return that.members().filter(function (member) {
                return !member.isWipLimited();
            });
        }, that, {deferEvaluation: true});

        that.init = function (project) {
            that.project = project;
            that.members = project.members;
            that.issues = project.issues;
            that.stages = project.stages;
            that.initDraggableIssueList();

            initSocket();
        };

        // 各ステージ、各メンバー毎にDraggableIssueListを作る
        // メンバーの追加を監視する
        that.initDraggableIssueList = function () {
            that.draggableList = {};
            var _params = {
                masterIssues: that.issues,
                onUpdateStage: that.updateStage,
                onUpdatePriority: that.updateIssuePriority
            };
            _.values(stages).forEach(function (stage) {
                var params = _.extend(_params, {stage: stage.name}),
                    list;

                if (stage.assigned) {
                    list = {};
                    that.members().forEach(function (member) {
                        list[member._id()] = new DraggableIssueList(_.extend(params, {assignee: member._id()}));
                    });
                    that.members.subscribe(function (member) {
                        if (!list[member._id()]) {
                            list[member._id()] = new DraggableIssueList(_.extend(params, {assignee: member._id()}));
                        }
                    });
                } else {
                    list = new DraggableIssueList(params);
                }

                that.draggableList[stage.name] = list;
            });
        };

        // メンバーを追加する
        that.addMember = function () {
            that.socket.emit('add-member', {userName: that.addMemberUserName()}, function (res) {
                if (res.status === 'success') {
                    // reset form
                    that.addMemberUserName(null);
                }
            });
        };

        // メンバーを削除する
        that.removeMember = function () {
            var member = that.selectedMember();
            if (!member) {
                console.error('unselected member');
                return;
            }

            that.socket.emit('remove-member', {userName: member.userName()}, function (res) {
                if (res.status === 'success') {
                    // モーダルを閉じる
                    $('.modal').modal('hide');
                }
            });
        };

        // メンバー設定を更新する
        that.updateMember = function () {
            var member = that.selectedMember();
            if (!member) { return console.error('unselected member'); }

            that.socket.emit('update-member', {userName: member.userName(), wipLimit: that.settingsWipLimit()});
        };

        that.updateMemberOrderUp = function (member) {
            var members = that.members();
            var idx = members.indexOf(member);
            if (idx === 0) { return console.log(member.userName() + ' is already top'); }
            that.updateMemberOrder(member, members[idx - 1]);
        };

        that.updateMemberOrderDown = function (member) {
            var members = that.members();
            var idx = members.indexOf(member);
            if (idx === members.length - 1) { return console.log(member.userName() + ' is already bottom'); }
            that.updateMemberOrder(member, (idx + 2) === members.length ? null : members[idx + 2]);
        };

        // タスクの優先順位を変更する
        that.updateMemberOrder = function (member, insertBeforeOfMember) {
            var insertBeforeOfUserName =  insertBeforeOfMember ? insertBeforeOfMember.userName() : null;
            that.socket.emit('update-member-order', {userName: member.userName(), insertBeforeOfUserName: insertBeforeOfUserName});
        };

        // Issueと追加する
        that.addIssue = function () {
            var title = that.addIssueTitle(),
                body = that.addIssueBody();

            that.socket.emit('add-issue', {title: title, body: body}, function (res) {
                if (res.status === 'success') {
                    // reset form
                    that.addIssueTitle(null);
                    that.addIssueBody(null);
                }
            });
        };

        // Issueを削除する (archive)
        that.removeIssue = function (issue) {
            that.socket.emit('remove-issue', {issueId: issue._id()}, _.noop);
        };

        // タスクをアサインする
        // ユーザが指定されていない場合はアンアサインする
        that.assignIssue = function () {
            var issue = that.selectedIssue(),
                user = that.project.getMemberByName(that.assignUserName());

            if (!issue) { throw new Error('issue is not selected.'); }

            that.socket.emit('update-stage', {
                issueId: issue._id(),
                userId: user ? user._id() : null,
                toStage: user ? stages.todo.name : stages.backlog.name
            }, function () {
                // reset form
                that.selectedIssue(null);
                that.assignUserName(null);
            });
        };

        // タスクのステージを一つ次へ移動する
        that.nextStage = function (issue) {
            that.stepStage(issue, 1);
        };

        // タスクのステージを一つ前へ移動する
        that.prevStage = function (issue) {
            that.stepStage(issue, -1);
        };

        // タスクのステージをstep分移動する
        that.stepStage = function (issue, step) {
            var currentStage = issue.stage(),
                toIndex = stageTypeKeys.indexOf(currentStage) + step,
                toStage;

            if (!_.inRange(toIndex, 0, stageTypeKeys.length)) {
                throw new Error('cannot change stage: ' +  toIndex);
            }

            toStage = stageTypeKeys[toIndex];

            // check assign
            // 担当者が必要なステージから必要ないステージへ移行した際には
            // 担当者を外す
            var currentStageDetail = stages[currentStage];
            var toStageDetail = stages[toStage];
            var assign = undefined;
            if (currentStageDetail.assigned && !toStageDetail.assigned) {
                assign = null; // unassign
            }

            that.updateStage(issue, toStage, assign);
        };

        // タスクのステージを変更する
        that.updateStage = function (issue, toStage, /* option */userId) {
            that.socket.emit('update-stage', {issueId: issue._id(), toStage: toStage, userId: userId !== undefined ? userId : issue.assignee()});
        };

        // タスクのタイトル/説明を更新する
        that.updateIssueDetail = function () {
            var issue = that.selectedIssue();
            if (!issue) { throw new Error('issue is not selected'); }

            that.socket.emit('update-issue-detail', {
                issueId: issue._id(),
                title: that.updateIssueDetailTitle(),
                body: that.updateIssueDetailBody()
            }, function (res) {
                if (res.status === 'success') {
                    // reset form
                    that.selectedIssue(null);
                }
            });
        };

        // タスクの優先順位を変更する
        that.updateIssuePriority = function (issue, insertBeforeOfIssue) {
            var insertBeforeOfIssueId =  insertBeforeOfIssue ? insertBeforeOfIssue._id() : null;
            that.socket.emit('update-issue-priority', {issueId: issue._id(), insertBeforeOfIssueId: insertBeforeOfIssueId});
        };

        // events
        that.onClickIssueDetail = function (issue, e) {
            that.selectedIssue(issue);
            $($(e.target.parentElement).attr('data-target')).modal('show');
            return util.cancelBubble(e);
        };

        that.onClickIssueNextStage = function (issue, e) {
            that.nextStage(issue);
            return util.cancelBubble(e);
        };

        that.onClickIssuePrevStage = function (issue, e) {
            that.prevStage(issue);
            return util.cancelBubble(e);
        };

        that.onClickIssueRemove = function (issue, e) {
            that.removeIssue(issue);
            return util.cancelBubble(e);
        };

        that.onClickIssueAssign = function (issue, e) {
            that.selectedIssue(issue);
            $($(e.target.parentElement).attr('data-target')).modal('show');
            return util.cancelBubble(e);
        };

        // ソケット通信のイベント設定、デバッグ設定を初期化する
        function initSocket () {
            that.socket = new model.Socket();

            that.socket.on('connect', function () {
                that.socket.emit('join-project-room', {projectId: that.project.id()});
            });

            that.socket.on('add-member', function (req) {
                that.project.addMember(req.member);
            });

            that.socket.on('remove-member', function (req) {
                var targetMember = that.project.getMember(req.member.user._id);
                that.project.removeMember(targetMember);
            });

            that.socket.on('update-member', function (req) {
                var targetMember = that.project.getMember(req.member.user._id);
                that.project.updateMember(targetMember, req.member);
            });

            that.socket.on('update-member-order', function (req) {
                that.project.updateMemberOrder(req.userName, req.insertBeforeOfUserName);
            });

            that.socket.on('add-issue', function (req) {
                that.project.addIssue(req.issue);
            });

            that.socket.on('remove-issue', function (req) {
                var targetIssue = that.project.getIssue(req.issue._id);
                that.project.removeIssue(targetIssue);
            });

            that.socket.on('update-stage', function (req) {
                that.project.updateStage(req.issueId, req.toStage, req.assignee);
            });

            that.socket.on('update-issue-detail', function (req) {
                var targetIssue = that.project.getIssue(req.issue._id);

                ['title', 'body'].forEach(function (key) {
                    targetIssue[key](req.issue[key]);
                });
            });

            that.socket.on('update-issue-priority', function (req) {
                that.project.updateIssuePriority(req.issue._id, req.insertBeforeOfIssueId);
            });

            that.socket.initSocketDebugMode();
        }
    }

}(ko, io, _, window.nakazawa.util));<commit_msg>member addの不具合修正<commit_after>(function (ko, io, _, util) {
    'use strict';

    var viewmodel = util.namespace('kpp.viewmodel'),
        model = util.namespace('kpp.model'),
        stageTypeKeys = model.stageTypeKeys,
        stages = model.stageTypes,
        DraggableIssueList = viewmodel.DraggableIssueList;

    viewmodel.Kanban = viewmodel.Kanban || Kanban;

    function Kanban(o) {
        var that = this;

        that.members = null;

        that.issues = null;

        that.project = null;

        // that.stages[name] = 各ステージのIssues
        that.stages = null;

        // that.draggableList[stage] = DraggableIssueList
        // that.draggableList[stage][UserID] = DraggableIssueList
        that.draggableList = {};

        // 追加するユーザの名前
        that.addMemberUserName = ko.observable();

        // 追加するIssueのタイトル
        that.addIssueTitle = ko.observable();

        // 追加するIssueの説明
        that.addIssueBody = ko.observable();

        // アサイン先のユーザ名
        that.assignUserName = ko.observable();

        // 選択しているIssue
        that.selectedIssue = ko.observable();

        // Issueの更新後のタイトル
        that.updateIssueDetailTitle = ko.observable();

        // Issueの更新後のBody
        that.updateIssueDetailBody = ko.observable();

        that.selectedIssue.subscribe(function (issue) {
            that.updateIssueDetailTitle(issue ? issue.title() : null);
            that.updateIssueDetailBody(issue ? issue.body() : null);
        });

        // 選択しているメンバー
        that.selectedMember = ko.observable();

        // 編集用の仮のWIP制限
        that.settingsWipLimit = ko.observable();

        // IssueListが表示されているか
        that.openedIssueList = ko.observable(false);

        // IssueListの表示/非表示の切り替え
        that.toggleOpenedIssueList = function () {
            that.openedIssueList(!that.openedIssueList());
        };

        that.selectedMember.subscribe(function (member) {
            if (member) {
                that.settingsWipLimit(member.wipLimit());
            }
        });

        // アサイン可能なメンバー
        that.canAssignMembers = ko.computed(function () {
            return that.members().filter(function (member) {
                return !member.isWipLimited();
            });
        }, that, {deferEvaluation: true});

        that.init = function (project) {
            that.project = project;
            that.members = project.members;
            that.issues = project.issues;
            that.stages = project.stages;
            that.initDraggableIssueList();

            initSocket();
        };

        // 各ステージ、各メンバー毎にDraggableIssueListを作る
        // メンバーの追加を監視する
        that.initDraggableIssueList = function () {
            that.draggableList = {};
            var _params = {
                masterIssues: that.issues,
                onUpdateStage: that.updateStage,
                onUpdatePriority: that.updateIssuePriority
            };
            _.values(stages).forEach(function (stage) {
                var params = _.extend(_params, {stage: stage.name}),
                    list;

                if (stage.assigned) {
                    list = {};
                    that.members().forEach(function (member) {
                        list[member._id()] = new DraggableIssueList(_.extend(params, {assignee: member._id()}));
                    });
                    that.members.subscribe(function (changes) {
                        _.chain(changes)
                            .where({status: 'added'})
                            .pluck('value')
                            .filter(function (member) { return !list[member._id()]; })
                            .forEach(function (member) {
                                list[member._id()] = new DraggableIssueList(_.extend(params, {assignee: member._id()}));
                            });
                    }, this, 'arrayChange');
                } else {
                    list = new DraggableIssueList(params);
                }

                that.draggableList[stage.name] = list;
            });
        };

        // メンバーを追加する
        that.addMember = function () {
            that.socket.emit('add-member', {userName: that.addMemberUserName()}, function (res) {
                if (res.status === 'success') {
                    // reset form
                    that.addMemberUserName(null);
                }
            });
        };

        // メンバーを削除する
        that.removeMember = function () {
            var member = that.selectedMember();
            if (!member) {
                console.error('unselected member');
                return;
            }

            that.socket.emit('remove-member', {userName: member.userName()}, function (res) {
                if (res.status === 'success') {
                    // モーダルを閉じる
                    $('.modal').modal('hide');
                }
            });
        };

        // メンバー設定を更新する
        that.updateMember = function () {
            var member = that.selectedMember();
            if (!member) { return console.error('unselected member'); }

            that.socket.emit('update-member', {userName: member.userName(), wipLimit: that.settingsWipLimit()});
        };

        that.updateMemberOrderUp = function (member) {
            var members = that.members();
            var idx = members.indexOf(member);
            if (idx === 0) { return console.log(member.userName() + ' is already top'); }
            that.updateMemberOrder(member, members[idx - 1]);
        };

        that.updateMemberOrderDown = function (member) {
            var members = that.members();
            var idx = members.indexOf(member);
            if (idx === members.length - 1) { return console.log(member.userName() + ' is already bottom'); }
            that.updateMemberOrder(member, (idx + 2) === members.length ? null : members[idx + 2]);
        };

        // タスクの優先順位を変更する
        that.updateMemberOrder = function (member, insertBeforeOfMember) {
            var insertBeforeOfUserName =  insertBeforeOfMember ? insertBeforeOfMember.userName() : null;
            that.socket.emit('update-member-order', {userName: member.userName(), insertBeforeOfUserName: insertBeforeOfUserName});
        };

        // Issueと追加する
        that.addIssue = function () {
            var title = that.addIssueTitle(),
                body = that.addIssueBody();

            that.socket.emit('add-issue', {title: title, body: body}, function (res) {
                if (res.status === 'success') {
                    // reset form
                    that.addIssueTitle(null);
                    that.addIssueBody(null);
                }
            });
        };

        // Issueを削除する (archive)
        that.removeIssue = function (issue) {
            that.socket.emit('remove-issue', {issueId: issue._id()}, _.noop);
        };

        // タスクをアサインする
        // ユーザが指定されていない場合はアンアサインする
        that.assignIssue = function () {
            var issue = that.selectedIssue(),
                user = that.project.getMemberByName(that.assignUserName());

            if (!issue) { throw new Error('issue is not selected.'); }

            that.socket.emit('update-stage', {
                issueId: issue._id(),
                userId: user ? user._id() : null,
                toStage: user ? stages.todo.name : stages.backlog.name
            }, function () {
                // reset form
                that.selectedIssue(null);
                that.assignUserName(null);
            });
        };

        // タスクのステージを一つ次へ移動する
        that.nextStage = function (issue) {
            that.stepStage(issue, 1);
        };

        // タスクのステージを一つ前へ移動する
        that.prevStage = function (issue) {
            that.stepStage(issue, -1);
        };

        // タスクのステージをstep分移動する
        that.stepStage = function (issue, step) {
            var currentStage = issue.stage(),
                toIndex = stageTypeKeys.indexOf(currentStage) + step,
                toStage;

            if (!_.inRange(toIndex, 0, stageTypeKeys.length)) {
                throw new Error('cannot change stage: ' +  toIndex);
            }

            toStage = stageTypeKeys[toIndex];

            // check assign
            // 担当者が必要なステージから必要ないステージへ移行した際には
            // 担当者を外す
            var currentStageDetail = stages[currentStage];
            var toStageDetail = stages[toStage];
            var assign = undefined;
            if (currentStageDetail.assigned && !toStageDetail.assigned) {
                assign = null; // unassign
            }

            that.updateStage(issue, toStage, assign);
        };

        // タスクのステージを変更する
        that.updateStage = function (issue, toStage, /* option */userId) {
            that.socket.emit('update-stage', {issueId: issue._id(), toStage: toStage, userId: userId !== undefined ? userId : issue.assignee()});
        };

        // タスクのタイトル/説明を更新する
        that.updateIssueDetail = function () {
            var issue = that.selectedIssue();
            if (!issue) { throw new Error('issue is not selected'); }

            that.socket.emit('update-issue-detail', {
                issueId: issue._id(),
                title: that.updateIssueDetailTitle(),
                body: that.updateIssueDetailBody()
            }, function (res) {
                if (res.status === 'success') {
                    // reset form
                    that.selectedIssue(null);
                }
            });
        };

        // タスクの優先順位を変更する
        that.updateIssuePriority = function (issue, insertBeforeOfIssue) {
            var insertBeforeOfIssueId =  insertBeforeOfIssue ? insertBeforeOfIssue._id() : null;
            that.socket.emit('update-issue-priority', {issueId: issue._id(), insertBeforeOfIssueId: insertBeforeOfIssueId});
        };

        // events
        that.onClickIssueDetail = function (issue, e) {
            that.selectedIssue(issue);
            $($(e.target.parentElement).attr('data-target')).modal('show');
            return util.cancelBubble(e);
        };

        that.onClickIssueNextStage = function (issue, e) {
            that.nextStage(issue);
            return util.cancelBubble(e);
        };

        that.onClickIssuePrevStage = function (issue, e) {
            that.prevStage(issue);
            return util.cancelBubble(e);
        };

        that.onClickIssueRemove = function (issue, e) {
            that.removeIssue(issue);
            return util.cancelBubble(e);
        };

        that.onClickIssueAssign = function (issue, e) {
            that.selectedIssue(issue);
            $($(e.target.parentElement).attr('data-target')).modal('show');
            return util.cancelBubble(e);
        };

        // ソケット通信のイベント設定、デバッグ設定を初期化する
        function initSocket () {
            that.socket = new model.Socket();

            that.socket.on('connect', function () {
                that.socket.emit('join-project-room', {projectId: that.project.id()});
            });

            that.socket.on('add-member', function (req) {
                that.project.addMember(req.member);
            });

            that.socket.on('remove-member', function (req) {
                var targetMember = that.project.getMember(req.member.user._id);
                that.project.removeMember(targetMember);
            });

            that.socket.on('update-member', function (req) {
                var targetMember = that.project.getMember(req.member.user._id);
                that.project.updateMember(targetMember, req.member);
            });

            that.socket.on('update-member-order', function (req) {
                that.project.updateMemberOrder(req.userName, req.insertBeforeOfUserName);
            });

            that.socket.on('add-issue', function (req) {
                that.project.addIssue(req.issue);
            });

            that.socket.on('remove-issue', function (req) {
                var targetIssue = that.project.getIssue(req.issue._id);
                that.project.removeIssue(targetIssue);
            });

            that.socket.on('update-stage', function (req) {
                that.project.updateStage(req.issueId, req.toStage, req.assignee);
            });

            that.socket.on('update-issue-detail', function (req) {
                var targetIssue = that.project.getIssue(req.issue._id);

                ['title', 'body'].forEach(function (key) {
                    targetIssue[key](req.issue[key]);
                });
            });

            that.socket.on('update-issue-priority', function (req) {
                that.project.updateIssuePriority(req.issue._id, req.insertBeforeOfIssueId);
            });

            that.socket.initSocketDebugMode();
        }
    }

}(ko, io, _, window.nakazawa.util));<|endoftext|>


<commit_before>const webpack = require('webpack');
const path = require('path');
const rootDir = __dirname;
const pkg = require(path.resolve(rootDir, 'package.json'));
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  devtool: "cheap-source-map",
  context: rootDir,
  resolve: {
    root: [
      rootDir,
      path.resolve('./web/static/js')
    ],
    extensions: ['', '.js', '.css', '.scss']
  },
  entry: {
    polyfill: "babel-polyfill",
    vendor: Object.keys(pkg.dependencies).filter(function(pkgName){
      // TODO: review this babel-runtime config now that we only have vendor and app
      return pkgName != "babel-runtime"
    }),
    app: "./web/static/js/index"
  },
  output: {
    path: "./priv/static",
    filename: "js/[name].js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      chunks: ['vendor', 'app']
    }),
    new ExtractTextPlugin("css/app.css", {
      allChunks: true // put css of all chunks in app.css
    })
  ],
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loader: 'babel',
      exclude: /node_modules/,
      query: {
        plugins: [
          path.resolve(rootDir, 'web/static/js/babelRelayPlugin'),
          'transform-runtime'
        ],
        presets: ['react', 'es2015', 'stage-0']
      }
    },{
      test: /\.scss$/,
      loader: ExtractTextPlugin.extract(
        'style?sourceMap',
        [
          'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
          'resolve-url',
          'sass?sourceMap'
        ]
      )
    }]
  }
};
<commit_msg>Add json loader in webpack. Refs #25<commit_after>const webpack = require('webpack');
const path = require('path');
const rootDir = __dirname;
const pkg = require(path.resolve(rootDir, 'package.json'));
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  devtool: "cheap-source-map",
  context: rootDir,
  resolve: {
    root: [
      rootDir,
      path.resolve('./web/static/js')
    ],
    extensions: ['', '.js', '.css', '.scss']
  },
  entry: {
    polyfill: "babel-polyfill",
    vendor: Object.keys(pkg.dependencies).filter(function(pkgName){
      // TODO: review this babel-runtime config now that we only have vendor and app
      return pkgName != "babel-runtime"
    }),
    app: "./web/static/js/index"
  },
  output: {
    path: "./priv/static",
    filename: "js/[name].js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      chunks: ['vendor', 'app']
    }),
    new ExtractTextPlugin("css/app.css", {
      allChunks: true // put css of all chunks in app.css
    })
  ],
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loader: 'babel',
      exclude: /node_modules/,
      query: {
        plugins: [
          path.resolve(rootDir, 'web/static/js/babelRelayPlugin'),
          'transform-runtime'
        ],
        presets: ['react', 'es2015', 'stage-0']
      }
    },{
      test: /\.scss$/,
      loader: ExtractTextPlugin.extract(
        'style?sourceMap',
        [
          'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
          'resolve-url',
          'sass?sourceMap'
        ]
      )
    },{
      test: /\.json$/,
      loader: 'json'
    }]
  }
};
<|endoftext|>


<commit_before>/** The most common candidate for a section's super class.
 * This class should be extended by the section classes
 * which you define for single form which has just one group of fields.
 */

'use strict';

var memoize               = require('memoizee/plain')
  , validDb               = require('dbjs/valid-dbjs')
  , defineStringLine      = require('dbjs-ext/string/string-line')
  , defineFormSectionBase = require('./form-section-base')
  , defineProgressRule      = require('./lib/progress-rule');

module.exports = memoize(function (db) {
	var StringLine, FormSectionBase, ProgressRule;
	validDb(db);
	StringLine      = defineStringLine(db);
	FormSectionBase = defineFormSectionBase(db);
	ProgressRule    = defineProgressRule(db);
	FormSectionBase.extend('FormSection', {
		// Only for internal usage
		resolvedPropertyNames: {
			type: StringLine,
			multiple: true,
			value: function (_observe) {
				var props, resolved, masterPrefix, propertyMaster = this.propertyMaster;
				props = [];
				if (propertyMaster !== this.master) {
					if (propertyMaster.master !== this.master) {
						throw new Error("Defined propertyMaster must share master with defined formSection");
					}
					masterPrefix = propertyMaster.__id__.slice(this.master.__id__.length + 1) + '/';
				} else {
					masterPrefix = '';
				}
				this.propertyNames.forEach(function (name) {
					resolved = propertyMaster.resolveSKeyPath(name, _observe);
					if (!resolved) {
						return;
					}
					if (!resolved.object.hasPropertyDefined(resolved.key)) {
						throw new Error('Could not find property: ' + name);
					}
					props.push(masterPrefix + name);
				}, this);

				return props;
			}
		},
		// Only for internal usage
		formApplicablePropertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var result = [], resolved;

			this.resolvedPropertyNames.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getFormApplicablePropName(resolved.key)])
						!== false) {
					result.push(name);
				}
			}, this);

			return result;
		} },
		// Only for internal usage
		applicablePropertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var props, resolved;
			props = this.formApplicablePropertyNames.copy();
			props.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getApplicablePropName(resolved.key)])
						=== false) {
					props.delete(name);
				}
			}, this);

			return props;
		} },
		missingRequiredPropertyNames: {
			type: StringLine,
			multiple: true,
			value: function (_observe) {
				var resolved, isOwn, db = this.database,
					File = db.File, NestedMap = db.NestedMap, result = [];

				if (this.isUnresolved) {
					return this.resolventStatus < 1 ? [this.resolventProperty] : [];
				}

				this.applicablePropertyNames.forEach(function (name) {
					var value;
					resolved = this.master.resolveSKeyPath(name, _observe);

					if (!resolved) return;
					if (this.isPropertyExcludedFromStatus(resolved, _observe)) return;

					if (resolved.object && NestedMap && (resolved.key === 'map')
							&& (resolved.object instanceof NestedMap)) {
						if (!_observe(resolved.object.ordered._size)) result.push(name);

						return;
					}

					if (resolved.value && (typeof resolved.value === 'object') && resolved.value.__id__ &&
							(typeof resolved.value.getDescriptor('resolvedValue')._value_ === 'function')) {
						// Constrained Value
						if (!_observe(resolved.value._resolvedValue)) result.push(name);

						return;
					}

					if (resolved.descriptor.requireOwn) {
						_observe(resolved.observable);

						if (resolved.descriptor.multiple) {
							isOwn = typeof resolved.descriptor._value_ !== 'function';
						} else {
							isOwn = resolved.descriptor._hasOwnValue_(resolved.object);
						}

						if (!isOwn) {
							result.push(name);
							return;
						}
					}

					if (resolved.descriptor.multiple && !_observe(resolved.observable).size) {
						result.push(name);
						return;
					}

					value = _observe(resolved.observable);

					if (value != null) {
						if (File && (value instanceof File)) {
							value = _observe(value._path);

							if (value == null) result.push(name);
						}
					} else {
						result.push(name);
					}
				}, this);

				return result;
			}
		},
		hasDisplayableRuleDeep: {
			value: function (_observe) {
				return _observe(this.progressRules.displayable._size) > 0;
			}
		},
		hasMissingRequiredPropertyNamesDeep: {
			value: function (_observe) {
				return _observe(this.missingRequiredPropertyNames._size) > 0;
			}
		},
		// Used to set input options for form.
		// Note that in order to use it, you need to set every option separately i.e:
		// db.SomeFormClass.prototype.inputOptions.get('someProperty').set('disabled', true)
		inputOptions: {
			type: db.Object,
			nested: true
		},
		lastEditStamp: {
			value: function (_observe) {
				var res = 0, resolved, lastModified, propertiesToCheck;
				propertiesToCheck = this.resolvedPropertyNames.copy();
				if (this.resolventProperty) {
					propertiesToCheck.add(this.resolventProperty);
				}

				propertiesToCheck.forEach(function (name) {
					resolved = this.master.resolveSKeyPath(name, _observe);
					if (!resolved) {
						return;
					}
					lastModified = _observe(resolved.object['_' + resolved.key]._lastModified);
					if (lastModified > res) res = lastModified;
				}, this);

				return res;
			}
		},
		isPropertyExcludedFromStatus: {
			value: function (resolved, _observe) {
				return this.readOnlyPropertyNames.has(resolved.key) ||
					this.database.FormSectionBase.prototype.isPropertyExcludedFromStatus.call(this,
						resolved, _observe);
			}
		},
		// The names of the model fields which should be handled by this section.
		// Remember to write full property paths relative to the section's master object.
		// value example: ["name", "lastName", "address/street"]
		propertyNames: { type: StringLine, multiple: true },
		// The names of the model fields that should be treated as read only in this section.
		// It should contain the same elements as in propertyNames list. Listed properties will
		// not count towards progress and rendered not as inputs.
		readOnlyPropertyNames: { type: StringLine, multiple: true }
	});
	db.FormSection.prototype.inputOptions._descriptorPrototype_.nested = true;
	db.FormSection.prototype.inputOptions._descriptorPrototype_.type   = db.Object;

	db.FormSection.prototype.progressRules.map.define('missingFields', {
		type: ProgressRule,
		nested: true
	});

	db.FormSection.prototype.progressRules.map.missingFields.setProperties({
		progress: function (_observe) {
			var resolved, section, invalid, total = 0;
			section = this.owner.owner.owner;
			invalid = _observe(section.missingRequiredPropertyNames).size;

			if (!_observe(section._resolventStatus)) return 0;

			_observe(section.applicablePropertyNames).forEach(function (name) {
				resolved = section.master.resolveSKeyPath(name, _observe);

				if (!resolved) {
					++total;
					return;
				}

				if (section.isPropertyExcludedFromStatus(resolved, _observe)) return;

				++total;
			});

			return total === 0 ? 1 : (total - invalid) / total;
		},
		weight: function (_observe) {
			var resolved, section, resolvedResolvent, total = 0, isResolventExcluded;
			section = this.owner.owner.owner;
			if (section.resolventProperty) {
				resolvedResolvent = section.ensureResolvent(_observe);

				if (!resolvedResolvent) return 0;

				isResolventExcluded = section.isPropertyExcludedFromStatus(resolvedResolvent, _observe);
				if (_observe(resolvedResolvent.observable) !== _observe(section._resolventValue)) {
					return isResolventExcluded ? 0 : 1;
				}
				if (!isResolventExcluded) {
					++total;
				}
			}
			_observe(section.applicablePropertyNames).forEach(function (name) {
				resolved = section.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					++total;
					return;
				}
				if (section.isPropertyExcludedFromStatus(resolved, _observe)) {
					return;
				}
				++total;
			});
			return total;
		}
	});

	return db.FormSection;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<commit_msg>Issue #1321. Added isPropertyFilled to FormSection.<commit_after>/** The most common candidate for a section's super class.
 * This class should be extended by the section classes
 * which you define for single form which has just one group of fields.
 */

'use strict';

var memoize               = require('memoizee/plain')
  , validDb               = require('dbjs/valid-dbjs')
  , defineStringLine      = require('dbjs-ext/string/string-line')
  , defineFormSectionBase = require('./form-section-base')
  , defineProgressRule      = require('./lib/progress-rule');

module.exports = memoize(function (db) {
	var StringLine, FormSectionBase, ProgressRule;
	validDb(db);
	StringLine      = defineStringLine(db);
	FormSectionBase = defineFormSectionBase(db);
	ProgressRule    = defineProgressRule(db);
	FormSectionBase.extend('FormSection', {
		// Only for internal usage
		resolvedPropertyNames: {
			type: StringLine,
			multiple: true,
			value: function (_observe) {
				var props, resolved, masterPrefix, propertyMaster = this.propertyMaster;
				props = [];
				if (propertyMaster !== this.master) {
					if (propertyMaster.master !== this.master) {
						throw new Error("Defined propertyMaster must share master with defined formSection");
					}
					masterPrefix = propertyMaster.__id__.slice(this.master.__id__.length + 1) + '/';
				} else {
					masterPrefix = '';
				}
				this.propertyNames.forEach(function (name) {
					resolved = propertyMaster.resolveSKeyPath(name, _observe);
					if (!resolved) {
						return;
					}
					if (!resolved.object.hasPropertyDefined(resolved.key)) {
						throw new Error('Could not find property: ' + name);
					}
					props.push(masterPrefix + name);
				}, this);

				return props;
			}
		},
		// Only for internal usage
		formApplicablePropertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var result = [], resolved;

			this.resolvedPropertyNames.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getFormApplicablePropName(resolved.key)])
						!== false) {
					result.push(name);
				}
			}, this);

			return result;
		} },
		// Only for internal usage
		applicablePropertyNames: { type: StringLine, multiple: true, value: function (_observe) {
			var props, resolved;
			props = this.formApplicablePropertyNames.copy();
			props.forEach(function (name) {
				resolved = this.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					return;
				}
				if (_observe(resolved.object['_' +
						this.database.Object.getApplicablePropName(resolved.key)])
						=== false) {
					props.delete(name);
				}
			}, this);

			return props;
		} },
		isPropertyFilled: {
			type: db.Function,
			value: function (resolved, _observe) {
				var owner     = resolved.object
				  , value     = resolved.value
				  , db        = this.database
				  , NestedMap = db.NestedMap
				  , File      = db.File;

				// NestedMap
				if (owner && NestedMap && (resolved.key === 'map') && (owner instanceof NestedMap)) {
					return Boolean(_observe(owner.ordered._size));
				}

				// Constrained Value
				if (value && (typeof value === 'object') && value.__id__ &&
						(typeof value.getDescriptor('resolvedValue')._value_ === 'function')) {
					return Boolean(_observe(value._resolvedValue));
				}

				// Simple multiple
				if (resolved.descriptor.multiple) return Boolean(_observe(resolved.observable).size);

				value = _observe(resolved.observable);
				if (value != null) {
					// File
					if (File && (value instanceof File)) {
						return Boolean(_observe(value._path));
					}

					// Simple value
					return true;
				}

				return false;
			}
		},
		missingRequiredPropertyNames: {
			type: StringLine,
			multiple: true,
			value: function (_observe) {
				var resolved, isOwn, db = this.database,
					File = db.File, NestedMap = db.NestedMap, result = [];

				if (this.isUnresolved) {
					return this.resolventStatus < 1 ? [this.resolventProperty] : [];
				}

				this.applicablePropertyNames.forEach(function (name) {
					var value;
					resolved = this.master.resolveSKeyPath(name, _observe);

					if (!resolved) return;
					if (this.isPropertyExcludedFromStatus(resolved, _observe)) return;

					if (resolved.object && NestedMap && (resolved.key === 'map')
							&& (resolved.object instanceof NestedMap)) {
						if (!_observe(resolved.object.ordered._size)) result.push(name);

						return;
					}

					if (resolved.value && (typeof resolved.value === 'object') && resolved.value.__id__ &&
							(typeof resolved.value.getDescriptor('resolvedValue')._value_ === 'function')) {
						// Constrained Value
						if (!_observe(resolved.value._resolvedValue)) result.push(name);

						return;
					}

					if (resolved.descriptor.requireOwn) {
						_observe(resolved.observable);

						if (resolved.descriptor.multiple) {
							isOwn = typeof resolved.descriptor._value_ !== 'function';
						} else {
							isOwn = resolved.descriptor._hasOwnValue_(resolved.object);
						}

						if (!isOwn) {
							result.push(name);
							return;
						}
					}

					if (resolved.descriptor.multiple && !_observe(resolved.observable).size) {
						result.push(name);
						return;
					}

					value = _observe(resolved.observable);

					if (value != null) {
						if (File && (value instanceof File)) {
							value = _observe(value._path);

							if (value == null) result.push(name);
						}
					} else {
						result.push(name);
					}
				}, this);

				return result;
			}
		},
		hasDisplayableRuleDeep: {
			value: function (_observe) {
				return _observe(this.progressRules.displayable._size) > 0;
			}
		},
		hasMissingRequiredPropertyNamesDeep: {
			value: function (_observe) {
				return _observe(this.missingRequiredPropertyNames._size) > 0;
			}
		},
		// Used to set input options for form.
		// Note that in order to use it, you need to set every option separately i.e:
		// db.SomeFormClass.prototype.inputOptions.get('someProperty').set('disabled', true)
		inputOptions: {
			type: db.Object,
			nested: true
		},
		lastEditStamp: {
			value: function (_observe) {
				var res = 0, resolved, lastModified, propertiesToCheck;
				propertiesToCheck = this.resolvedPropertyNames.copy();
				if (this.resolventProperty) {
					propertiesToCheck.add(this.resolventProperty);
				}

				propertiesToCheck.forEach(function (name) {
					resolved = this.master.resolveSKeyPath(name, _observe);
					if (!resolved) {
						return;
					}
					lastModified = _observe(resolved.object['_' + resolved.key]._lastModified);
					if (lastModified > res) res = lastModified;
				}, this);

				return res;
			}
		},
		isPropertyExcludedFromStatus: {
			value: function (resolved, _observe) {
				return this.readOnlyPropertyNames.has(resolved.key) ||
					this.database.FormSectionBase.prototype.isPropertyExcludedFromStatus.call(this,
						resolved, _observe);
			}
		},
		// The names of the model fields which should be handled by this section.
		// Remember to write full property paths relative to the section's master object.
		// value example: ["name", "lastName", "address/street"]
		propertyNames: { type: StringLine, multiple: true },
		// The names of the model fields that should be treated as read only in this section.
		// It should contain the same elements as in propertyNames list. Listed properties will
		// not count towards progress and rendered not as inputs.
		readOnlyPropertyNames: { type: StringLine, multiple: true }
	});
	db.FormSection.prototype.inputOptions._descriptorPrototype_.nested = true;
	db.FormSection.prototype.inputOptions._descriptorPrototype_.type   = db.Object;

	db.FormSection.prototype.progressRules.map.define('missingFields', {
		type: ProgressRule,
		nested: true
	});

	db.FormSection.prototype.progressRules.map.missingFields.setProperties({
		progress: function (_observe) {
			var resolved, section, invalid, total = 0;
			section = this.owner.owner.owner;
			invalid = _observe(section.missingRequiredPropertyNames).size;

			if (!_observe(section._resolventStatus)) return 0;

			_observe(section.applicablePropertyNames).forEach(function (name) {
				resolved = section.master.resolveSKeyPath(name, _observe);

				if (!resolved) {
					++total;
					return;
				}

				if (section.isPropertyExcludedFromStatus(resolved, _observe)) return;

				++total;
			});

			return total === 0 ? 1 : (total - invalid) / total;
		},
		weight: function (_observe) {
			var resolved, section, resolvedResolvent, total = 0, isResolventExcluded;
			section = this.owner.owner.owner;
			if (section.resolventProperty) {
				resolvedResolvent = section.ensureResolvent(_observe);

				if (!resolvedResolvent) return 0;

				isResolventExcluded = section.isPropertyExcludedFromStatus(resolvedResolvent, _observe);
				if (_observe(resolvedResolvent.observable) !== _observe(section._resolventValue)) {
					return isResolventExcluded ? 0 : 1;
				}
				if (!isResolventExcluded) {
					++total;
				}
			}
			_observe(section.applicablePropertyNames).forEach(function (name) {
				resolved = section.master.resolveSKeyPath(name, _observe);
				if (!resolved) {
					++total;
					return;
				}
				if (section.isPropertyExcludedFromStatus(resolved, _observe)) {
					return;
				}
				++total;
			});
			return total;
		}
	});

	return db.FormSection;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<|endoftext|>


<commit_before>
/*********************************
*    process key operations on search
***********************************/

function processSearchKey(){
    keyCode = d3.event.keyCode;

    filterText = d3.select('#search-text').property('value');
    if (keyCode == 13) {
            // initialize left charts panel
            var productCode = onFilter(filterText);
            $("#chart1").empty();
            var uc1=parallel_chart("#chart1");
            filtered = find_and_rank_comparables(data, productCode, "sugars_100g");
            uc1.update(filtered);
    }
    else{
        onFilter(filterText);
    }

}

/*********************************
*    search for matching data
***********************************/

function onFilter(filterText){

    filteredData = data;
    var filteredData;

    // if products need to be filtered based on user input, then do that here
    if (filterText !== ""){
        var filteredData = data.data.filter(function(d){
            filteredItems = d.product_name.indexOf(filterText);
            return (filteredItems === 0);
        });
    }

    filteredData = filteredData.slice(0,4)
    d3.select('#filteredList').html(
        filteredData.map(function(d){
        return d.product_name;
        }).join(".
")
    );

    return filteredData[0].code;
}
<commit_msg>passing data.data instead of data to chart<commit_after>
/*********************************
*    process key operations on search
***********************************/

function processSearchKey(){
    keyCode = d3.event.keyCode;

    filterText = d3.select('#search-text').property('value');
    if (keyCode == 13) {
            // initialize left charts panel
            var productCode = onFilter(filterText);
            $("#chart1").empty();
            var uc1=parallel_chart("#chart1");
            filtered = find_and_rank_comparables(data.data, productCode, "sugars_100g");
            uc1.update(filtered);
    }
    else{
        onFilter(filterText);
    }

}

/*********************************
*    search for matching data
***********************************/

function onFilter(filterText){

    filteredData = data;
    var filteredData;

    // if products need to be filtered based on user input, then do that here
    if (filterText !== ""){
        var filteredData = data.data.filter(function(d){
            filteredItems = d.product_name.indexOf(filterText);
            return (filteredItems === 0);
        });
    }

    filteredData = filteredData.slice(0,4)
    d3.select('#filteredList').html(
        filteredData.map(function(d){
        return d.product_name;
        }).join(".
")
    );

    return filteredData[0].code;
}
<|endoftext|>


<commit_before>// 评论相关js
(function(){
	window.Comment = {};

	$(document).ready(function(){
		// 文本框事件
		$(".page-comment #commentForm textarea").on('click', function(){
			// 没有登录
			if($("#is_login_status").val() != 1){
				openPop("#login-pop");
			}
		});

		$('#comment-content').on('change', function() {
			var content = $(this).val();

			var objdata = {content: content};

			saveReplyDraft(uid, keyprefix, objid, objdata);
		});

		(function() {
			var draft = loadReplyDraft(uid, keyprefix, objid);
			if (draft) {
				$('#comment-content').val(draft.content);
			}
		})();

		$('.page-comment .md-toolbar .edit').on('click', function(evt){
			evt.preventDefault();

			$(this).addClass('cur');
			$('.page-comment .md-toolbar .preview').removeClass('cur');

			$('.page-comment .content-preview').hide();
			$('.page-comment #commentForm .text').show();
		});
		$('.page-comment .md-toolbar .preview').on('click', function(evt){
			evt.preventDefault();

			var marked = SG.markSetting();

			$(this).addClass('cur');
			$('.page-comment .md-toolbar .edit').removeClass('cur');

			$('.page-comment #commentForm .text').hide();
			var content = $('.page-comment #commentForm textarea').val();
			$('.page-comment .content-preview').html(marked(content));
			// emoji 表情解析
			emojify.run($('.page-comment .content-preview').get(0));
			$('.page-comment .content-preview').show();

		});

		$('#replies').on('mouseenter', '.reply', function(evt) {
			$(this).find('.op-reply').removeClass('hideable');
		});
		$('#replies').on('mouseleave', '.reply', function(evt) {
			$(this).find('.op-reply').addClass('hideable');
		});

		$('#replies').on('click', '.reply_user', function(evt) {
			if ($(evt.target).hasClass('reply_user')) {
				$(this).parents('.reply-to-block').find('.markdown').toggleClass('dn');
			}
		});

		$('#replies').on('click', '.btn-reply', function(evt) {
			evt.preventDefault();

			var floor = $(this).data('floor'),
				username = $(this).data('username');
			var $replyTo = $('.md-toolbar .reply-to');

			$replyTo.data('floor', floor).data('username', username);

			var title = '回复#'+floor+'楼';
			$replyTo.children('.fa-mail-reply').attr('title', title);
			$replyTo.children('.user').attr('title', title).attr('href', '#reply'+floor).text(username+' #'+floor);
			$replyTo.removeClass('dn');

			$('#commentForm textarea').focus();
		});

		$('.md-toolbar .reply-to .close').on('click', function(evt) {
			evt.preventDefault();
			$(this).parents('.reply-to').addClass('dn').data('floor', '').data('username', '');
		});

		emojify.setConfig({
			// emojify_tag_type : 'span',
			only_crawl_id    : null,
			img_dir          : SG.EMOJI_DOMAIN,
			ignored_tags     : { //忽略以下几种标签内的emoji识别
				'SCRIPT'  : 1,
				'TEXTAREA': 1,
				'A'       : 1,
				'PRE'     : 1,
				'CODE'    : 1
			}
		});

		// 异步加载 评论
		window.loadComments = function() {
			var objid = $('.comment-list').data('objid'),
				objtype = $('.comment-list').data('objtype');

			var params = {
				'objid': objid,
				'objtype': objtype
			};
			$.getJSON('/object/comments', params, function(data){
				if (data.ok) {
					data = data.data;
					var comments = data.comments;

					var content = '';
					for(var i in comments) {
						var comment = comments[i],
							user = data[comment.uid];

						var avatar = user.avatar;
						if (avatar == "") {
							if (isHttps) {
								user.avatar = 'https://secure.gravatar.com/avatar/'+md5(user.email)+"?s=48";
							} else {
								user.avatar = 'http://gravatar.com/avatar/'+md5(user.email)+"?s=48";
							}
						} else if (avatar.indexOf('http') === -1) {
							user.avatar = cdnDomain+'avatar/'+avatar+'?imageView2/2/w/48';
						}

						var cmtTime = SG.timeago(comment.ctime);
						if (cmtTime == comment.ctime) {
							var cmtTimes = cmtTime.split(" ");
							comment.cmt_time = cmtTimes[0];
						} else {
							comment.cmt_time = cmtTime;
						}

						if (comment.reply_floor > 0) {
							var replyComment = comments[comment.reply_floor-1]
							comment.reply_user = data[replyComment.uid];
							comment.reply_content = replyComment.content;
						}

						comment.content = parseCmtContent(comment.content);

						content += $.templates('#one-comment').render({comment: comment, user: user});
					}

					if (content != '') {
						$('.comment-list .words').html(content);

						// 链接，add target=_blank
						$('.comment-list .words .markdown').on('mousedown', 'a', function(evt){
							var url = $(this).attr('href');
							$(this).attr('target', '_blank');
						});
					}
					$('.comment-list .words').removeClass('hide');

					// emoji 表情解析
					emojify.run($('.comment-list .words').get(0));

					if ($("#is_login_status").val() == 1) {
						SG.registerAtEvent(true, true, $('.page-comment textarea'));
					}
				} else {
					comTip("回复加载失败");
				}
			});
		}

		var parseCmtContent = function(content) {
			var marked = SG.markSetting();
			content = SG.preProcess(content);
			content = marked(content);
			return SG.replaceCodeChar(content);
		};

		// 回复提交
		$('#comment-submit').on('click', function(){
			var content = $('#commentForm textarea').val();

			if(content == ""){
				alert("其实你想说点什么...");
			} else {
				var floor = $('.md-toolbar .reply-to').data('floor');
				if (parseInt(floor, 10) > 0) {
					var username = $('.md-toolbar .reply-to').data('username');
					content = '#'+floor+'楼 @'+username+' '+content;
				}
				postComment($(this), content, function(comment) {
					comTip("回复成功！");
					purgeReplyDraft(uid, keyprefix, objid);

					$('#commentForm textarea').val('');

					$('.md-toolbar .reply-to .close').click();
				});
			}
		});

		var postComment = function(thiss, content, callback){
			thiss.text("稍等").addClass("disabled").attr({"title":'稍等',"disabled":"disabled"});

			var objid = $('.comment-list').data('objid'),
				objtype = $('.comment-list').data('objtype');

			var usernames = SG.analyzeAt(content);

			$.ajax({
				type:"post",
				url: '/comment/'+objid,
				data: {
					"objtype": objtype,
					"content": content,
					"usernames": usernames.join(',')
				},
				dataType: 'json',
				success: function(data){
					if(data.ok){
						var comment = data.data;

						var $pageComment = $('.comment-list'),
							user = {};

						user.username = $pageComment.data('username'),
						user.uid = $pageComment.data('uid'),
						user.avatar = $pageComment.data('avatar'),
						comment.cmt_time = SG.timeago(comment.ctime);
						comment.reply_floor = 0;
						comment.content = parseCmtContent(comment.content);

						var oneCmt = $.templates('#one-comment').render({comment: comment, user: user, is_new: true});

						var $cmtNumObj = $('#replies .cmtnum'),
							cmtNum = parseInt($cmtNumObj.text(), 10);
						if (cmtNum == 0) {
							$('.comment-list .words').html('');
						}

						$('.comment-list .words').append(oneCmt).removeClass('hide');

						// emoji 表情解析
						emojify.run($('.comment-list .words .reply:last').get(0));

						// 注册@
						SG.registerAtEvent(true, true, $('.page-comment textarea'));

						cmtNum++;

						$cmtNumObj.text(cmtNum);

						setTimeout(function(){
							$('.comment-list .words .reply').removeClass('light');
						}, 2000);
						callback();
					} else {
						alert(data.error);
					}
				},
				complete:function(){
					thiss.text("提交").removeClass("disabled").removeAttr("disabled").attr({"title":'提交'});
				},
				error:function(){
					thiss.text("提交").removeClass("disabled").removeAttr("disabled").attr({"title":'提交'});
				}
			});
		}
	});
}).call(this)<commit_msg>回复引用楼层问题处理<commit_after>// 评论相关js
(function(){
	window.Comment = {};

	$(document).ready(function(){
		// 文本框事件
		$(".page-comment #commentForm textarea").on('click', function(){
			// 没有登录
			if($("#is_login_status").val() != 1){
				openPop("#login-pop");
			}
		});

		$('#comment-content').on('change', function() {
			var content = $(this).val();

			var objdata = {content: content};

			saveReplyDraft(uid, keyprefix, objid, objdata);
		});

		(function() {
			var draft = loadReplyDraft(uid, keyprefix, objid);
			if (draft) {
				$('#comment-content').val(draft.content);
			}
		})();

		$('.page-comment .md-toolbar .edit').on('click', function(evt){
			evt.preventDefault();

			$(this).addClass('cur');
			$('.page-comment .md-toolbar .preview').removeClass('cur');

			$('.page-comment .content-preview').hide();
			$('.page-comment #commentForm .text').show();
		});
		$('.page-comment .md-toolbar .preview').on('click', function(evt){
			evt.preventDefault();

			var marked = SG.markSetting();

			$(this).addClass('cur');
			$('.page-comment .md-toolbar .edit').removeClass('cur');

			$('.page-comment #commentForm .text').hide();
			var content = $('.page-comment #commentForm textarea').val();
			$('.page-comment .content-preview').html(marked(content));
			// emoji 表情解析
			emojify.run($('.page-comment .content-preview').get(0));
			$('.page-comment .content-preview').show();

		});

		$('#replies').on('mouseenter', '.reply', function(evt) {
			$(this).find('.op-reply').removeClass('hideable');
		});
		$('#replies').on('mouseleave', '.reply', function(evt) {
			$(this).find('.op-reply').addClass('hideable');
		});

		$('#replies').on('click', '.reply_user', function(evt) {
			if ($(evt.target).hasClass('reply_user')) {
				$(this).parents('.reply-to-block').find('.markdown').toggleClass('dn');
			}
		});

		$('#replies').on('click', '.btn-reply', function(evt) {
			evt.preventDefault();

			var floor = $(this).data('floor'),
				username = $(this).data('username');
			var $replyTo = $('.md-toolbar .reply-to');

			$replyTo.data('floor', floor).data('username', username);

			var title = '回复#'+floor+'楼';
			$replyTo.children('.fa-mail-reply').attr('title', title);
			$replyTo.children('.user').attr('title', title).attr('href', '#reply'+floor).text(username+' #'+floor);
			$replyTo.removeClass('dn');

			$('#commentForm textarea').focus();
		});

		$('.md-toolbar .reply-to .close').on('click', function(evt) {
			evt.preventDefault();
			$(this).parents('.reply-to').addClass('dn').data('floor', '').data('username', '');
		});

		emojify.setConfig({
			// emojify_tag_type : 'span',
			only_crawl_id    : null,
			img_dir          : SG.EMOJI_DOMAIN,
			ignored_tags     : { //忽略以下几种标签内的emoji识别
				'SCRIPT'  : 1,
				'TEXTAREA': 1,
				'A'       : 1,
				'PRE'     : 1,
				'CODE'    : 1
			}
		});

		// 异步加载 评论
		window.loadComments = function() {
			var objid = $('.comment-list').data('objid'),
				objtype = $('.comment-list').data('objtype');

			var params = {
				'objid': objid,
				'objtype': objtype
			};
			$.getJSON('/object/comments', params, function(data){
				if (data.ok) {
					data = data.data;
					var comments = data.comments;

					var content = '';
					for(var i in comments) {
						var comment = comments[i],
							user = data[comment.uid];

						var avatar = user.avatar;
						if (avatar == "") {
							if (isHttps) {
								user.avatar = 'https://secure.gravatar.com/avatar/'+md5(user.email)+"?s=48";
							} else {
								user.avatar = 'http://gravatar.com/avatar/'+md5(user.email)+"?s=48";
							}
						} else if (avatar.indexOf('http') === -1) {
							user.avatar = cdnDomain+'avatar/'+avatar+'?imageView2/2/w/48';
						}

						var cmtTime = SG.timeago(comment.ctime);
						if (cmtTime == comment.ctime) {
							var cmtTimes = cmtTime.split(" ");
							comment.cmt_time = cmtTimes[0];
						} else {
							comment.cmt_time = cmtTime;
						}

						if (comment.reply_floor > 0) {
							var replyComment = comments[comment.reply_floor-1]
							comment.reply_user = data[replyComment.uid];
							comment.reply_content = replyComment.content;
						}

						comment.content = parseCmtContent(comment.content);

						content += $.templates('#one-comment').render({comment: comment, user: user});
					}

					if (content != '') {
						$('.comment-list .words').html(content);

						// 链接，add target=_blank
						$('.comment-list .words .markdown').on('mousedown', 'a', function(evt){
							var url = $(this).attr('href');
							$(this).attr('target', '_blank');
						});
					}
					$('.comment-list .words').removeClass('hide');

					// emoji 表情解析
					emojify.run($('.comment-list .words').get(0));

					if ($("#is_login_status").val() == 1) {
						SG.registerAtEvent(true, true, $('.page-comment textarea'));
					}
				} else {
					comTip("回复加载失败");
				}
			});
		}

		var parseCmtContent = function(content) {
			var marked = SG.markSetting();
			content = SG.preProcess(content);
			content = marked(content);
			return SG.replaceCodeChar(content);
		};

		// 回复提交
		$('#comment-submit').on('click', function(){
			var content = $('#commentForm textarea').val();

			if(content == ""){
				alert("其实你想说点什么...");
			} else {
				var floor = $('.md-toolbar .reply-to').data('floor');
				if (parseInt(floor, 10) > 0) {
					var username = $('.md-toolbar .reply-to').data('username');
					content = '#'+floor+'楼 @'+username+' '+content;
				}
				postComment($(this), content, function(comment) {
					comTip("回复成功！");
					purgeReplyDraft(uid, keyprefix, objid);

					$('#commentForm textarea').val('');

					$('.md-toolbar .reply-to .close').click();
				});
			}
		});

		var postComment = function(thiss, content, callback){
			thiss.text("稍等").addClass("disabled").attr({"title":'稍等',"disabled":"disabled"});

			var objid = $('.comment-list').data('objid'),
				objtype = $('.comment-list').data('objtype');

			var usernames = SG.analyzeAt(content);

			$.ajax({
				type:"post",
				url: '/comment/'+objid,
				data: {
					"objtype": objtype,
					"content": content,
					"usernames": usernames.join(',')
				},
				dataType: 'json',
				success: function(data){
					if(data.ok){
						var comment = data.data;

						var $pageComment = $('.comment-list'),
							user = {};

						user.username = $pageComment.data('username'),
						user.uid = $pageComment.data('uid'),
						user.avatar = $pageComment.data('avatar'),
						comment.cmt_time = SG.timeago(comment.ctime);
						if (comment.reply_floor > 0) {
							comment.content = content.substr(2);
						}
						comment.reply_floor = 0;
						comment.content = parseCmtContent(comment.content);

						var oneCmt = $.templates('#one-comment').render({comment: comment, user: user, is_new: true});

						var $cmtNumObj = $('#replies .cmtnum'),
							cmtNum = parseInt($cmtNumObj.text(), 10);
						if (cmtNum == 0) {
							$('.comment-list .words').html('');
						}

						$('.comment-list .words').append(oneCmt).removeClass('hide');

						// emoji 表情解析
						emojify.run($('.comment-list .words .reply:last').get(0));

						// 注册@
						SG.registerAtEvent(true, true, $('.page-comment textarea'));

						cmtNum++;

						$cmtNumObj.text(cmtNum);

						setTimeout(function(){
							$('.comment-list .words .reply').removeClass('light');
						}, 2000);
						callback();
					} else {
						alert(data.error);
					}
				},
				complete:function(){
					thiss.text("提交").removeClass("disabled").removeAttr("disabled").attr({"title":'提交'});
				},
				error:function(){
					thiss.text("提交").removeClass("disabled").removeAttr("disabled").attr({"title":'提交'});
				}
			});
		}
	});
}).call(this)<|endoftext|>


<commit_before>
angular.module('forms', [
	'duScroll', 'ngResource', 'NodeForm.templates', 'pascalprecht.translate',
	'angular-input-stars',
	'ui.select'
]);

angular.module('NodeForm', [
    'ui.select', 'cgBusy', 'ngSanitize', 'vButton', 'ngResource',
    'ui.router', 'ui.bootstrap', 'ui.utils', 'ngRaven', 'forms',
	'ui.select'
]);

angular.module('forms').factory('Auth', [
  function() {
    var service = {
      _currentUser: null,
      get currentUser(){
        return this._currentUser;
      },
      ensureHasCurrentUser: function() {
        return null;
      },
      isAuthenticated: function() {
        return false;
      },
      getUserState: function() {
        return '';
      },
      login: function() {
      },
      logout: function() {
      },
    };
    return service;
  }
]);
angular.module('forms').factory('$state', [function() {
    return {
        go: function() {}
    };
}]);
angular.module('forms').factory('myForm', ['Forms', function(Forms) {
    var form = window.form;
    form.visible_form_fields = _.filter(form.form_fields, function(field){
    	return (field.deletePreserved === false);
    });
    return form;
}]);
angular.module('forms').constant('FORM_URL', '/form/:formId');

angular.element(document).ready(function() {
	//Then init the app
	angular.bootstrap(document, ['forms']);
});
<commit_msg>fix star in demo<commit_after>
angular.module('forms', [
	'duScroll', 'ngResource', 'NodeForm.templates', 'pascalprecht.translate',
	'angular-input-stars'
]);

angular.module('NodeForm', [
    'ui.select', 'cgBusy', 'ngSanitize', 'vButton', 'ngResource',
    'ui.router', 'ui.bootstrap', 'ui.utils', 'ngRaven', 'forms',
	'ui.select'
]);

angular.module('forms').factory('Auth', [
  function() {
    var service = {
      _currentUser: null,
      get currentUser(){
        return this._currentUser;
      },
      ensureHasCurrentUser: function() {
        return null;
      },
      isAuthenticated: function() {
        return false;
      },
      getUserState: function() {
        return '';
      },
      login: function() {
      },
      logout: function() {
      },
    };
    return service;
  }
]);
angular.module('forms').factory('$state', [function() {
    return {
        go: function() {}
    };
}]);
angular.module('forms').factory('myForm', ['Forms', function(Forms) {
    var form = window.form;
    form.visible_form_fields = _.filter(form.form_fields, function(field){
    	return (field.deletePreserved === false);
    });
    return form;
}]);
angular.module('forms').constant('FORM_URL', '/form/:formId');

angular.element(document).ready(function() {
	//Then init the app
	angular.bootstrap(document, ['forms']);
});
<|endoftext|>