App-MFILE-WWW

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

- 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

Changes  view on Meta::CPAN

  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

Changes  view on Meta::CPAN


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

Changes  view on Meta::CPAN

  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

Changes  view on Meta::CPAN

- 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

Changes  view on Meta::CPAN

- 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

Changes  view on Meta::CPAN

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)

Changes  view on Meta::CPAN

  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

Changes  view on Meta::CPAN

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

Changes  view on Meta::CPAN

- 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

Changes  view on Meta::CPAN

- 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

Changes  view on Meta::CPAN


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

Changes  view on Meta::CPAN

  - 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

Changes  view on Meta::CPAN

- 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"

Changes  view on Meta::CPAN

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

Changes  view on Meta::CPAN

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 "&nbsp;" 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:&nbsp;&nbsp;';
                    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:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
                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, '&nbsp;');
                    r += i + '.&nbsp' + menuText + '&nbsp; ';
                }
                r += 'X.&nbspExit/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;



( run in 2.062 seconds using v1.01-cache-2.11-cpan-49f99fa48dc )