Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- module darklight.jbmain;
- import std.stdio;
- import std.algorithm : splitter;
- import std.process : environment;
- //darklight base module and some utilities
- import darklight.controls;
- import darklight.sql;
- import darklight.document;
- import darklight.parse;
- //These two could be public imports in darklight.controls I think, gave you the option to leave them out though
- import darklight.stackpanel;
- import darklight.tab;
- //Creates this module's control factory - this allows for dependency injections
- //(Grid containing LeftPanel for example, even though that would otherwise be a circular reference)
- mixin FactoryGen!("darklight.jbmain",
- "jbm:darklight.jbmain;", //defines the alias jbm for darklight.jbmain
- "LeftPanel:jbm.LeftPanel;
- ContentPanel:jbm.ContentPanel;", //aliases this module's Controls for easier usage
- darklight.controls); //imports the aliases defined in darklight.controls
- //nothing is stopping these ViewModels from being classes, in fact it's probably better if they are
- struct ContentTab
- {
- string title;
- string content;
- }
- struct MainVM
- {
- ContentTab[] tabs;
- }
- //xaml to be sent to the factory
- //Doesn't need it's own Control class since it's top-level, but could have one if you wanted
- //1fr in the ColumnDefinition is the equivalent to 1* in xaml, this is css3 grid syntax
- //css3 grids are emulated in js for this since the only browser to support them only runs on windows 8
- enum string _mainPanel =
- "<Grid width={{`100%`}} height={{`100%`}}>
- <GridDefinition>
- <RowDefinition height={{`100%`}}/>
- <ColumnDefinition width={{`400px`}}/>
- <ColumnDefinition width={{`1fr`}}/>
- <ColumnDefinition width={{`500px`}}/>
- </GridDefinition>
- <LeftPanel xname={{`Lefty`}}
- grid.column={{1}}/>
- <ContentPanel grid.column={{2}}/>
- <StackPanel xname={{`RightStack`}}
- grid.column={{3}}>
- <TextBlock xname={{`CompanyHeader`}}
- text={{`Darklight corporation (not a real corporation)`}}/>
- </StackPanel>
- </Grid>";
- //This is the left panel on the site, contains a form, with a textbox and button, and a textblock
- //The targetElement/replace functionality of the form is contrived, to show ajax passing can be done but what it does with it sucks
- class LeftPanel : StackPanel
- {
- enum string _xmlstring =
- "<StackPanel>
- <Form xname={{`form1`}}
- url={{`jbmain.dl`}}
- targetElement={{`placeholder1`}}>
- <TextBox xname={{`inputText1`}} text={{`default string`}}/>
- <Button text={{`Button!!`}}/>
- </Form>
- <TextBlock xname={{`placeholder1`}}/>
- </StackPanel>";
- string content()
- {
- //The html generated will have a string concatenated to it, it's non-breaking in this example but be careful to still return valid xhtml
- return super.content() ~ "I snuck in a string!";
- }
- //To make this be a Control with it's own xaml - This performs code generation that extracts bindings and sub-elements
- mixin Control.extractgen!(_xmlstring);
- }
- class ContentPanel : TabMenu
- {
- //The first exciting piece of xaml, it has bindings!
- //viewModel is the VM that can be bound
- //in the TextBlock case, this.text=viewModel.title will get added during compile-time to the code that generates the content, so that at run-time the text will be set according to the viewModel
- //binding=viewModel.tabs is a ListPanel feature, ListPanel repeats the inner xaml content for all elements in a provided range (not range as in 1-10, range as in array datatype)
- //the controls inside the ListPanel from that point on will now have their viewModels each representing one of the tabs. So the ViewModel for the ListPanel is not the same as the one for the TabPage
- //binding is a variable that can be assigned to, that will set the viewModel of controls nested in a given control
- //In this case there is additional behaviour in that inner controls only get an element of binding instead of the whole array, but that is a feature of the ListPanel.
- //the overflow Control just defines expander behaviour, int this case a scroll-bar would appear if the content got too large
- //The TextBlock inside the Overflow has the same ViewModel depth as the other one. That is, their viewModels are the same ContentTab.
- enum string _xmlstring =
- "<TabMenu xname={{`ContentTabs`}}>
- <ListPanel binding={{viewModel.tabs}}>
- <TabPage>
- <TextBlock text={{viewModel.title}}/>
- <Overflow behaviour={{Behaviour.automatic}}>
- <TextBlock text={{viewModel.content}}/>
- </Overflow>
- </TabPage>
- </ListPanel>
- </TabMenu>";
- mixin Control.extractgen!(_xmlstring);
- }
- void main(string[] args)
- {
- //stupid handler for ajax calls
- //eventually I'll flesh this out into full rpc's using mixin templates to make code generation automatic
- if (environment.get("REQUEST_METHOD", "GET") == "POST")
- {
- handleAjax();
- return;
- }
- MainVM mvm;
- auto db = db_open("db1.s3db"); //to ppoulate the vm's tabs
- record[] records = db_execute(db, "SELECT * FROM table1");
- db_close(db);
- foreach(rec; records)
- {
- ContentTab tab;
- tab.title=rec.data["id"];
- tab.content=rec.data["name"];
- mvm.tabs ~= tab;
- }
- auto control = darklight_jbmainFactory.BuildControl!(_mainPanel)(mvm, mvm);
- Document doc = new Document("My first webpage", "cgi-bin/", control);
- doc.filenames = "ut/site1";
- writeln(doc.content);
- doc.prepare();
- }
- void handleAjax()
- {
- //This method just returns the environment variables from the POST call, removing the first one which is a darklight message
- writeln("Content-type: text/plain;\n\n");
- string reqs = environment.get("QUERY_STRING","");
- auto reqargs = splitter(reqs, "&");
- reqargs.popFront();
- string[string] vals;
- foreach(req; reqargs)
- {
- auto split = splitter(req, "=");
- vals[split.front] = split.back;
- }
- writeln("request received : ", vals);
- return;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement