2014-05-09 ========== - The hook Node.onCreateNewItem can create new children, but there is no way as of yet give an indication to the user which nodes support that feature, and which don't. - TODO: suppress hover highlighting while dragging 2014-05-07 ========== Last night I found out the (very) hard way what an unfriendly beast browserify-shim is. Here are the lessons learned: - I didn't, and still don't, know how to programmatically specify options for browserify-shim when it's being used as a transform. Passing a "shim" property in the browserify config object (as I've seen done) appears to have no effect whatsoever. - What does work is a package.json file placed alongside the Javascript file(s) being bundled. This is a total nuisance if the package provider does not provide a "browserify-shim" property (and most don't yet). [Just wrote the author about this as a comment to a Github issue.] 2014-05-06 ========== I've asked myself whether SketchPad was worth pursuing further in its current form, and I think the answer is no. I believe it makes more sense to create a new knockout widget based on an existing library like paper.js. paper.js seems to offer the necessary features. What remains to do is find ways to synchronize between drawn objects and the observable date they represent. With SketchPad, I've so far sidestepped that need (imperfectly) by creating drawables that use observables for point positions, but of course that approach has narrow limits. What is needed are adapters, which should probably consist of very little code plus good descriptions (or metadata? constraint-checkers?) of how to translate existing data into graphical objects of the kind that paper.js (or another library) supports. The little bit of code that could be provided may help with avoiding synchronization loops. Object.observe() may be worth a try for the other direction. 2014-04-27 ========== - Bug: mouse position is not scroll-adjusted! - I'm a bit concerned about documentation - how to make sure I'll be able to use this stuff after 6 months interruption? - The test harness will help. - It may be time to think about defining a meta-language that will allow extracting documentation bits directly from the code. -> This should be the same meta-language that will also allow template-based coding. 2014-04-26 ========== - Observation about Knockout: when it is necessary to operate with the view itself (i.e. the DOM elements), the view model is the wrong starting point. Instead, one must start at the root element, look at contained elements and use ko.dataFor() to obtain the associated parts of the view model. ---------- - Command Group reflow is working, but a redesign will be needed: groups should never, or at least almost never, just flip from being above to being below. 2014-04-25 ========== - SketchPad zoom: outline widths are scaled, shouldn't be ? 2014-04-22 ========== I've begun work on the command panel. I have brackets visually marking the "zones" that the commands in a given group would act upon; and as it is, the group panels are also moving to be on the exact right of that zone. That is not good enough though. I must prevent group panels from overlapping each other; and there must be some logic as to their respective placement. To achieve this, command group panels should be able to take a "priority" parameter, which will place them higher up. The group panels should still try to get close to their target zones though. 2014-04-19 ========== I'm about to begin working on the "Commands" module. To sum up what that is: The Commands module provides ways to represent and manage the commands that a user can send to a Widget. [Reminder: a Widget is defined as a reusable software component with a user-facing aspect (a usually visual representation, usually but not necessarily interactive.] The goal is to show the user what commands can be given to a Widget at any given time, and to facilitate the giving of these commands. To this end, Commands are associated with interactive representations called Command Buttons. Command Buttons can have keyboard shortcuts, but can always be triggered by a mouse click. Command Buttons could be presented to the user in any number of ways, but one idea worth looking at is the "Side Panel". This is a Panel that would most probably be "floating" over the rightmost 15-20% of the display's width, showing the Buttons of all Commands that are relevant in the current situation. Just displaying a list, top-down, of all available Commands could be helpful as-is, but the idea is to help the user by creating a visual association between the command and the visual representation of the command's *target*. This association could comprise several indications, but the most basic one would be to vertically align the Buttons with their *target zone*. [Other, possibly complementary options would be rectangular overlays, possibly even animations.] Commands are associated with view models (or parts thereof) via Command Sets; in other words, a command set represents, for the Commands module, the entirety of the commands that can be given to that view model (at that level). A command set is an object that can be used in any view model. The CommandSet class provides handlers for keyboard, focus and blur events, which can be bound declaratively in the standard Knockout way, or called from model-specific event handlers (in which case, making the command set part of the view model may not be necessary). Incidently, what exactly the "target" is depends on the widget. [In the case of the TreeView, it would the currently selected (and focused) node.] Widgets can be nested, building a "tree" each "node" of which can have a Command Set that the Side Panel must be able to present to the user. Because of this, it is often impractical to have each Widget instance create its own Command Set. Better create a Command Set that covers the needs of the whole widget class, then "re-connect" the Commands to a specific widget instance every time such an instance becomes *active* [which would usually be in response to a focus event]. [How exactly to (re-)connect the commands is left to the widget code. If the widget consists of many elements, one approach could be to use a class-wide top-level "activeInstance" observable that all observables in the commands are subscribed to, which would make them update automatically through the Knockout dependency chains when the active instance changes. However, just traversing the actions and re-connecting them by code should work just as well.] Commands can have a keyboard shortcut definition; the idea behind that is to help users by displaying the shortcuts as part of the labels of the command buttons, thereby not only telling the user what key combination to use, but providing an alternate way to trigger the command as well (by clicking on the command button). In order for the Commands module to be able to trigger actions through their shortcuts, it must be informed of keyboard events. In order to make this as easy as possible, the view model used by the Commands module provides handlers that can be bound by (example) "keydown: $root.commands.keyDown, keyup: $root.commands.keyUp". ---- SCRAPPING ALL THAT, TOO COMPLICATED --------------- --------------------- Let's begin simple, then improve incrementally. Also, avoid trying to make it 100% specific to Knockout (while still using Knockout for the implementation). Command: name, description, shortcut, enabled, visible, target, method, parameters. ---------------------- - The Gulpfile is getting too long, and is already too complicated. Maybe it's time to create a Gulp-based build module of my own. - Wrapping text files into string-defining CommonJS - Generating bundles with browserify (what to require, and exclude) - Use meaningful default locations (src, dist, testsrc, testbed), overridable - Automate watching (by standardizing the way source files are specified, making it easy to create a "watch list") - Using gulp-newer consistently - Support "special" builds (future) - Based on this generic build framework, the KO Widgets Gulpfile can then define a task generator function that can be reused for each new widget. ---------------------- - Having modules refer to a "temp" directory is ok for generating bundles with browserify but sucks when using native Node-Webkit require(). Either: a) generate to a subdirectory that will be considered part of the source (even though it really isn't) or b) use the readFileSync() method: http://stackoverflow.com/a/17502924/754534 -> (b) doesn't work; I would actually need a combination of readDirSync() and readFileSync() to gather all the templates used by a widget. UNLESS I decide to import my templates one by one. ... so (c) the pragmatic solution: instead of calling the directory "temp", I'm now calling it "generated", which is at least less misleading. 2014-04-18 ========== - http://knockoutjs.com/documentation/custom-bindings-disposal.html 2014-04-17 ========== - Support using functions where values or observables are usually expected, by default everywhere. Create a utility function/method makeValue() or something. 2014-04-15 ========== Ok, next steps. It appears clear at this point that I do need a mechanism for synchronization between data models. I kind of improvised something for the Tree View, but that's just too cumbersome to become a pattern. [Or rather, that pattern is no longer sufficient for SketchPad, as SketchPad cannot pass data model parts "down" to custom Knockout templates). What are the requirements ? - Initialize view model with a tailored image of the data model. - Synchronize changes made on the view model back to the data model. - Synchronize changes made on the data model to the live view model. What does "tailored image" mean ? We want to: - Drop properties and sub-objects that do not have to, or cannot, be represented in the view model - Move properties and sub-objects up or down the view model hierarchy (should be more rare) - Generally freely adapt between the data and the view model What does syncing view model back to data model imply ? - Filtering view model parts that have no representation in the data model - Adapting the view model's representation to the one used in the data model - It implies that the view model is notifying the "mapper" of any and all changes What do live updates to the view model imply ? - It actually means repeating parts of the initial mapping process, BUT: - Care must be taken not to duplicate already existing mapped view model data I'm now going to do some research into possible existing approaches: - JSONPath: interesting --- Ok, now on to my own thoughts and ideas: - I was thinking about a selector syntax like maybe JSONPath. Maybe however it would be better to create my own, specially made for this particular taks; or maybe it can be avoided after all, by providing a clean, callback-driven API. - [I should keep in mind that at some point, I will create an interactive tool to help create mappings.] - Mapping is orchestrated by a "mapping controller". - Behavior is default-driven; i.e., no code should be needed to obtain a fully functional mapping when every property needed by the view model is already contained in the data model. - Maybe a full-fledged mapping is not needed (yet) after all? How about: - SketchPad notifies about all changes via callback that specifies full path - Callback finds data model object - how ? -> mapping stores ref to data model object into view model object - And so, it appears I'll be re-using the TreeView pattern after all. I shall try to isolate it into a separate module, and take a more or less iterative approach from here. ------------- Ok, no big steps today. A few things to note: - GObject implements dragging now; however it relies on the existence of _drawPath() for this. It might be better to define a hitTest(x, y) method so that the hit test doesn't depend on _drawPath(), which may not be needed in some cases. - OR... create a default implementation that depends on the existence of width and height properties, which so far are not part of GObject. - It may be useful to implement a way to select objects obscured by others. - Cycle through objects in reverse Z order when mouse button is depressed for more than 1 second ? - Adjoin a tree view representing the objects (and their properties) ? - Should polygon edges stay selected when the polygon itself is de-selected ? - No way to de-select by mouse click so far 2014-04-14 ========== Implemented polygon vertex dragging, noted two problems: - When dragging beyond the margin (i.e. the extended region of the overlay canvas) and letting go of the mouse button there, the mouse up is not detected. Is there a way to implement real mouse capturing? -> NO. This is not sufficiently standardized. I worked around it (for now?) by sending a mouseUp() to the current object from the mouseout event. - No matter where exactly you click inside a vertex handle, when dragging it, the vertex will be moved to the exact location of the mouse pointer (i.e., the initial offset is discarded). Not sure that's really a problem though. 2014-04-06 ========== I've done a small amount of additional work on the SketchPad widget. - The Area outlining is inclusive on top and to the left, but exclusive at the bottom and on the right (if the area is a rectangle). I'm not sure how to remedy this; it is simple for rectangles only. Perhaps the previous approach with the Polygon class was better after all. - Idea: use observables to initialize Polygon properties like fillColor ? However, do not provide them directly as they could not access the not-yet-created Polygon object; instead, support functions that will be turned into computeds (actually though, it would be easy to support both [!create a utility function]) - The "overlay" canvas probably should *not* be offset by (0.5, 0.5) (though it should be offset by (margin, margin)). The idea of getting automatic grid-fitting so easily was naive; better leave that to the code handling the individual elements. - canvas.style.cursor = ... can be used to easily changed the mouse cursor 2014-04-01 ========== - Provide a "widget" that does nothing more than do the "centered layout trick" ----------- Ok, I've successfully added the embryo for a new widget "SketchPad" (code and Gulpfile). I wonder whether I should now proceed to create a test page for it as well ? Another question is how to make it interactive. The project I'm actually intending it for right now doesn't really *need* it to have controls (though it could probably benefit if they were there). This underscores the need for an "action" subsystem. I wonder whether I should get that off the ground right now? What should be in it right off the bat? - Action bars / panels ? - How to position such things ? Or is that outside the scope of the action subsystem? -> probably - Custom actions (and their buttons) must be easy to do (shouldn't be a problem) - Enable/disable (by assigning an observable?) 2014-03-23 ========== The next step - what shall it be ? There is a need for a "panel" widget, or perhaps more accurately a "property" panel: displaying labels to the left and values to the right. In its simplest form though, that wouldn't help much - the TreeView widget can do something very similar on its own. A row must be more flexible than that, able to display multiple cells. I'm thinking that there is actually no real need for a "widget" to support this. Using Knockout, creating customized DOM for any given row of data and /or input fields is a breeze. All that is needed for now are: 1) a way to insert arbitrary nodes (on top of the filtering that is already implemented), and 2) a way to have an arbitrary named template render the rest of a row (at the aligned position after the label) I'll get to work on that immediately. 2014-03-22 ========== Okay, so I've finally figured out how to use browserify properly. The current Gulpfile produces a Javascript bundle that exports a synchronous require() function returning the TreeView constructor. It would be very easy to use the standalone option to produce instead a bundle that exposes a global such as gpc.kowidgets.TreeView - yes namespacing is supported. 2014-03-21 ========== - Browserify "standalone" option name components cannot contain underscores! - http://aeflash.com/2014-03/a-year-with-browserify.html - Read: https://github.com/thlorenz/browserify-shim (global:) -------------- - The "global:" shim type was the answer all along... gawds the time spent because of not knowing that - or was it the unwillingness to lean back for a moment? 2014-03-20 ========== - Create a package.json in dist - UMD-ify the treeview bundle - Try browserify "standalone" ? ----------- OMG. What at frustrating day, ending in nothing more than a consolation prize. - Why can't I browserify-require a bundle that I created in a separate run of browserify? require() keeps returning an empty object, as if there was an unresolved dependency. Shouldn't this be the standard way to use a bundle ? - How do I create a bundle that satisfies certain dependencies from globals ? - Namespacing the global export with the browserify standalone option is possible! (Just use dot-separated names.) - Good starting point: http://blakeembrey.com/articles/introduction-to-browserify/ 2014-03-19 ========== It just occurred to me that the "treeview.js" bundle contains stuff that is global to the widget library and thus does not directly belong to the treeview widget. In the future, this might lead to multiple inclusions. => QUESTION: is this ok ? Probably not. I must be prepared to do one or more of the following: 1) Make the code available as CommonJS modules 2) Make the code available as AMD modules 3) Make the code available as UMD modules 4) Provide a bundle that includes the complete widget collection 5) Provide bundles for certain widget combinations 6) Provide custom bundles through a web site / service (à la jQuery-UI) ------------ Next step: provide Knockout templates without requiring users to import them using Jade, i.e. pass them into Knockout via Javascript. 2014-03-16 ========== Nothing done for two days... But I don't want this project to stall, so I'll lay out the next steps here. My vision for this is to get me an unfailing toolkit from which to build lots of future applications. I feel that I'm running into the usual trap here, which is a form of overwhelm, combined with a longing for perfection, both of which are conspiring to make me quit, as they VERY often have in the past. I hereby acknowledge and accept that as fact. I HEREBY ACKNOWLEDGE AND ACCEPT THAT AS FACT. Ok. When faced with overwhelm, a good thing to do is to come up with a clear path forward. The goal: I want a toolbox. I want a quick, easy and robust way to put together a node-webkit application using Jade, Knockout and whathever else it takes. Let's list the obstacles: 1) Boilerplate code and build system setup. Short of a full, Delphi-inspired IDE (which is a dream of mine), one may choose between using (and maintaining a template) or just copying the last comparable project. This is where my sense of perfection gets in my way - I profoundly dislike both these options. I don't really require an IDE, but I absolutely want something that doesn't make me think. This is what I want to provide as a product. THIS IS WHAT I WANT TO PROVIDE AS A PRODUCT. To build towards that goal, I have already obtained some experience with build systems, and I'm currently favoring Gulp. I am routinely using Jade not just because it is faster to type and easier to read than HTML, but also because I can use include statements to bring in Knockout templates that are needed by the Knockout-based widget collection I am trying to create [note: this is probably not a good idea though - I definitely want to inject Knockout templates via Javascript in the very near future]. A particularly annoying part of boilerplate, and as yet unsolved, is (script) package management. I have learned quite a bit about that too, but it's still murky. I do have a standard approach for obtaining the packages and getting them into the build (preferring manual Gulpfile work), but I still don't have a clear procedure for actually using the scripts in my code: whether or not to use AMD, CommonJS, or to just really on script tags. Perhaps I should do some more research there; UMD seems to be getting traction. One thought that came to my mind is to maintain a JSON list of packages to include, tagging each with the package manager to use (NPM, bower, etc; or "manual") and the packaging standard it uses (CommonJS, AMD, UMD, none); or perhaps it's sufficient to just add that info to package.json. The idea is to derive the configuration for the build tasks without the need for endless trial and error as was so often the case before now. Another dream tries to get into the way of progress here, and that's the ability to create both a web page/site and a node-webkit application from the same codebase. ... to be continued ... 2014-03-14 ========== - Create a base class "Element" (ancestor for node, usable from all widgets) to help with computed observables (disposeWhen), and possibly other things ? 2014-03-13 ========== On top of the question "how to add child nodes to an as yet childless parent", I have to answer the related question "how to append a child node" ? To create a first child for a childless parent, one could use the "Right" arrow key when the node has already been opened. As for the second question, that is trickier. The "Down" arrow should simply move to the next visible node, while the "return" key is normally understood to mean "activate". Yet, as the key that is used to append a new paragraph when typing text, return would be the most intuitive choice. (Actually, that's not quite true - what the return key does is actually *end* the current paragraph and *begin* a new one.) One thing to keep in mind: only nodes of "array" (or "collection") type can have children. There is not much choice but to reserve the return key for appending, and to assign the "activate" action to another key. And actually, "activate" probably won't even be needed for an unmodified tree view. [DONE. return/enter now appends nodes] -------------- - Instead of having "combined" actions like closeOrClimb() that do different actions depending on the node's state, it might be better to implement this via predicates (talking about the future "action" registry system). -------------- - Wouldn't it be cool to be able to visually assign keyboard shortcuts to actions during development, possibly using predicates to have the same key (combination) trigger different actions depending on current state ? -------------- Problem: the closures I'm using to determine array item indexes automatically seem to be generating memory leaks by keeping the items around indefinitely. - Tried alternate way, same results => Must implement as a method of Node 2014-03-12 ========== - After deleting last child, focus should go to parent node [DONE 2014-03-13] - How to add child nodes to an empty array ? -------- - Cool: the npm-junction module provides a command "nlink" based on SysInternals that works around the annoying EPERM problem when using "npm link " 2014-03-11 ========== - I'm no longer sure if the idea to support data that is in KO viewmodel form was a good one. The problem is that every piece of code accessing the underlying data must now support both plain values/objects and ko observables. - I should not assume that the index of a child node within its parent is the same as the index of the data item within its containing array. -> Should I store the index/key of each item in the node representing it ? - This implies a change to the constructor signature - If index is changeable, it must be an observable - Node class: underscores to lead the names of all private members ? ------------- - There should be an automatic label observable on array item nodes ----------- - Rename "filter" func to "onNewNode" ? DONE ----------- Things to write down before going to sleep: - Inserting nodes indirectly by inserting into the underlying observableArray seems to be a good working principle. Left to do: somewhow set focus on new node once it's been generated. - Luxury edition: implement a mechanism that checks whether or not the node actually was added - Todo: ability to remove nodes 2014-03-10 ========== - Interesting: http://philipnilsson.github.io/Badness10k/articles/knockout-patterns-proxy/ ----------- A note about the "value" column in the present implementation, for future reference: - The "value" column is an optional feature of the treeview, and can be enabled via the showValueColumn() observable - The tree view cannot display anything in the value column by itself, though it does generate a container element for each cell (currently a TD); it delegates the rendering of values to the KO template that can be specified for each node via the valueTemplateName() observable - There is currently no observable or treeview-specific CSS rules to set the width of the value column. ---------- It's too late in the evening now to get started on implementing node insertion, but it'll have to be done soon. Here are my thoughts about it at this time: - There must be a way to create the actual item being added. - The filter() callback offers an opportunity to attach the relevant code to a parent node. Drawback: the separation of concerns is not clean: the responsibility for creating items should lie with its container (reminder: a node's children member is not the original container, more like a reflection of it) - Attaching a createItem() function to the "real" observableArray() is also a possibility. The drawback with that is that it requires user code in one more place, i.e. in the callback passed to ko.mapping.fromJS() (for example). (Of course, there is nothing wrong with implementing both.) Another way to look at it is that JS arrays, by themselves, are simply not intended to be full-fledged collections. It would be very interesting, in the future, to integrate the tree view widget with Backbone (possibly using Knockback?), which should have all required features. In the meantime though, I guess it would be perfectly acceptable to use node-attached code to complement plain observableArrays with the code necessary to make insertion/deletion work. 2014-03-09 ========== About models (not view models) with two-way binding capability: - Extending observables! http://knockoutjs.com/documentation/fn.html - Knockback could probably provide the models that I need, but it seems like overkill. -> https://github.com/thelinuxlich/knockout.model ? -> https://coderwall.com/p/bzmrja ? ----------- - The newer version of Knockout have an observableArray subscription "arrayChange" that should cover my needs (done a quick-and-dirty experiment). - About the labels of array elements: they should not necessarily indicate the index. Use the fromModel() filter callback for that (e.g. computed observable) [DONE for the "adjustments" array (and fixed a bug along the way)] 2014-03-08 ========== I've successfully gotten started on implementing keyboard commands (navigation only, so far). Next steps: - Although fromModel(), the (temporary?) replacement for fromJSON() now accepts KO observables, I haven't implemented bidirectional synchronization yet. However this is crucial for the tree view (and widgets generally) to reach production level, so I should do this next. - I'm not sure it's possible/practical for items being added to / removed from an object (non-array), since there is no equivalent to observableArray for objects. - Unidirectional updates (from the tree to the model) could be implemented using an extended version of observableArray: https://groups.google.com/forum/#!topic/knockoutjs/RLzj244VGSo Thinking about it, it could be made bidirectional by making ko.mapping use that extended version of observableArray as well. - Care must be taken to avoid update loops. This could be done by concatenating "routes" (as arrays of objects); a simple indexOf() would then tell whether the current object could be the origin of the change notification. - Detail: having to use the ctrl modifier to open and close nodes is not really intuitive now that the up & down keys will cross levels. [CHANGED. At least I was able to test key combinations.] 2014-03-07 ========== - Customize Node objects by assigning a "prototype" ? -> no longer create default node instances in advance ? create it afterwards unless the filter function provides one - Provide hooks in Node - as virtual methods or as events ? - Provide CollectionNode, HashNode, ValueNode derived classes ? --------------- - fromJSON() replaced with fromModel() which is capable of working with observable-wrapped data (as obtained from ko.mapping()) What is called for now is a way to add columns; or at least, for now, to apply knockout templates to cells in the "value" column. This should be possible using Knockout named templates. I'm going to start by providing three very basic templates for numbers, booleans, and strings. Fine-tuning appearance and behaviour could be done through the use of $parent, which would, in the context of a value "cell" template, access the containing node object. --------- I've done the above experimentally, it appears to work well. Next steps: - Implement keyboard navigation - decide whether to support multiple "custom columns" or just a single one. I'm leaning towards a single one, which user code can then fill by itself - The "extension column" is enabled by a property of each node, which specifies the name of the template to use. - Support read-only cells - The simplest option is simply not to wrap those values as observables. - Other options (a meta-info callback ?) could be implemented later - I need a means to add and delete items to and from arrays. For the time being, it will suffice if this can only be done through the keyboard [and if I get around to implement "actions" and keyboard shortcuts, this may eliminate the need to provide specific UI elements]. Validating and sorting should be doable via the mapping.fromJS() callbacks (using multi-level "mapping" properties). - I also want to provide, as a convenience, a few super-simple predefined named templates for numbers, booleans and strings - How to pass in the metadata (min, max, etc.) ? It may turn out to be necessary to wrap cells into standardized span's after all. In that case, it would also be necessary to implement a node type registry. Each node's type would then determine what additional columns it displays. I rather hope to avoid this. It would mean a lot of work for something that is beyond the scope of a simple tree view. Each extended row would, in effect, be treated like a sub-widget; and ko.mapping.fromJS's "create" hook provides the golden opportunity to create custom nodes, by using classes derived from Node. - FUTURE: support "container" child nodes that do not have a name and can contain anything 2014-03-05 ========== - It may be interesting to investigate an alternative way to layout the tree view, based on CSS table styling. This depends on whether or not it is possible to "wrap" table-row-group elements. - As an alternative, just use tables (or equivalently styled elements) as they are supposed to and use generated class tags to show/hide node content. The advantage of any of these methods (only the second would probably work) is that layout can be done by CSS alone. UPDATE: I have found and implemented a way to do it CSS-only with the current approach by wrapping the "main" part of each node header and setting the width of that. -------------- UPDATE #2: back to computed indenting - easier in the end. Implementing ellipses for labels was tricky, it required using tables because inline-block elements that have overflow != visible cannot align on the baseline (for no reason I can understand). 2014-03-03 ========== - fromJSON(): option to automatically make children-less object into leaf nodes ? - Should nodes be Observables ? - Should children array be an observableArray ? - Option to leave out non-object (implying non-array) items ? - Implement a way to order children - Implement extra columns - Implement two-way binding (with callback to enable updating) - Implement a way to delay loading of contents - Possibly give visual feedback to distinguish between "has children" and "content not loaded yet" ? 2014-03-02 ========== - distinguish between nodes that are necessarily leafs and those that could have children but don't for the time being 2014-03-01 ========== Lots of todos: - option to include root node - open/close - mouse or keyboard -> what about tab behavior ? - callback function(s) to customize fromJSON() - "default property" (typically an array) that is represented by its parent node ? - ability to attach additional "columns" - emit events when node is entered or exited; same for hover Misc: - Is it possible to have knockout remove/omit the structural stuff when generating DOM content from templates ? --------------- - The root node is actually already being rendered, but without a label 2014-02-27 ========== - I've fallen back for now to adding the knockout templates by including a single Jade include file. But I think I will replace this with a browserify or RequireJS plugin to read in the code as text, then use document.write() to inject those templates into the DOM (alternative: override ko.templateEngine.makeTemplateSource()). 2014-02-26 ========== - It starts to look like Injector will really be necessary; adding knockout templates by hand is incredibly tedious. 2014-02-25 ========== Project description ------------------- This is where I'm going to develop and maintain my library of reusable widgets based on Knockout, Jade, and Stylus; plus possibly jQuery-UI. The idea is not to create something incredibly sophisticated here (though it may develop into just that in time), but rather to keep it as simple as possible. In terms of the triad of building blocks mentioned above, this means: - Use Knockout as the mechanism binding data to visual (i.e. DOM) elements; in particular, use Knockout "templates" to create reusable building blocks. - Use Jade as the means to bring knockout templates into the HTML (as pseudo-script elements) and provide mixins that generate app-specific DOM subtrees (e.g. forms) First step: a Tree View to navigate a JSON data object ------------------------------------------------------ (Project ls-instr-editor) JSON data is comprised of nodes, each containing named properties that can be either simple data values, other nodes or arrays. The challenge is to make this into a useful tree view without having the code getting overly complex. Knockout's named template mechanism provides a possibility here: have a property in each node of the view model that determines which of the available node-rendering templates should be used for that particular node. [Another question is how to get node-specific content into the tree view (though that may be for a future version). It could be done through viewmodel properties with HTML content; precompiled Jade templates can help here - the question is whether those can contain Knockout in turn. Or is it necessary (and possible ?) to generate named templates on-the-fly?] Concretizing ------------ Let's call our visual components "widgets". What's in a widget ? - There is Jade source code common to all instances of the widget. This code defines: a) the named templates that widget instances' building blocks may use b) the mixins defining the building blocks c) CSS include directives [not sure yet - it may be easier to let users add the link rel themselves] - There is Stylus source code, defining the CSS required by the widget instances. - There is JavaScript code: - There may be JS code defining a custom binding. [This will probably not be required for all widgets.] - There may be event handlers (afterRender, afterAdd, beforeRemove) Output file structure --------------------- This is the structure the fully built widget library should have: dist common css style.css javascript gpc-ko-widgets.js html .html css .css javascript .js templates .html .html .html templates.jade ... The top-level DOM element of every widget is assigned two classes, "gpc-ko-widget" plus the name of the widget itself, e.g. "treeview". The common/css/style.css file contains style definitions common to all GPC Knockout widgets. The file gpc-ko-widgets.js exports the object "gpc", which will contain a subobject "ko_widgets". The latter is the namespace object for this library. The .css file contains style rules specific to the widget. The .js file registers the custom binding (if any). It may also add members to the gpc.ko_widgets namespace object. Each of the .html files under "templates" contains a single template that may be used by the widget. Its ID is prefixed with "gpckow--". The special file templates.jade can be included from a Jade source file. It will do the same thing as adding external script tags for each of the .html files under "templates". Javascript code will not be packaged, neither in CommonJS nor in AMD format. I'd like to make shimming as straightforward as possible though. About the development process ----------------------------- There shall be a test page (or sub-page) for each widget, with at least one test case each. More sophisticated testing (Karma?) may be added later. Building will be done using Gulp (initially - maybe something better will turn up later).