view release on metacpan or search on metacpan
- dhandler.mc: modifications to enable display of main page when session is
authorized; add debug messages
- login_dialog.mi: call MFILE.main() upon successful auth
0.021 2014-08-18 17:02 CEST
- WWW.pm: edit POD to get rid of 'Use of uninitialized value $_ in pattern
match (m//)' error
- lots of other changes (WIP)
0.022 2014-08-18 22:19 CEST
- config/mainMenu_Config.pm: add config file for runtime generation of
the main menu
- add mainLogout.{mi,js}
- rename main_menu.{mi,js} -> mainMenu.{mi,js}
- rename login_dialog.{mi,js} -> loginDialog.{mi,js}
- Resource.pm: move init_session function to WWW.pm
- Form.pm: fix logic; add mainMenu and setSessionTarget targets
- js/mainMenu.pm: add mainMenuFormSubmit function with AJAX call
- status: login dialog works, dynamically-generated main menu is displayed,
mainLogout works
- NEXT: cache Mason-generated source in the JavaScript top-level object so we
can switch between forms/targets without triggering a page reload
0.023 2014-08-19 14:16 CEST
- WWW.pm: write POD
- Form.pm: add a debug message
- Resource.pm: add debug messages; don't push session object onto context;
refactor is_authorized
0.024 2014-08-19 16:59 CEST
- attempt to cache subcomponent output in JavaScript object (failing so far
due to quoting issues)
0.025 2014-08-19 23:37 CEST
- IMPLEMENT SUBCOMPONENT CACHING
- WWW.pm: export a $tcache singleton
- Form.pm: import $tcache singleton; fix mainLogout function; make
mainMenu function return cached content if available; convert
setSessionTarget into getTargetCache
- dhandler.mc: import $tcache singleton; get rid of previous caching attempt;
use content from $tcache if available so subcomponents are not called
more than once
- mainMenu.js: add JS code to check the tcache and display the subcomponent source directly if
available in MFILE.tcache object (not tested -- need to implement at least two subcomponents
other than "mainLogout")
- Resource.pm: simplify _render_response_html; cleanup
- mainLogout.mi: remove <%perl> block -- these can no longer be used in subcomponents
unless it's OK for them to run only once
- mainLogout.js: add AJAX call to send 'mainLogout' form submit
- status: caching implemented but only sketchily tested, and no testing yet on
the JavaScript side
0.026 2014-08-20 16:19 CEST
- start implementing submenus
0.027 2014-08-21 09:44 CEST
- rename mainMenu.js to menu.js as we will use a single JS file for all
menus/submenus
- WWW.pm: add 'generate_menu' function called from dhandler.mc
- Form.pm: add generalized '_processMenuSelection' function and call it
from the form-submit function of each menu/submenu
- menu.js: refactor for multiple menus/submenus - kludgey, but it works
0.028 2014-08-21 10:53 CEST
- WWW.pm: 'generate_menu' now generates entire form, not just the entries
- mainMenu.mi, mainEmployee.mi: remove duplicated code
- menu.js: mitigate kludginess
0.029 2014-08-21 11:24 CEST
- Form.pm: make menu selections case insensitive
- mainLogout.mi: display a goodbye message for 1 second on logout
- WWW_Config.pm: add DOCHAZKA_WWW_LOGOUT_MESSAGE site param
- mainMenu_Config.pm: rename to menu_Config.pm, change logout selector from
'10' to 'X'
- MANIFEST: menu_Config.pm was missing - add it
- mainLogout.js: wait one second before reloading the page
0.030 2014-08-21 11:53 CEST
- WWW.pm, bin/dochazka-www: write log messages to file defined in site
param instead of relying on App::CELL::Test::LogToFile, which creates a
temporary file each time
- WWW_Config.pm: add DOCHAZKA_WWW_LOG_FILE and DOCHAZKA_WWW_LOG_FILE_REST
params
target function
- empProfile.mi: improve HTML, uncomment JS handler call
- menu_Config.pm: add 'loadCurrentEmployee' to DOCHAZKA_WWW_FORMS
- empProfile.js: add some debug messages
- current state of empProfile implementation: HTML form is displayed,
AJAX call gets current employee object and values from it appear in
the form; single-line minimenu is displayed; NEXT: enable user to make a
selection from the minimenu and process that selection
0.034 2014-08-22 10:56 CEST
- Form.pm: cleanup; comment out some debug messages; add empProfileMiniMenu
- menu_Config.pm: add DOCHAZKA_WWW_EMPPROFILE_MINIMENU
- empProfile.js: add call to MFILE.menu( 'empProfileMiniMenu' ) and started
to debug why it doesn't work as expected
0.035 2014-08-22 11:15 CEST
- fix bug "empProfileMiniMenu submits the wrong form, breaking the
application"
- dhandler.mc: refrain from setting MFILE.target
- menu_Config.pm: add missing 'empProfileMiniMenu' form to
DOCHAZKA_WWW_FORMS
- menu.js: move misplaced console.log statement to right place
0.036 2014-08-22 16:44 CEST
- embark on major refactor after realizing that the current model
of treating everything (including menu selections) as a "form submit"
is bogus -- WIP
0.037 2014-08-23 09:06 CEST
- continue stabilizing the application and converting Mason components
to JavaScript -- the goal is to remove Mason completely. Mason is
nice, but it makes sense to have it only when the application consists
of a number of different, complex pages and page reloads are used to
switch between them. This application consists of very simple HTML
that can easily be stored in JavaScript variables and keeping all the
navigation on the JavaScript side eliminates page reloads and makes
working with the application much faster. Also, conceptually it is
easier to understand because AJAX calls are used only to communicate
with the REST server.
- js/: add 03empProfile.js; rename 03simpleMenu.js to 04simpleMenu.js;
work on both
0.038 2014-08-24 20:34 CEST
- eliminate obsolete Form.pm module
- WWW.pm: write some POD
- Resource.pm: instead of representing the current employee with three separate
session attributes, do it with a single 'currentEmployee' hashref containing
the entire employee object; now that Form.pm is gone, we have to do
login/logout POST handling here -- add the necessary code
- dhandler.mc: use 'currentEmployee' session attribute
0.043 2014-08-25 12:39 CEST
- js/: rearrange code
0.044 2014-08-25 16:11 CEST
- implement dynamic generation of "simple forms"
- simple forms consist of a title, a set of read-only span elements,
a set of read/write input elements, and a minimenu
- each form entry/element has an ACL profile
- individual menuItems also have ACL profile
- miniMenu logic autodetects presence of additional input elements above
'sel' and enables user to use TAB and SHIFT-TAB to navigate the entire
form
- ready to implement empProfileEditSave
0.045 2014-08-25 20:26 CEST
- implementing empProfileEditSave (WIP)
0.046 2014-08-25 22:13 CEST
- js/: start refactoring to take advantage of prototypes and methods
don't refer to objects that don't yet exist; hopefully this change
will streamline the code and facilitate addition of additional targets
- HTML.pm: get names of JavaScript code files from site configuration
parameter, instead of having them hardcoded
- status: login, logout, and simple menus are working again; rest is broken
0.048 2014-08-26 14:49 CEST
- MANIFEST: update JavaScript source files
- 002lib.js: expand MFILE.lib.AJAX to take success and failure callbacks
- js/: change all MFILE.lib.AJAX calls to send success and failure callbacsk
- 011simpleMenu2.js: use anonymous function call to loop over all simple
menus
- status: ready to re-implement simple forms
0.049 2014-08-26 15:52 CEST
- WIP refactoring JavaScript code for the new 'target' prototype
and to use object references wherever possible
0.050 2014-08-27 16:18 CEST
- ironing out bugs in the JavaScript code
- remove 'priv' from empProfile and empProfileEdit, as it is not a property
of the employee data model object and it's presence here might confuse users
0.052 2014-08-27 22:31 CEST
- add 'new employee' simple form
- 013simpleForm2.js: fix some minor issues in makeSimpleFormSource function
0.053 2014-08-28 10:03 CEST
- 006simpleForm1.js: add 'dispEmployee' target; add preamble text to 'empProfileEdit' target;
Nick is a required field, so it gets a star
- 011simpleMenu2.js: clear '#result' before displaying the menu
- 013simpleForm2.js: text of Back menu entry is now configurable; add 'dispEmployee' target;
makeSimpleFormSource display preamble; menu selection defaults to 'X' (back) when there
are no other menu entries
- 110empProfile.js: add 'dispEmployee' target; newEmployee target now calls 'dispEmployee'
upon successful insert
0.054 2014-08-28 16:16 CEST
- WWW/Resource.pm: trying to tame JSON encoding/decoding
- js/: start working on employee search/browse
- status: implemented new 'Search employee' menu item, simple form for
- 100login.js: refactor MFILE.loginDialogFormSubmit, improve error message
displayed on login failure
- 110empProfile.js: refactor MFILE.directAction.newEmployee.start so it
properly validates the nick before attempting the insert, which is
implemented by calling the new 'insertEmployee' dA target
0.060 2014-08-29 22:36 CEST
- implement "Masquerade as different employee" feature
- js/: add 'masqEmployee' target; rename 003targets.js to 003prototypes.js
after adding MFILE.empProfile prototype to it; minor cleanup
- 011simpleMenu2.pm: check MFILE.currentEmployee's heritage each time we enter
a menu, and fix it if necessary (it loses its heritage on each page reload,
but each page reload takes us to the main menu, where we restore the heritage)
- HTML.pm: give 'userbox' an id so we can refer to it
- test and debug MFILE.empProfile.sanitize function, now using it to ensure
that employee objects don't have illegal properties when sending them to
the REST server
0.061 2014-09-01 07:49 CEST
- Unicode hell
0.062 2014-09-02 17:57 CEST
- Unicode hell
- js/: split JS source files into 'mfile' and 'dochazka' trees -- 'mfile'
is the "framework" and 'dochazka' is our "application" (preparation for
Hackweek 11), minor cleanup
- HTML.pm, config/WWW_Config.pm: instead of having all the JS source code files
in a single array ref, each source file now has its own site param
0.063 2014-09-02 18:42 CEST
- js/: js/mfile/004simpleMenu1.js contains "general" code, while the "real"
simple menus are defined in js/dochazka/004simpleMenu1.js, which overwrites
the MFILE.simpleMenu object; eliminate MFILE.listOfAllSimpleMenus array since
this information can easily be derived from the MFILE.simpleMenu object
0.064 2014-09-03 12:21 CEST
- js/: separating general ('MFILE') from specific ('DOCHAZKA') code,
implementing privilege history submenu (WIP)
0.065 2014-09-03 13:58 CEST
- Resource.pm, WWW_Config.pm: implement DOCHAZKA_WWW_BYPASS_LOGIN_DIALOG
site configuration parameter so we don't have to type in credentials
a gazillion times a day
0.068 2014-09-03 22:44 CEST
- Resource.pm: fix bug ('ip_addr' and 'last_seen' not getting set properly
following addition of DOCHAZKA_WWW_BYPASS_LOGIN_DIALOG); add more
debug messages
- js/: move some code to mfile/002lib.js, move more Dochazka-specific code
from mfile/ to dochazka/
0.069 2014-09-04 11:12 CEST
- HTML.pm, config/WWW_Config.pm: fix bug (DOCHAZKA_WWW_JS_PRIVHISTORY was not
defined and hence not being loaded at runtime)
- dochazka/004simpleMenu1.js: generate 'start' method of objects in
MFILE.simpleMenu using a loop, instead of assigning them manually
- {mfile,dochazka}/013simpleForm2: move Dochazka-specific code to
the right place
- 120privHistory.js: fix bug (Dochazka-specific code in wrong place)
- 120privHistory.js: fix bug (wrong concatenation operator)
- mfile/002lib.js: fix bug (new MFILE.lib.hairCut method was broken)
0.070 2014-09-04 20:39 CEST
- js/mfile/003prototypes.js: fix bug in 'sanitize' method (missing return)
- js/dochazka/110empProfile.js: fix bug in MFILE.directAction.empProfileUpdate.start
(failure callback was not displaying server error message)
to by this general name ('app')
0.076 2014-09-09 17:51 CEST
- hack like mad to get a menu to appear (still no joy)
0.077 2014-09-10 09:39 CEST
- js/: cleanup, delete deprecated code
- js/current-user.js: include 'obj' and 'priv' properties in module object
- js/daction.js: add 'logout' to the set of "core" dactions
- js/dform.js: integrate entries object
- js/dmenu.js: work on introductory comments; add demoForm to demoMenu
- js/lib.js: fix priv_check; add 'entries' and 'back' properties to dmenu
constructor; add 'hookGetObj' property to dform constructor
- js/main.js: start demo menu instead of main menu
0.078 2014-09-10 11:03 CEST
- implement phased initialization of dmenu and dform objects
- dforms now working and can be included in dmenus
- js/dmenu.js: rewrite introductory documentation; move initialization of
'entries' and 'back' properties to js/init/
- js/init/dform-source-start.js: rename from init/dform; adapt to
0.110 2014-09-21 00:33 CEST
- bin/mfile-www, Resource.pm: add code to get the right version string so we
don't display App::MFILE::WWW version in derived distro mode
0.111 2014-09-22 21:02 CEST
- Resource.pm: fix bug "App::MFILE::WWW version number in HTML title, but
in derived distribution mode the version number should be that of the
derived distribution"
0.112 2014-09-24 10:19 CEST
- js/core/dform-source-start.js: fix two bugs relating to how miniMenu length
is determined
0.113 2014-09-24 11:53 CEST
- Resource.pm: fix bug "privilege reverts to passerby on page reload"
0.114 2014-09-24 13:47 CEST
- do the right thing when user hits ENTER at a menu or miniMenu
0.115 2014-09-24 15:27 CEST
- js/: two minor clarifications
0.116 2014-09-25 10:29 CEST
- WWW.pm: massage POD
0.117 2014-09-27 10:53 CEST
- js/: start refactor in preparation for adding new 'browser' widget
- js/: work on dbrowser (progress)
0.122 2014-10-11 14:53 CEST
- lib.js: add 'holdObject' routine
- start.js: add 'dbrowserSubmit' routine; when dbrowsing, use 'lib.holdObject'
to store the current object so we can return to it
- dform-init.js: start work on a 'demoEditFromBrowser' target
0.123 2014-10-11 17:15 CEST
- start.js: add warning about argument passed by dform handler to start
method of item picked from the dform miniMenu; store dbrowser state in a
descriptively-named object; rewrite dbrowser method to enable it to be
called either with an argument ("new" dbrowser) or without (return to
existing dbrowser state)
- daction-{init,start}.js: add 'returnToBrowser' action for returning to
the saved dbrowser state
- dform-init.js: make the 'Back' miniMenu selection in 'demoEditFromBrowser'
call the 'returnToBrowser' target
0.124 2014-10-11 19:34 CEST
- dbrowser-init.js: use lib.holdObject for the hook and store the sample
result set there before initializing the target
0.125 2014-10-11 20:34 CEST
- html.js: add comment; remove useless arguments from 'dbrowser' function
declaration
- lib.js: add comment
- core/ajax.js: display '* * * Communicating with REST server * * *' in the
#result line before each AJAX call
- lib.js: add 'clearResult' library function
- star.js: use 'clearResult' library function
0.131 2014-10-13 16:47 CEST
- js/: CSS tweaks
0.132 2014-10-13 17:53 CEST
- core/lib.js: stop logging every keypress
- core/start.js: move miniMenu keyhandler into its own routine, thereby
eliminating some code duplication and preparing the code for a 'dnotice'
target type
0.133 2014-10-14 08:41 CEST
- js/: implement dnotice feature
0.134 2014-10-14 09:20 CEST
- js/: fix bug "privHistory dnotice: race condition in AJAX call"
0.135 2014-10-14 09:32 CEST
0.147 2016-09-28 22:58 CEST
- dtable boilerplate
- dtable: everything except table rendering
- js: fix ACL profile property name in entry definitions
- build/ops: fix session management
- js: rename charsNeeded() to maxLength() and generalize
- dtable: finalize initial feature implementation
0.148 2016-09-29 00:43 CEST
- html.js: handle null entries prop in miniMenu()
- js: take headings into account when calculating column widths
- js: grammatically correct "Displaying table" message
- js: add readableDate() function to core lib
0.149 2016-09-29 01:09 CEST
- html.js: restore proper padding in dform
- js: make readableDate() take full "YYYY-MM-DD HH:DD:SS+TZ"
0.150 2016-11-03 19:22 CET
- Resource.pm: expose REST server URI to JavaScript side
- Resource.pm: export foreign site params only if defined
- js demo: dtable return to demoSubmenu instead of demoMenu
- js demo: implement a proper hook for dbrowser demo
- html.js: do not concatenate undefined entries
- html.js: use text prop to determine max length
- html.js: implement and use getObjStr()
- start.js: simplify dtable start/listen code
- Implement a drowselect target (widget)
- demo: plumb in the new "drowselect" widget
- demo: implement proper dtable, drowselect hooks
- html.js: initial HTML for drowselect widget
- start.js: add drowselect start and listen functions
- core: implement drowselect reverse video, arrow-key navigation
- html.js: remove potential code duplication (dtable/drowselect)
0.151 2016-11-04 22:02 CET
- demo: storage and accessors for dbrowser data set
- demo: move rowselect demo to submenu
- demo: enable editing in dbrowser/drowselect demos
- core: refactor to eliminate code duplication (and fix a bug)
0.152 2016-11-05 21:18 CET
- core: prevent miniMenu entries from being undefined in start.js
- core: library function for displaying error messages
- core: improve display of empty table/rowselect in html.js
- core: display drowselect navigation only if rows > 1
0.153 2017-02-25 09:30 CET
- Refrain from making a new Plack::Session object
- doc: tweak log messages
- Resource.pm: implement session and session_id methods
- script: store sessions in a dedicated directory in /tmp
- Display session data on all screens
- tests: js: add a test that starts the application
- tests: js: add #mainarea introspection to dmenu test
- tests: js: migrate QUnit from 1.15.0 to 2.4.0
0.159 2017-09-23 23:52 CEST
- js: core: migrate to JQuery 3.2.1
- doc: describe how to add new JavaScript unit tests
- doc: JS unit testing: include QUnit documentation links
- js: core: upgrade to RequireJS 2.3.5
- js: core: strictly separate dmenu submit codepaths
- js: tests: test what happens when 0 is pressed in demoMenu
- js: core: drop passhash and salt from user prototype
- js: root: prevent login dialog from appearing in tests
- Resource.pm: do not set currentUser when testing
- js: core: current-user: handle null values
- js: core: cf, current-user: allow override with null
- js: core: root: reset stack on every invocation
- js: core: stack: implement getStack method
- Refactor login, logout codepaths to allow login/logout from tests
- js: core: rename logout.js to loggout.js
- js: tests: declare QUnit module "mfile-www core"
0.169 2017-10-29 17:57 CET
- js: core/ajax: display failure message with displayError()
- js: core/loggout: propagate result line message
- js: core/html: fix bug in vetEntries()
0.170 2017-10-31 12:00 CET
- js: core/datetime: add tsrangeToDateAndTimeRange()
- js: core/start: vet all writable dform entries when submitting
- js: core/start: populate object regardless of target type
- js: core/start: display opts.resultLine after populate in dform
- js: core/html: make long miniMenus wrap a little less clumsily
- js: core/stack: fix missing underscore in pop()
- js: core/stack: push opts onto stack
- js: core/stack: make unwindTo{Flag,Target} handle opts and respect _start
- js: core/start: make dmenu process opts and respect resultLine
0.171 2017-11-02 20:14 CET
- js: core: implement dcallback widget
- js: core/start: support htmlCallback and populateCallback props in dcallback
- js: core/datetime: implement dateToDay()
- start.css: make boxtopbot have the same background color as mainarea
0.172 2017-11-10 15:17 CET
- js: core/current-user: use jQuery to reset object
- js: core/datetime: implement vetTimeRangeNoOffset
- js: core/lib: implement "nullify" function
- js: core/stack: add ability to unwind to target without starting
0.173 2017-11-11 13:23 CET
- js: core/datetime: improve tab completion of months
- js: core/datetime: fix strToMonth() regression
- js: core/populate: do not clobber result line
- css, js/html: implement proper miniMenu indenting
- js: core: html/start: make menu entries start at 1
0.174 2017-11-13 11:43 CET
- Revert "Integrate D3 into the application"
- js: core/html: fix dmenu regression
- js: core/html: give minimenu div an id
- js: reflect appLib.fillUserBox() behavior change
0.175 2017-11-14 17:24 CET
- js: core/start: pass resultLine to drowselectListen()
- js: core: implement "menu" prototype
- js: core/populate: handle undefined populateArray a little less stupidly
- js: core/init2: do not clobber miniMenu objects
0.176 2017-11-27 11:22 CET
- js: core/html: unify usage of " " in dmenu and miniMenu; populate
mainarea with a placeholder
- build/ops: drop older versions of jquery, qunit, requirejs
- js: core/ajax: refactor; logout if session expires; always clear result line
lib/App/MFILE/WWW.pm view on Meta::CPAN
=head2 daction
The C<daction> primitive is a generalized widget that can do anything.
=head2 dbrowser
The C<dbrowser> primitive is like C<dform>, except that it displays a set
of data objects and enables the user to "browse" the dataset using arrow keys.
Like C<dform>, the primitive includes "miniMenu" functionality through which
the user can potentially trigger actions that take the current object as input.
=head2 dcallback
The C<dcallback> primitive is useful for cases when none of the other primitives
are appropriate for displaying a given type of object, and no interactivity is
needed beyond that provided by miniMenu. The C<dcallback> primitive writes the
target title and miniMenu to the screen, along with a "dcallback" div in
between, which it populates by calling the callback function. Since the callback
function part of the target definition, it can be app-specific.
=head2 dform
The C<dform> primitive is used to implement forms consisting of read-only
fields (for viewing data), read-write fields (for entering data), or a
combination of both. A "miniMenu" can be defined, allowing the user to trigger
actions that take the current object as input.
=head2 dmenu
The C<dmenu> primitive is used to implement menus.
=head2 dnotice
The C<dnotice> primitive takes an HTML string and displays it. The same
functionality can be accomplished with a C<daction>, of course, but using the
C<dnotice> primitive ensures that the notice will have the same "look and feel"
as the other widgets.
=head2 drowselect
The C<drowselect> primitive takes an array of strings, displays them vertically
as a list, and allows the user to choose one and perform an action on it. Actions
are defined via a C<miniMenu>. The currently-selected item is displayed in
reverse-video.
=head2 dtable
The C<dtable> primitive is similar to C<dbrowser> in that it takes a set of
objects and allows the user to choose one and perform actions on it via a
C<miniMenu>. Unlike C<dbrowser>, however, it display the objects in table form.
The currently-selected object is displayed in reverse video.
=head1 JAVASCRIPT UNIT TESTS
The JavaScript side has unit tests and functional tests that can be run by
starting the application and then pointing the browser to a URL like:
http://localhost:5001/test
share/js/core/html.js view on Meta::CPAN
'lib',
'app/lib',
'target'
], function (
cf,
coreLib,
appLib,
target
) {
var browserNavMenu = function (len, pos) {
var r = '',
context = {
forward: 0,
back: 0,
jumpToEnd: 0,
jumpToBegin: 0
};
// context-sensitive navigation menu: selections are based on
// set length and cursor position:
// if set.length <= 1: no navigation menu at all
share/js/core/html.js view on Meta::CPAN
r += '<span id="navJumpToBegin">[\u2303\u2190] Jump to first </span>';
}
if (context.jumpToEnd) {
r += '<span id="navJumpToEnd">[\u2303\u2192] Jump to last </span>';
}
r += '<br>';
} else {
r = '';
}
return r;
}, // browserNavMenu
genericTable = function (tname, tobj, targetType) {
return function (set) {
// console.log("Generating source code of " + tname);
// console.log("tobj", tobj);
// console.log("set", set);
var r = '<form id="' + tobj.name + '">',
allEntries,
entry,
share/js/core/html.js view on Meta::CPAN
// Navigation menu (drowselect only)
if (targetType === 'drowselect' && set.length > 1) {
r += 'Navigation: ';
r += '<span id="navBack">[\u2190] Previous </span>';
r += '<span id="navForward">[\u2192] Next </span>';
r += '<span id="navJumpToBegin">[\u2303\u2190] Jump to first </span>';
r += '<span id="navJumpToEnd">[\u2303\u2192] Jump to last </span>';
r += '<br>';
}
// miniMenu at the bottom: selections are target names defined
// in the 'miniMenu' property of the dform object
r += miniMenu(tobj);
// your choice section
r += yourChoice();
r += '</form>';
console.log("Assembled source code for " + tname + " - it has " + r.length + " characters");
return r;
};
}, // genericTable
share/js/core/html.js view on Meta::CPAN
}
if (elem.text.length > prevVal) {
prevVal = elem.text.length;
}
return prevVal;
}, arr[0].text.length);
// console.log("The longest entry is " + max + " characters long");
return max;
}, // maxLength
miniMenu = function (tobj) {
var entries,
entry,
menuText,
mm = tobj.miniMenu.menuObj,
i,
r;
r = "<div class='minimenu' id='minimenu'>";
console.log("Generating miniMenu HTML for target", tobj);
// console.log("menu object is", mm);
if (mm.isEmpty) {
r += "To leave this page, press ENTER or click the Submit button";
} else {
entries = mm.entries;
r += "<div class='minimenuleft'>";
// r += 'Menu: ';
r += 'Menu:';
r += "</div>"; // minimenuleft
r += "<div class='minimenuright'>";
for (i = 1; i < entries.length; i += 1) {
// console.log("i === " + i);
entry = entries[i];
menuText = entry.menuText.replace(/ /g, ' ');
r += i + '. ' + menuText + ' ';
}
r += 'X. Exit/back';
r += '</div>'; // minimenuright
}
r += "</div>"; // minimenu
return r;
}, // miniMenu
valueToDisplay = function (obj, prop, mode) {
// console.log("valueToDisplay with object", obj, "and prop " + prop);
// given an object and a property, return the value to display
if (typeof obj !== 'object') {
if (mode === 'hidden') {
return '';
} else {
return '(NOT_AN_OBJECT)';
}
share/js/core/html.js view on Meta::CPAN
r += '<span id="' + entry.name + '">';
r += valueToDisplay(obj, entry.prop);
r += '</span><br>';
}
}
r += '<br>';
}
// context-sensitive navigation menu: selections are based on
// set length and cursor position
r += browserNavMenu(set.length, pos);
// miniMenu at the bottom: selections are target names defined
// in the 'miniMenu' property of the dbrowser object
r += miniMenu(dbo);
// your choice section
r += yourChoice();
r += '</form>';
// console.log("Assembled source code for " + dbn + " - it has " + r.length + " characters");
return r;
};
}, // dbrowser
share/js/core/html.js view on Meta::CPAN
dcallback: function (dcn) {
// console.log("Entering html.dcallback with argument " + dcn);
// dcn is dcallback name
// dco is dcallback object
var dco = target.pull(dcn),
r = '';
r += '<br><div id="dcallback"></div><br>';
r += '<form id="' + dcn + '">';
// miniMenu at the bottom
r += miniMenu(dco);
// your choice section
r += yourChoice();
r += '</form>';
return r;
}, // dcallback
dform: function (dfn) {
// dfn is dform name
// dfo is dform object
share/js/core/html.js view on Meta::CPAN
r += 'name="entry' + i + '" ';
r += 'value="' + valueToDisplay(obj, entry.prop, "write") + '" ';
r += 'size="' + entry.size + '" ';
r += 'maxlength="' + entry.maxlen + '"><br>';
}
}
if (len > 0) {
r += '<br>';
}
// miniMenu at the bottom
r += miniMenu(dfo);
// your choice section
r += yourChoice();
r += '</form>';
// console.log("Assembled source code for " + dfn + " - it has " + r.length + " characters");
return r;
};
}, // dform
share/js/core/init2.js view on Meta::CPAN
'start',
'target'
], function (
html,
coreLib,
prototypes,
start,
target
) {
var transformMenu = function (menu, type) {
// transform array of strings into a menu object
var entry,
i,
newMenu;
if (! coreLib.isArray(menu)) {
throw("CRITICAL ERROR: non-array sent to transformMenu");
}
newMenu = Object.create(prototypes.menu);
newMenu.entries = [ null, ]; // 0th menu entry is not used
for (i = 0; i < menu.length; i += 1) {
entry = target.pull(menu[i]);
if (coreLib.privCheck(entry.aclProfile) && entry.onlyWhen()) {
newMenu.entries.push(entry);
}
}
newMenu.isEmpty = (menu.length === 0);
newMenu.isDmenu = (type === 'dmenu');
newMenu.isMiniMenu = (type === 'miniMenu');
// return transformed array of target objects
// console.log("Transformed menu into", newMenu);
return newMenu;
};
return function (wtype) {
var entry,
i,
tgt,
widgets = target.getAll(wtype);
for (i in widgets ) {
if (widgets.hasOwnProperty(i)) {
tgt = widgets[i];
tgt.start = start[wtype](i);
// adjust dmenu and miniMenu - the idea here is to remove items
// for which the current user does not have sufficient privileges
if (wtype === 'dmenu') {
if (! tgt.menuObj) {
if ('entries' in tgt && coreLib.isArray(tgt.entries)) {
// console.log("Transforming dmenu " + tgt.name);
tgt.menuObj = transformMenu(tgt.entries, "dmenu");
} else {
tgt.menuObj = Object.create(prototypes.menu);
tgt.menuObj.isDmenu = true;
}
}
tgt.source = html[wtype](i);
continue; // dmenus do not have miniMenus
}
// console.log("Considering miniMenu of target", tgt);
if ( tgt.miniMenu &&
'entries' in tgt.miniMenu &&
coreLib.isArray(tgt.miniMenu.entries) &&
! tgt.miniMenu.menuObj
) {
// console.log("Transforming miniMenu of " + tgt.name);
tgt.miniMenu.menuObj = transformMenu(tgt.miniMenu.entries, "miniMenu");
}
tgt.source = html[wtype](i);
}
}
};
});
share/js/core/prototypes.js view on Meta::CPAN
source: '(none)',
start: function () {},
onlyWhen: function () { return true; },
},
// prototype for users ("employees" in App::Dochazka::WWW)
user: {
nick: '',
},
// prototype for menus (dmenu and miniMenu)
menu: {
entries: [],
isDmenu: false,
isMiniMenu: false,
isEmpty: true,
},
};
});
share/js/core/start.js view on Meta::CPAN
dmenuSubmit(dmn);
} else if (event.keyCode === 9) {
event.preventDefault();
} else if (event.keyCode === 27) {
stack.pop();
} // end of function - don't put anything after this line
};
},
//
// miniMenu handlers
//
mmVetEntry = function (n, entryName) {
var input = $("input[id='" + entryName + "']"),
vetted = true,
vettedVal,
vetter;
console.log("In writable entry " + entryName);
vetter = currentTarget.getVetter(entryName);
// console.log("vetter", vetter);
if (typeof vetter === 'function') {
share/js/core/start.js view on Meta::CPAN
coreLib.clearResult();
var sel = $('input[name="sel"]').val(),
len,
i,
newObj,
selection,
formEntry,
menuEntry,
menuObj = currentTarget.miniMenu.menuObj;
if (menuObj.isEmpty) {
console.log("Setting sel to 'X' by default because miniMenu has no entries");
sel = 'X';
len = 0;
} else {
len = menuObj.entries.length;
}
if (len > 0 && sel === '') {
console.log("User hit ENTER ambiguously; doing nothing");
return;
}
share/js/core/start.js view on Meta::CPAN
},
dbrowserKeyListener = function () {
var set = coreLib.dbrowserState.set,
pos = coreLib.dbrowserState.pos;
return function (evt) {
coreLib.logKeyDown(evt);
// since the dbrowser has (may have) a navigation menu, we
// check first for those keys before moving to miniMenu handler
if (evt.keyCode === 37) { // <-
if (evt.ctrlKey) {
console.log('Listener detected CTRL-\u2190 keydown');
if ($("#navJumpToBegin").length) {
coreLib.dbrowserState.pos = 0;
dbrowserListen();
}
} else {
console.log('Listener detected \u2190 keydown');
if ($("#navBack").length) {
share/js/core/start.js view on Meta::CPAN
},
//
// drowselect handlers
//
drowselectSubmit = function () {
var drso = coreLib.drowselectState.obj,
set = coreLib.drowselectState.set,
pos = coreLib.drowselectState.pos,
xtgt;
if (drso.hasOwnProperty('miniMenu')) {
if (drso.miniMenu.hasOwnProperty('entries')) {
if (drso.miniMenu.entries.length > 0) {
mmSubmit(set[pos]);
return;
}
}
}
if (drso.hasOwnProperty('submitAction')) {
stack.push(drso.submitAction, set[pos]);
} else {
// no miniMenu, no submitAction - just go back
xtgt = stack.getXTarget();
if (typeof xtgt === "string") {
stack.unwindToTarget(xtgt);
} else {
stack.pop();
}
}
},
drowselectKeyListener = function () {
var set = coreLib.drowselectState.set,
share/js/mfile-www/daction-init.js view on Meta::CPAN
target,
dactionStart
) {
return function () {
//
// demo dactions that can safely be deleted after making sure they
// aren't mentioned anywhere in your app-specific code
//
target.push('demoActionFromMenu', {
'name': 'demoActionFromMenu',
'type': 'daction',
'menuText': 'Do something from main menu',
'aclProfile': 'passerby',
'start': dactionStart('demoActionFromMenu'),
'pushable': false
}),
target.push('demoNoticeAction', {
'name': 'demoNoticeAction',
'type': 'daction',
'menuText': 'Demo notice',
'aclProfile': 'passerby',
'start': dactionStart('demoNoticeAction'),
'pushable': false
}),
share/js/mfile-www/daction-start.js view on Meta::CPAN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// app/daction-start.js
//
// daction 'start' method definitions
//
// shorter daction functions can be included directly (see, e.g.,
// 'demoActionFromMenu' below); longer ones should be placed in their own
// module and brought in as a dependency (see, e.g. 'logout' below)
//
"use strict";
define ([
"jquery",
"lib",
"logout",
"stack",
"start",
share/js/mfile-www/daction-start.js view on Meta::CPAN
], function (
$,
lib,
logout,
stack,
start,
target
) {
var act = {
"demoActionFromMenu": function () {
// not pushable
console.log("Entering demoActionFromMenu");
$('#mainarea').html(
'<br><br>SAMPLE ACTION - SOMETHING IS HAPPENING<br><br><br>'
);
setTimeout(stack.getTarget().start, 1500);
},
"demoNoticeAction": function () {
stack.push(
target.pull('demoNotice'),
'Random number of the day ' + Math.random() + ' WOW!'
);
share/js/mfile-www/dbrowser-init.js view on Meta::CPAN
// push dbrowser object definitions onto 'target' here
//
target.push('demoBrowser', {
'name': 'demoBrowser',
'type': 'dbrowser',
'menuText': 'Demonstrate browser',
'title': 'Demo browser',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'entriesRead': [ entries.browserEntry1, entries.browserEntry2 ],
'miniMenu': {
entries: ['demoEditFromBrowser']
}
});
};
});
share/js/mfile-www/dform-init.js view on Meta::CPAN
//
target.push('demoForm', {
'name': 'demoForm',
'type': 'dform',
'menuText': 'Demonstrate simple forms',
'title': 'Demo form',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'entriesRead': [ entries.ROFormEntry1 ],
'entriesWrite': [ entries.RWFormEntry1 ],
'miniMenu': {
entries: ['demoActionFromForm']
}
});
target.push('demoEditFromBrowser', {
'name': 'demoEditFromBrowser',
'type': 'dform',
'menuText': 'Edit',
'title': 'Demo edit from browser',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'entriesRead': null,
'entriesWrite': [entries.RWprop1, entries.RWprop2],
'miniMenu': {
entries: ['saveToBrowser']
}
});
target.push('demoEditFromRowselect', {
'name': 'demoEditFromRowselect',
'type': 'dform',
'menuText': 'Edit',
'title': 'Demo edit from rowselect',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'entriesWrite': [entries.RWprop1, entries.RWprop2],
'miniMenu': {
entries: ['saveToRowselect']
}
});
};
});
share/js/mfile-www/dmenu-init.js view on Meta::CPAN
"use strict";
define ([
'target'
], function (
target
) {
return function () {
target.push('demoMenu', {
'name': 'demoMenu',
'type': 'dmenu',
'menuText': 'Demo menu',
'title': 'Demo menu',
'aclProfile': 'passerby',
'entries': ['demoActionFromMenu', 'demoNoticeAction', 'demoSubmenu']
});
target.push('demoSubmenu', {
'name': 'demoSubmenu',
'type': 'dmenu',
'menuText': 'Demo submenu',
'title': 'Demo submenu',
'aclProfile': 'passerby',
'entries': ['demoActionFromSubmenu', 'demoForm', 'demoBrowserAction',
'demoTableAction', 'demoRowselectAction']
share/js/mfile-www/dnotice-init.js view on Meta::CPAN
return function () {
target.push('demoNotice', {
'name': 'demoNotice',
'type': 'dnotice',
'menuText': 'Demo notice',
'title': 'Demo notice',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'back': 'demoMenu'
});
};
});
share/js/mfile-www/drowselect-init.js view on Meta::CPAN
// push drowselect object definitions onto 'target' here
//
target.push('demoRowselect', {
'name': 'demoRowselect',
'type': 'drowselect',
'menuText': 'Demonstrate rowselect',
'title': 'Demo rowselect',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'entriesRead': [ entries.rowselectEntry1, entries.rowselectEntry2 ],
'miniMenu': {
entries: ['demoEditFromRowselect']
}
});
};
});
share/js/mfile-www/dtable-init.js view on Meta::CPAN
return function () {
target.push('demoTable', {
'name': 'demoTable',
'type': 'dtable',
'menuText': 'Demo table',
'title': 'Demo table',
'preamble': 'This is just an illustration',
'aclProfile': 'passerby',
'entriesRead': [ entries.tableEntry1, entries.tableEntry2 ],
'miniMenu': {
entries: ['demoActionFromTable']
}
});
};
});
share/js/mfile-www/target-init.js view on Meta::CPAN
// round two - add 'source' and 'start' properties
// (widget targets only)
initRoundTwo('dform');
initRoundTwo('dmenu');
initRoundTwo('dbrowser');
initRoundTwo('dnotice');
initRoundTwo('dtable');
initRoundTwo('drowselect');
// fire up the main menu
stack.push('demoMenu');
};
});
share/js/mfile-www/tests/demo-menu.js view on Meta::CPAN
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// app/tests/demo-menu.js
//
// tests exercising the "demoMenu" component of the standalone demo app
//
"use strict";
define ([
'QUnit',
'jquery',
'current-user',
'login',
'root',
'stack',
share/js/mfile-www/tests/demo-menu.js view on Meta::CPAN
cu;
login({"nam": "root", "pwd": "root"});
setTimeout(function () {
console.log("TEST: post-login tests");
cu = currentUser();
assert.ok(cu, "current user object after login: " + QUnit.dump.parse(cu));
assert.strictEqual(cu.obj.nick, nick, 'we are now ' + nick);
assert.strictEqual(cu.priv, priv, nick + ' has ' + priv + ' privileges');
assert.ok(true, "Starting app in fixture");
root(); // start app in QUnit fixture
stackFunc(assert, 1, 'starting app', 'dmenu', 'demoMenu');
mainareaFormFunc(assert, 'demoMenu');
assert.ok(true, '*** REACHED logged in as ' + nick);
done();
}, 500);
});
test_desc = 'press 0 in main menu';
QUnit.test(test_desc, function (assert) {
console.log('***TEST*** ' + prefix + test_desc);
var done = assert.async(),
sel;