Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
1,570 additions
and
992 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
|
||
type Hashmap<T> = { [K: string]: T }; | ||
type primitive = "string" | "number" | "boolean"; | ||
type SettingsPageItem = { | ||
level: 0 | 1 | 2, | ||
name: string, | ||
// valueType: string, | ||
// valueOptions?: any[] | ||
}; | ||
type ValueType_function = { | ||
valueType: "function", | ||
// validate: (level: number, upd: ServerConfig) => { valid: boolean, value: any, isChanged: boolean } | ||
} & SettingsPageItem; | ||
type ValueType_primitive = { | ||
valueType: primitive | ||
} & SettingsPageItem; | ||
type ValueType_enum = { | ||
valueType: "enum", | ||
enumType: primitive, | ||
enumOpts: any[] | ||
// valueOptions: ["number" | "string", (number | string)[]] | ||
} & SettingsPageItem; | ||
type ValueType_hashmapenum = { | ||
valueType: "hashmapenum", | ||
enumType: primitive, | ||
enumKeys: string[] | ||
} & SettingsPageItem; | ||
type ValueType_subpage = { | ||
valueType: "subpage", | ||
// handler: (state: StateObject) => void; | ||
} & SettingsPageItem; | ||
type SettingsPageItemTypes = ValueType_function | ValueType_enum | ValueType_hashmapenum | ValueType_primitive | ValueType_subpage; | ||
|
||
interface RootScope extends angular.IScope { | ||
|
||
} | ||
|
||
interface GlobalScope extends RootScope { | ||
|
||
} | ||
|
||
|
||
|
||
//@ts-ignore | ||
let app = angular.module('settings', [ | ||
|
||
]); | ||
app.config(function ($locationProvider) { | ||
$locationProvider.html5Mode(false).hashPrefix('*'); | ||
}); | ||
app.run(function ($templateCache) { | ||
var templates = { | ||
string: `<input type="text" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`, | ||
number: `<input type="number" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`, | ||
boolean: `<input type="checkbox" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`, | ||
enum: ` | ||
<select name="{{item.name}}" value="" ng-disabled="readonly" title="{{item.name}}" | ||
ng-model="outputs[item.name]" | ||
ng-options="k for k in item.enumOpts"> | ||
</select> <span ng-bind-html="description"></span> | ||
`, | ||
hashmapenum: ` | ||
<div ng-repeat="(i, key) in item.enumKeys track by $index" | ||
ng-controller="HashmapEnumItemCtrl" | ||
ng-include="'template-' + item.valueType"></div> | ||
`, | ||
subpage: `<a href="{{item.name}}">Please access this setting at the {{item.name}} subpage.</a>`, | ||
function: ` | ||
<ng-include src="'template-function' + item.name"></ng-include> | ||
`, | ||
functiontypes: `Coming soon`, | ||
settingsPage: ` | ||
<fieldset ng-repeat="(i, item) in data" ng-controller="SettingsPageItemCtrl"> | ||
<legend>{{item.name}}</legend> | ||
<div ng-include="'template-' + item.valueType"></div> | ||
</fieldset> | ||
` | ||
} | ||
|
||
for (var i in templates) { | ||
$templateCache.put("template-" + i, templates[i]); | ||
} | ||
}); | ||
interface HashmapEnumItemCtrlScope extends SettingsPageItemCtrlScope { | ||
key: string; | ||
} | ||
app.controller("HashmapEnumItemCtrl", function ($scope: HashmapEnumItemCtrlScope, $sce: angular.ISCEService) { | ||
let parentItem: SettingsPageItemTypes = $scope.item; | ||
if (parentItem.valueType !== "hashmapenum") return; | ||
$scope.item = { | ||
valueType: parentItem.enumType, | ||
name: $scope.key, | ||
level: parentItem.level | ||
} as SettingsPageItemTypes; | ||
if (typeof $scope.outputs[parentItem.name] !== "object") | ||
$scope.outputs[parentItem.name] = {}; | ||
$scope.outputs = $scope.outputs[parentItem.name]; | ||
$scope.description = $scope.description[$scope.key]; | ||
if (typeof $scope.description === "string") { | ||
$scope.description = $sce.trustAsHtml($scope.description); | ||
} | ||
}); | ||
interface SettingsPageItemCtrlScope extends SettingsPageCtrlScope { | ||
item: SettingsPageItemTypes; | ||
description: string | Hashmap<any>; | ||
readonly: boolean; | ||
} | ||
app.controller("SettingsPageItemCtrl", function ($scope: SettingsPageItemCtrlScope, $sce: angular.ISCEService) { | ||
|
||
$scope.description = $scope.descriptions[$scope.item.name]; | ||
if (typeof $scope.description === "string") { | ||
$scope.description = $sce.trustAsHtml($scope.description); | ||
} | ||
$scope.readonly = $scope.item.level > $scope.level; | ||
|
||
}); | ||
interface SettingsPageCtrlScope extends RootScope { | ||
data: SettingsPageItemTypes[]; | ||
descriptions: Hashmap<string | Hashmap<any>>; | ||
outputs: Hashmap<any>; | ||
level: number; | ||
} | ||
app.controller("SettingsPageCtrl", function ($scope: SettingsPageCtrlScope, $http: angular.IHttpService) { | ||
let timeout: number; | ||
let saveWait = 1000; | ||
let oldSettings: Hashmap<string>; | ||
function saveSettings() { | ||
let set = {}; | ||
$scope.data.forEach(item => { | ||
let key = item.name; | ||
let newval = JSON.stringify($scope.outputs[key]); | ||
console.log(newval, oldSettings[key]); | ||
if (oldSettings[key] !== newval) { | ||
if (item.level <= $scope.level) | ||
set[key] = $scope.outputs[key]; | ||
oldSettings[key] = newval; | ||
} | ||
}) | ||
$http.put("?action=update", JSON.stringify(set)); | ||
} | ||
|
||
$http.get('?action=getdata').then(res => { | ||
let { level, data, descriptions, settings } = res.data as any; | ||
let timeout; | ||
|
||
$scope.$watch((s: SettingsPageCtrlScope) => JSON.stringify(s.outputs), (item, old) => { | ||
if (!old || item === old) return; | ||
if (timeout) clearTimeout(timeout); | ||
timeout = setTimeout(saveSettings, saveWait); | ||
}); | ||
|
||
$scope.data = data; | ||
$scope.descriptions = descriptions; | ||
$scope.outputs = settings; | ||
$scope.level = level; | ||
oldSettings = {}; | ||
data.forEach(item => { | ||
oldSettings[item.name] = JSON.stringify(settings[item.name]); | ||
}) | ||
$scope.$broadcast('refresh'); | ||
}); | ||
}) | ||
|
||
|
||
|
||
|
||
|
||
// angular.module("settings", [ | ||
|
||
// ]).controller("globalCtrl", function ($scope: GlobalScope, $http: angular.IHttpService) { | ||
// $http.get("?") | ||
// }) | ||
|
||
// const old = { | ||
// "template-children":/* just some green */ ` | ||
// <div ng-repeat="(i, item) in item.children track by $index" | ||
// ng-class="[item.classes, 'type-' + item.type, 'output-' + item.output].join(' ')" | ||
// ng-include="'template-' + item.type"></div>`, | ||
// "template-repeater":/* just some green */ ` | ||
// <div ng-repeat="(j, outputs) in outputs[item.output]" | ||
// ng-class="item.repeaterClasses" | ||
// ng-include="'template-children'"></div>`, | ||
// "template-hashmap-item":/* just some green */ ` | ||
// <li ng-repeat="(j, outputs) in outputs"> | ||
// <code style="min-width: 200px;">{{j}}</code> | ||
// <ul ng-if="typeof(outputs) == 'object'" ng-include="'template-hashmap-item'"></ul> | ||
// <code ng-if="typeof(outputs) == 'string'">{{outputs}}</code> | ||
// </li>`, | ||
// 'template-hashmap':/* just some green */ ` | ||
// <ul ng-include="'template-hashmap-item'" ></ul> | ||
// <input type="text" ng-model="outputs[item.output]['$add']" /> | ||
// <button ng-click="outputs[item.output][outputs[item.output]['$add']] = ''">+ Item</button> | ||
// <button ng-click="outputs[item.output][outputs[item.output]['$add']] = {}">+ Hash</button> | ||
// `, | ||
// 'template-text-row':/* just some green */ ` | ||
// <span style="display:inline-block; width:200px">{{item.name}}</span> | ||
// <input type="text" ng-model="outputs[item.output]"/> | ||
// ` | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"compilerOptions": { | ||
"outDir": "../static/" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<!doctype html> | ||
<html> | ||
|
||
<head> | ||
<style> | ||
dl.treelist { | ||
margin: 0; | ||
} | ||
</style> | ||
<!-- https: //code.angularjs.org/1.6.9 --> | ||
<script src="/static/polyfill-promise.js"></script> | ||
<script src="/static/angular.min.js"></script> | ||
<script src="/static/settings-tree.js"></script> | ||
<title></title> | ||
</head> | ||
|
||
<body ng-app="settings"> | ||
<h1>Hi</h1> | ||
<div ng-include="'template-settingsPage'" ng-controller="SettingsPageCtrl"></div> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
|
||
type Hashmap<T> = { [K: string]: T }; | ||
type SettingsPageItem = { | ||
level: 0 | 1 | 2, | ||
name: string, | ||
// valueType: string, | ||
// valueOptions?: any[] | ||
}; | ||
type ValueType_function = { | ||
valueType: "function", | ||
// valueOptions: [(defValue: any, keys: string[], readOnly: boolean, description: any) => string] | ||
} & SettingsPageItem; | ||
type ValueType_primitive = { | ||
valueType: "string" | "number" | "boolean" | ||
} & SettingsPageItem; | ||
type ValueType_enum = { | ||
valueType: "enum", | ||
valueOptions: ["number" | "string", (number | string)[]] | ||
} & SettingsPageItem; | ||
type ValueType_hashmapenum = { | ||
valueType: "hashmapenum", | ||
valueOptions: [("string" | "number" | "boolean")[], string[]] | ||
} & SettingsPageItem; | ||
type ValueType_subpage = { | ||
valueType: "subpage", | ||
valueOptions: {} | ||
} & SettingsPageItem; | ||
type SettingsPageItemTypes = ValueType_function | ValueType_enum | ValueType_hashmapenum | ValueType_primitive | ValueType_subpage; | ||
|
||
interface RootScope extends angular.IScope { | ||
|
||
} | ||
|
||
interface GlobalScope extends RootScope { | ||
|
||
} | ||
|
||
|
||
|
||
//@ts-ignore | ||
let app = angular.module('settings', [ | ||
|
||
]); | ||
app.config(function ($locationProvider) { | ||
$locationProvider.html5Mode(false).hashPrefix('*'); | ||
}); | ||
app.run(function ($templateCache) { | ||
var templates = { | ||
string: `<input type="text" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`, | ||
number: `<input type="number" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`, | ||
boolean: `<input type="checkbox" title="{{item.name}}" name="{{item.name}}" ng-disabled="readonly" ng-model="outputs[item.name]"/> <span ng-bind-html="description"></span>`, | ||
enum: ` | ||
<select name="{{item.name}}" value="" ng-disabled="readonly" title="{{item.name}}" | ||
ng-model="outputs[item.name]" | ||
ng-options="k for k in item.valueOptions[1]"> | ||
</select> <span ng-bind-html="description"></span> | ||
`, | ||
hashmapenum: ` | ||
<div ng-repeat="(i, key) in item.valueOptions[1] track by $index" | ||
ng-controller="HashmapEnumItemCtrl" | ||
ng-include="'template-' + item.valueType"></div> | ||
`, | ||
subpage: `<a href="{{item.name}}">Please access this setting at the {{item.name}} subpage.</a>`, | ||
function: ` | ||
<ng-include src="'template-function' + item.name"></ng-include> | ||
`, | ||
functiontypes: `Coming soon`, | ||
settingsPage: ` | ||
<fieldset ng-repeat="(i, item) in data" ng-controller="SettingsPageItemCtrl"> | ||
<legend>{{item.name}}</legend> | ||
<div ng-include="'template-' + item.valueType"></div> | ||
</fieldset> | ||
` | ||
} | ||
|
||
for (var i in templates) { | ||
$templateCache.put("template-" + i, templates[i]); | ||
} | ||
}); | ||
interface HashmapEnumItemCtrlScope extends SettingsPageItemCtrlScope { | ||
key: string; | ||
} | ||
app.controller("HashmapEnumItemCtrl", function ($scope: HashmapEnumItemCtrlScope, $sce: angular.ISCEService) { | ||
let parentItem: SettingsPageItemTypes = $scope.item; | ||
if (parentItem.valueType !== "hashmapenum") return; | ||
$scope.item = { | ||
valueType: parentItem.valueOptions[0][0], | ||
name: $scope.key, | ||
level: parentItem.level | ||
} as SettingsPageItemTypes; | ||
if (typeof $scope.outputs[parentItem.name] !== "object") | ||
$scope.outputs[parentItem.name] = {}; | ||
$scope.outputs = $scope.outputs[parentItem.name]; | ||
$scope.description = $scope.description[$scope.key]; | ||
if (typeof $scope.description === "string") { | ||
$scope.description = $sce.trustAsHtml($scope.description); | ||
} | ||
}); | ||
interface SettingsPageItemCtrlScope extends SettingsPageCtrlScope { | ||
item: SettingsPageItemTypes; | ||
description: string | Hashmap<any>; | ||
readonly: boolean; | ||
} | ||
app.controller("SettingsPageItemCtrl", function ($scope: SettingsPageItemCtrlScope, $sce: angular.ISCEService) { | ||
|
||
$scope.description = $scope.descriptions[$scope.item.name]; | ||
if (typeof $scope.description === "string") { | ||
$scope.description = $sce.trustAsHtml($scope.description); | ||
} | ||
$scope.readonly = $scope.item.level > $scope.level; | ||
|
||
}); | ||
interface SettingsPageCtrlScope extends RootScope { | ||
data: SettingsPageItemTypes[]; | ||
descriptions: Hashmap<string | Hashmap<any>>; | ||
outputs: Hashmap<any>; | ||
level: number; | ||
} | ||
app.controller("SettingsPageCtrl", function ($scope: SettingsPageCtrlScope, $http: angular.IHttpService) { | ||
$http.get('?action=getdata').then(res => { | ||
let { level, data, descriptions, settings } = res.data as any; | ||
$scope.data = data; | ||
$scope.descriptions = descriptions; | ||
$scope.outputs = settings; | ||
$scope.level = level; | ||
$scope.$broadcast('refresh'); | ||
}); | ||
}) | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"compilerOptions": { | ||
"outDir": "../static/" | ||
} | ||
} |
Oops, something went wrong.