Start of menu

 

End of menu
PlannerFw Exec Processor, Postprocessor pfComponent PlannerFw Dev Preprocessor DeployBuilder

PlannerFw URLs and related variable names

 

PlannerFw uses Page Processor (/app/index.html, /app/be-index.html) to retrieve model and template then generate result page, there are two ways to access the Page Processor: client request for Page Processor and server directly output the Page Processor, URL that client request for the Page Processor include

Some URL for default models and default templates:

scheme://host[:port]/app
scheme://host[:port]/app/index.html
scheme://host[:port]/app/index.html?tml=/template
scheme://host[:port]/app/index.html?tml=/layout
scheme://host[:port]/app/index.html?mdl=/
scheme://host[:port]/app/index.html?xmdl=/

URL for models and templates or layouts:

scheme://host[:port]/app/index.html?tml=/path[?query]&mdl=/path[?query]
scheme://host[:port]/app/index.html?tml=/path[?query]&mdl=/path?method=get|post|put|delete
scheme://host[:port]/app/index.html?tml=/path[?query]&mdl=ws[s]://.../path[?query]
scheme://host[:port]/app/index.html?tml=/path[?query]&xmdl=/path[?query]
scheme://host[:port]/app/index.html?tml=/path[?query]&xmdl=/path?method=get|post|put|delete
scheme://host[:port]/app/index.html?tml=/path[?query]&xmdl=ws[s]://.../path[?query]

tml for template or layout, mdl for JSON data model, xmdl for XML data model, value of tml, mdl and xmdl can include character "&", "?" and "/", ... in URL, default PlannerFw's WebSocket Port is 8060. template and layout must use absolute path that start from website root. Position of tml in query string is prior to mdl and xmdl. Template and layout must use absolute path that starts from website root.

File extension of template and layout depends on web server, it could be js, php, jsp, asp and others, extension of the Page Processor could be php, jsp, asp and others except html

PlannerFw requires to access models and templates from same host of the Page Processor

Web servers output following JavaScript variables for Page Processor

JavaScript variableValue
pftmlURL to template or layout
pfmdlURL for JSON data model
pfxmdlURL for XML data model

PlannerFw does not output model and template in one same response for keeping model and template separation.

 

PlannerFw modules

 

A module is a group of templates, layouts, PFCSS that make up similar data presentation to same PlannerFw model, PlannerFw's module structures include:

Path structures from document root Comments
/template/module/submodule/raw/[subdirectory/]HTML-filesRaw page templates include HTML scripts and PlannerFw tags
/template/module/submodule/[subdirectory/]js-filesJavaScript files that are compiled from raw templates
/template/module/submodule/section/[subdirectory/]HTML-filesRaw section templates that are content sections of layout templates
/layout/module/submodule/raw/[subdirectory/]HTML-filesRaw layout template has to include one planner.section method at least
/layout/module/submodule/[subdirectory/]js-filesJavaScript files that are compiled from layout templates and section templates
/css/pfcss/module/submodule/raw/[subdirectory/]pfcss-filesPFCSS files include CSS and PlannerFw tags, extensions of PFCSS files include "pfcss" and "css"
/css/pfcss/module/submodule/[subdirectory/]css-filescss files that are compiled from PFCSS files
/directory-under-root/module/[submodule/][subdirectory/]filesstatic pages, images, media files and other

 

[subdirectory/] in path is optional, default PlannerFw's module name is 'pfm', you can create new modules or just update default module for your website/web application. Naming submodule with "a", "b", "c", .., "a1", "b1", "c1", ..., "a2", "b2", "c2", .... as PlannerFw's naming conventions.

"directory-under-root" is a directory name under website root, it could be "js", "images", "static", "media" or other name, [submodule/] in this structure is optional, it will store the files related to the modules in template, layout and PFCSS.

PlannerFw's Preprocessor can watch file size in siteroot/template, siteroot/layout and siteroot/css/pfcss on development server, automatic compile updated raw files into JavaScript or CSS files.

Extensions of raw templates, section templates and layout templates include "html", "htm", "xhtml", "xhtm", "xml" and "rss".

When PlannerFw Dev of PlannerFw Enterprise Edition creates product deployment from development server, raw directories in path structures would be bypassed.

 

PlannerFw Model members

 

Model in PlannerFw is a group of dynamic data that is created by web server for template to generated result web page, model format is either JSON or XML, model data members include required members and optional members two parts, required data members for JSON data model include:

NameValue
expiration0 or 1, default is 0. If value is 1, PlanenrFw would output error message and stop all processes.
pfDataSetDynamic data for template and layout template. If model is for layout template, pfDataSet immediate members, for example, pfSet1, pfSet2, pfSet3,... would be transferred to each section templates through PlannerFw section API in a layout template.

 

Optional data members for JSON data model include:

NameValue
descriptionDescription text of model
modelIdAn unique identity of current model
jwtClaim0 or 1, default value 0 represents no JWT (JSON Web Token) for model access
jwtValue of JWT(JSON Web Token)
errorLevel0 to 3 for Uncatchable error, Other catchable error, Catchable warning and Catchable information respectively
errorMessageRelated message with errorLevel
versionA number
categoryCategory that model belongs to
authorModel author
companyCompany name
copyrightCopyright declaration
licensesLicenses declaration

 

If model format is XML, the model must have one root element, XML prolog and comments could be neglected, model member name would be elements names, all XML elements must be closed, and XML element without a value could be expressed as <element_name></element_name>or <element_name />, replacing characters: < > & ' " with HTML entity in XML elements if applicable.

 

Neglected directory and files by deployment builder

 

Preprocessor server recursively monitor all files under siteroot/template, siteroot/layout and siteroot/css/pfcss in real time, any file changed under them will trigger a series of processes, deployment builder can create deployment or update archives for production server from siteroot/layout, siteroot/template and siteroot/css/pfcss but neglect all raw subdirectories and files with extension html, htm, xhtml, xml and pfcss under them, so try to save files that you don't want to add to result archives in raw directories and subdirectories.

 

PlannerFw Cookies

 

PlannerFw uses session Cookie pf_auth_token to save JWT(JSON Web Token) for user authentication and model access over HTTP/HTTPS or WebSocket/WebSocket Security. New JWT would be transmitted by model to client then wrote to Cookie pf_auth_token, an example of pf_auth_token value:


     eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
     eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
     TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
		

PlannerFw uses session Cookie pf_times to save processes time consumed from request start to page rendering completion on client. For example, 1.35.9.54.6 is a value of pf_times

PlannerFw uses session Cookie pfDataKey in short lifetime to transfer encryption password then immediately delete it after Page Processor received it.

 

Encryption password transmission

PlannerFw requires to transfer password of PlannerFw's encryption from session Cookie pfDataKey in short expiry time in case of delete code failed, for example, expiry time is 2 seconds, if there is no password for PlannerFw's encryption then default password in length of 256 characters would be applied, stronger encryption depends on the length of password, try not to use same password for multiple models.

 

Notations for JSON members

 

PlannerFw uses "__pfenc", "__pfcrc" and "__pfvld" as suffix of JSON member name to trigger data decryption, check checksum and type validation in Page Processor, and remove suffix notations from member names after successfully pass related processes. If there are more than one notations applied to one JSON member at same time, PlannerFw only process last one and neglect others.

 

Keys for CSS contents of Session Storage

 

FCSS uses construction API: importCss(url) and loadCss(url) to save CSS contents to Session Storage to decrease requests and uses Postprocessor API to switch CSS contents in following pages, PFCSS converts CSS URL into key for contents saved to Session Storage, the key starts with "pf_" and followed by a base64 encoded string without +, / and = characters, CSS contents in Session Storage are protected by a 32-bits CRC checksum and a time stamp to against any unauthorized changes.

 

 

pfConfig object

 

JavaScript pfConfig is configuration of Page Processor /app/index.html, its default setting include:


var pfConfig = {
	/**
	 * Set application environment either Development or Production
	 * default value is Development
	 * 
	 */
	environment : "Development",
	
	/**
	 * Set current file path started from website root
	 *  
	 */
	processorPath : "/app",
	
	/**
	 * Set site default template URL, when value of tml is one of 
	 * undefined, empty, '/', 'template', '/template', 'template/' or '/template/',  
	 * this variable would be used to retrieve template
	 *
	 */
	defaultTemplate : "/template/pfm/a/index.html.js",
	
	/**
	 * Set site default template layout URL.  When value of tml is
     * one of 'layout', '/layout', 'layout/' or '/layout/',    
	 * this variable would be used to retrieve layout template
	 *
	 */
	defaultLayout : "/layout/pfm/a/index.html.js",
	
	/**
	 * Set site default model for defaultTemplate
	 * When template is not layout and there are not specific 
	 * values for mdl and xmdl, this variable would be applies
	 *
	 */
	defaultModel : "/samples/template-model.json",
	
	/**
	 * Set site default model for defaultLayout
	 * When template is layout and there are not specific 
	 * values for mdl and xmdl, this variable would be applies
	 *
	 */
	defaultLayoutModel : "/samples/layout-model.json",
	
    /**
	 * An server-side file to display error message or to log client errors of production environment
	 * File extension varies depending on web servers 
     *
	 */
    clientErrorHandler: "/errors/php/client-error-handler.php",
    
	/**
	 * WebSocket default port is 8060
	 * Port in WebSocket server-side has to be updated if the value is replaced
	 * 
	 */
	defaultWebsocketPort : 8060,
	
	/**
	 * Set maximum seconds to store key/value pairs in Session Storage
	 * under PlannerFw storing mechanisms
	 *
	 */
	storeMaxAge : 1800,
	
	/**
	 * Set to track elapsed time 
	 *
	 */
	trackExecutedTime : true
}
	

Object pfConfig in Page Processor /app/be-index.html with less properties than object pfConfig in /app/index.html

 

layout.json

 

siteroot/layout/layout.json is layout configuration that maps section templates to raw layout template, section templates are some raw template files that result code would be a part of result page, Preprocessor server of PlannerFw Dev could convert section templates raw layout template to JavaScript functions as long as file size of section templates is changed, and section API applied in layout template would assign functions calling in layout template.

Example of layout.json

{
  "name": "Layout configuration",
  "description": [
      " This file describes how Preprocessor mapping raw-template files to raw-layout template  ",
      " file, any raw-template file changed would be leaded to layout template regenerated      ",
      " automatically.                                                                          ",
      " Name in value object of layoutMapping is a raw-layout template file and value is an     ",
      " array with at least two raw-template files as its elements, all path in this file begin ",
      " from website root, Windows directory separator has to be escaped, for example,          ",   
      " a\\raw\\mbstyle-1.pfcss                                                                 "
    ],
  "version": "0.1.0",
  "author": "W3plan Technologies",
  "package": "Preprocessor",
  "copyright": "Copyright 2015-2016 W3plan Technologies, http://w3plan.net",
  "licenses": "GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>",
  
  "layoutMapping": {
    "/layout/pfm/a/raw/index.html": [ 
                                      "/template/pfm/a/section/s-head.html", 
                                      "/template/pfm/a/section/s-lang-en.html", 
                                      "/template/pfm/a/section/s-lang-cn.html"
                                    ],
    "/layout/pfm/b/raw/index.html": [ 
                                      "/template/pfm/b/section/mb-head.html", 
                                      "/template/pfm/b/section/mb-lang-en.html", 
                                      "/template/pfm/b/section/mb-lang-cn.html"
                                    ]
    },
  "layoutFileComments": true
}

 

pfcss.json

 

siteroot/css/pfcss/pfcss.json is PFCSS configuration that lets Preprocessor server know how to create result CSS files from pfcss files that CSS code contains PlannerFw tags, an example of pfcss.json are:

{
  "name": "PFCSS configuration",
  "description": [
        " PFCSS is an abbreviation of PlannerFw Cascading Style Sheets, this file describes   ",
        " how Preprocessor mapping pfcss files to result css files, any pfcss file changed    ",
        " would be leaded to result css file regenerated automatically.                       ",
        "                                                                                     ",
        " Name in value object of pfcssMapping is a css file and value is an array of pfcss   ",
        " files or a string of pfcss file, all path for css and pfcss files are relative path ",
        " to this file. value of comments will be output to each result css file. Windows     ",
        " directory separator has to be escaped, for example,  a\\raw\\mbstyle-1.pfcss        "
      ],
  "version": "0.1.0",
  "author": "W3plan",
  "package": "Templates and PFCSS Preprocessor",
  "copyright": "Copyright 2015-2016 W3plan Technologies, http://w3plan.net",
  "licenses": "GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>",
  
  "pfcssMapping": {
     "pfm/mbstyle.css": [
                         "pfm/a/raw/mbstyle-1.pfcss", 
                         "pfm/a/raw/mbstyle-2.pfcss", 
                         "pfm/a/raw/mbstyle-3.pfcss"
                        ],
     "pfm/b/double-ring.css": [
                               "pfm/b/raw/double-ring-1.css", 
                               "pfm/b/raw/double-ring-2.css"
                              ],
     "pfm/c/single-file.css": "pfm/c/raw/single-file.css"
   },
  "cssFileComments": true
}

PlannerFw Preprocessor server would use mapping configuration to generate new css file from changed pfcss file, for example, if any one of siteroot/css/pfcss/pfm/a/raw/mbstyle-1.pfcss, siteroot/css/pfcss/pfm/a/raw/mbstyle-2.pfcss or siteroot/css/pfcss/pfm/a/raw/mbstyle-3.pfcss was changed by code editor, new css file siteroot/css/mbstyle.css would be automatically generated and the value of comments in configuration would be wrote to the top of result css file.

PlannerFw only process the files in path siteroot/css/pfcss and there is mapping setting in pfcss.json

You can use this features to add multiple CSS files to a minified CSS file even CSS files without any PlannerFw tags.

 

component.json

 

pfcomponent/component.json in server component sets website root directory and secret key for JWT(JSON Web Token), property siteRootPath let PlannerFw's server-side component know the location of website as PlannerFw's server-side component could be installed in any place except website root and its subdirectories. Default component.json is


{
  "name": "pfcomponent configuration",
  "description": "Set website root-path and secret key for JWT (JSON Web Token) verification",
  "version": "2.0.1",
  "author": "W3plan",
  "package": "pfcomponent",
  "copyright": "Copyright 2015-2016 W3plan Technologies, http://w3plan.net",
  "licenses": "GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>",
	
  "siteRootPath" : "z:/pfeeroot",
  "jwtSecretKey" : "put your JWT(JSON Web Token) secret key here"
}
	

 

.htaccess or other similar file for non Apache HTTP server

 

Set directory of PlannerFw's server-side component as a environment variable, so that website or application can find it, for example, SetEnv PFCOMPONENT_PATH "z:/pfcomponent"

 

pfwatch.json

 

pfdevelop/preprocessor/pfwatch.json sets website root directory and template file extension to Preprocessor of PlannerFw Dev, PlannerFw Dev could be installed in any place except website root and its subdirectories. Default pfwatch.json is:

{
  "name": "Preprocess watching",
  "description":[
        " Set website root-path that would be watched by Preprocessor    ",
        " siteRootPath should be an absolute path of website root        ",
        " comments could be wrote to result file, windows directory      ",
        " separator has to be escaped, for example, z:\\buz\\public_html "
       ],
  "version": "2.0.1",
  "author": "W3plan",
  "package": "Templates and PFCSS Preprocessor",
  "copyright": "Copyright 2015-2016 W3plan Technologies, http://w3plan.net",
  "licenses": "W3plan Software License <http://w3plan.net/customer/general/pflicense/>",
  
  "siteRootPath": "z:\\pfeeroot",
  "templateFileExtension": "js",
  "comments": [
      "/**",
      " * This file was generated by PlannerFw's Preprocessor",
      " * ",
      " * Version    0.0.1",
      " * Author     W3plan",
      " * Category   Preprocessor",
      " * Copyright  Copyright 2015-2016 W3plan Technologies, http://w3plan.net",
      " * Licenses   GNU GPLv3 license <http://www.gnu.org/copyleft/gpl.html>",
      " */"
    ]
}

 

pfbuild.json

 

pfdevelop/deploybuilder/pfbuild.json sets website root directory, archive type, target file name and to add directories that updating builder does not process in default, deploybuilder is an application to create an archive that contained all files and subdirectories of website's template, css, layout directories and appended not watching directories for updating production severs. Default pfbuild.json is

{
  "name": "Deployment builder",
  "description": [ 
    " Create an archive from updated /template, /layout, /css/pfcss and other directories for   ",
    " production server. siteRootPath should be an absolute path, targetFile is the path to save",
    " result archive file, addDirsToBuilder includes subdirectories of website root except      ",
    " /template, /layout and /css/pfcss folders, subdirectories to addDirsToBuilder can not     ",
    " include the part of website root, archiveType should be one of zip and tar. Windows       ",
    " directory separator has to be escaped, for example, c:\\htdocs, z:\\buz\\public_html      "
   ],
  "version": "0.1.0",
  "author": "W3plan",
  "package": "Deployment builder",
  "copyright": "Copyright 2015-2016 W3plan Technologies, http://w3plan.net",
  "licenses": "W3plan Software License <http://w3plan.net/customer/general/pflicense/>",
  
  "siteRootPath": "z:/pfeeroot",
  "allowedTemplateExtension": ["js", "php"], 
  "archiveType": "zip",
  "targetFile": "./updated-deploy.zip",
  "addDirsToBuilder": ["js"]
}

 

jwt-handle.*

 

pfcomponent/server-type/pfjwt/jwt_handle.* in server component provides functions for server using JWT(JSON Web Token) for model access, here server-type includes php, nodejs, python, java, perl, .net, ruby, jwt-handle file extension depends on server type. default contents of jwt_handle.php are

<?php
/**
 * Read JWT from Cookie, decode it then return it or do other processes 
 * according to JWT issued time. You have to use this file for all 
 * PlannerFw model access if you want to use JWT
 * 
 * As long as start to use JWT for model access, jwtClaim has to be 1 to model in 
 * following response otherwise PlannerFw would turn off model access from JWT
 * 
 */

// Load JWT Library
require __DIR__ . "/../vendor/jwt/JWT.php";

use \Firebase\JWT\JWT;

/**
 * beginning to use JWT for model access
 *
 * Update JWT claims with user data that collects from sign-in code or server-side storage
 *
 * To apply RESTful architecture for model system, startJwt() have to run with sign-in code 
 * so that transfers user data into JWT because REST application is stateless, the server 
 * does not store client context
 * 
 */
function startJwt() {	
	// Unix time stamp
	$timestamp = time();

	// user private data, for example, 
	$firest_name = "Mary";
	$last_name = "Kay";
	$email = "mary.kay@gmail.com";
	$country_code = "US";
	$ownership = "public";
	
	/**
     * Registered Claim Names for JWT
	 *   iss: (mandatory) The issuer of the token
	 *   sub: The subject of the token
	 *   aud: The audience of the token
	 *   exp: Token expiration time defined in Unix time
	 *   nbf: "Not before" time that identifies the time before 
     *        which the JWT must not be accepted for processing
	 *   iat: (mandatory) "Issued at" time, in Unix time, at which the token was issued
	 *   jti: JWT ID claim provides a unique identifier for the JWT
	 *   
     * Public Claim Names for JWT used by PlannerFw
     *   permission: read, create, replace or delete. read is default value
     *   ownership: owner, group or public. public is default value
     * 
	 * Following fname, lname, email, country and ownership are examples of JWT public claims
     *     
	 * Be mind: do not put sensitive information to claims because they are not encrypted  
	 * 
	 */
	
	$token = array (
		"iss" => "http://w3plan.net",
		"sub" => "Model in JSON or XML",
		"iat" => $timestamp,
		"fname" => $firest_name,				
		"lname" => $last_name,		
		"email" => $email,
		"country" => $country_code,
		"ownership" => $ownership
	);
    
	/**
	 * Encoding token
	 * 
	 */
	try {
		$jwt = JWT::encode($token, $scretKey);
	}
	catch(Exception $e) {
		echo getEmptyModel(2, "PlannerFw can not encode JWT");
		exit();
	}
	
	return array("jwtClaim" => 1, "jwt" => $jwt);	
}

/**
 * Decode JWT, verify JWT, return an array with jwtClaim element or jwtClaim and 
 * jwt elements. If there is an error happen getEmptyModel would be called to output
 * an empty model with error message to the client
 *
 * @param string $pf_type    "http" or "ws"
 * @param string $pf_jwt     The value of JWT
 * @return array      An array with jwtClaim element or jwtClaim and jwt elements
 */
function checkPfJwt($pf_jwt, $pf_type = "http") {
	if (isset($pf_jwt) && strlen(trim($pf_jwt)) > 10) {
		/**
		 * Save JWT secret key to $_SERVER or $_ENV variable would get 
		 * better performance than saving them in the file 
		 *
		 */
		$config = file_get_contents(__DIR__ . "/../../component.json");
		
		try {
			$config = json_decode($config, true);
		}
		catch(Exception $e) {
			if (strtolower($pf_type) == "ws") {
				throw $e;
			} else {
				/**
				 * Output empty model as a string with message in description
				 */
				echo getEmptyModel(2, $e->getMessage());
				exit();
			}
		}
		
		$scretKey = $config['jwtSecretKey'];
		
		/**
		 *  You have to specify algorithms to decode JWT 
		 *  this library supports: HS256, HS384, RS256, RS384 and ES256
		 *
		 */		
		try {
			$decodedJwt = JWT::decode( $pf_jwt, 
									   $scretKey, 
									   array('HS256','HS384', 'RS256', 'RS384', 'ES256')
									  );
		}
		catch(Exception $e) {
				if (strtolower($pf_type) == "ws") {
					throw $e;				
				} else {
					echo getEmptyModel(2, $e->getMessage());
					exit();
				}
			}
		
		// Unix time stamp
		$timestamp = time();

		/**
		 *  Refresh issued time with current Unix time stamp. If JWT issued
		 *  time is large then 12 minutes then delete cookie pf_auth_token
		 *  
		 *  You are able to replace it with your value
		 * 
		 */
		if ($timestamp - $decodedJwt->iat < 720) {
			
			// rewrite issued time to JWT if it is between 60 and 720 seconds 
			if ($timestamp - $decodedJwt->iat >= 60) {
				
				// set new Unix time stamp
				$decodedJwt->iat = $timestamp;
				
				/**
				 * Encoding existing token
				 *
				 */
				try {
					$jwt = JWT::encode($decodedJwt, $scretKey);
				}
				catch(Exception $e) {
					if (strtolower($pf_type) == "ws") {
						throw $e;				
					} else {
						echo getEmptyModel(2, "PlannerFw can not encode JWT", true);
						exit();
					}
				}
				
				return array("jwtClaim" => 1, "jwt" => $jwt);
			} else {
				// do not rewrite issued time to JWT if it is no more then 6 seconds 
				return array("jwtClaim" => 1);
			}
		} else {
			// if JWT issued time is more than 720 seconds, turn off model access from JWT
			return array("jwtClaim" => 0);
		}
		
	} else {
		// Client computer does not use JWT to access model
		return array("jwtClaim" => 0);
	}
}

/**
 * output a empty PlannerFw model with errorLevel and errorMessage properties
 * 
 * @param string $errorLevel      One of "Error", "Warning", "Notice"
 * @param string $errorMessage   Error message
 *
 * @return string      Empty model in string
 */
function getEmptyModel($errorLevel, $errorMessage, $jwtClaim = 0 ) {
	return <<<DOC
		{	
			"description": "An empty model",
			"expiration": 0,
			"jwtClaim": $jwtClaim,
			"pfDataSet": null,
			"errorLevel": $errorLevel,
			"errorMessage": $errorMessage
		}
DOC;
}

/**
 * Execute startJwt() to start JWT for model, then use checkPfJwt() to verify JWT. 
 * If you do not use JWT to access model, do not use this file
 *
 * The value of JWT from client would be $_COOKIE['pf_auth_token'] for a HTTP request, 
 * and the value of JWT would be got through Cookie parse of WebSocket server
 * 
 * $this->jwtInfo = array("jwtClaim" => 0);	or $this->jwtInfo = checkPfJwt("") would 
 * turn off model access from JWT
 * 
 */
?>

 

PlannerFw tags are some special HTML comment tags that could be used to implement dynamic data presentation on static HTML pages, there are two types of PlannerFw tags:

Operation tag: <!--% ... %-->

Output tag: <!--%= ... %--> and <!-%= ... %->

Operation tag wrap JavaScript code, PlannerFw APIs and model data members to implement data presentation, output tag is used to output contents of expressions or variables to result page. PlannerFw's Preprocessor can automatically compile HTML file that contains PlannerFw tags into a JavaScript file which is called template in development environment, and PlannerFw's Processor would retrieve dynamic data (or called model) and template then generate result web page from them.

PlannerFW operation tag can't nest operation tag and output tag <!--%= ... %-->, only output tag <!-%= ... %-> can be used inside Operation tag.

PlannerFW output tag can't nest operation tag and output tag.

PlannerFW tags have to be closed.

Any web browser would parse PlannerFw tags as PlannerFw tags are HTML comments, HTML code with and without PlannerFw tags would be rendered in same result to all web browser.

PlannerFw raw template, raw section template and raw layout template is a HTML file or HTML code that include PlannerFw tag inside, PlannerFw PFCSS is a CSS file or CSS code that include PlannerFw tag inside. Examples of raw template, model (dynamic data) and result code are:

Raw template

<!--% /* A typical template of PlannerFw  */ %--> 
<table border="1" cellpadding="1" cellspacing="1" width="60%"> 
<caption class="dcap"><strong>User data grid</strong></caption> 
<tr> 
<th rowspan="2">City</th> 
<th colspan="3">Users</th> 
</tr> 
<tr> 
<th>Name</th> 
<th>Age</th> 
<th>Education</th> 
</tr> 
<!--% 
	for(var key in pfDataSet.grid) { 
		if (pfDataSet.grid.hasOwnProperty(key)) { 
%--> 
<tr> 
<td><!--%= pfDataSet.grid[key].city %--></td> 
<td><!--%= pfDataSet.grid[key].name %--></td> 
<td><!--%= pfDataSet.grid[key].age %--></td> 
<td><select name="Education" style="cursor:pointer"> 
<!--% 
	var educations = ["No College", "Some College", "Graduate School", "College"]; 
	/* PlannerFw can use all JavaScript code except single line comment */ 
	for (var i = 0, len = educations.length; i < len; i++) { 
		if (pfDataSet.grid[key].education.toLowerCase() == educations[i].toLowerCase()) { 
			<!-%= /* Tag nested sample */  
					'<option value="' +  
					educations[i] + 
					'" selected>' + 
					educations[i] + 
					"</option>" 
			%-> 
		} else { 
			<!-%= '<option value="' + 
					educations[i] + 
					'">' + 
					educations[i] + 
					"</option>" 
			%-> 
		} 
	} 
%--> 
</select></td></tr> 
<!--% 
	}
}
%-->
</table>

Model (dynamic data)

var pfDataSet = 
{"grid": { 
		"gridRow1": {
			"city": "New York",
			"name": "Jonesy Band",
			"age": 16,
			"education": "No College"
		},
		"gridRow2": {
			"city": "Chicago",
			"name": "Mary Kay",
			"age": 35,
			"education": "Graduate School"
		},
		"gridRow3": {
			"city": "Los Angeles",
			"name": "James Franco",
			"age": 28,
			"education": "College"
		},
		"gridRow4": {
			"city": "San Diego",
			"name": "Ellen Compell",
			"age": 20,
			"education": "Some College"
		}
	}
};

Result code

<table border="1" cellpadding="1" cellspacing="1" width="60%"> 
	<caption class="dcap"><strong>User data grid</strong> 
	</caption> 
	<tr> 
		<th rowspan="2">City</th> 
		<th colspan="3">Users</th> 
	</tr> 
	<tr> 
		<th>Name</th> 
		<th>Age</th> 
		<th>Education</th> 
	</tr> 
	<tr> 
		<td>New York</td> 
		<td>Jonesy Band</td> 
		<td>16</td> 
		<td> 
			<select name="Education" style="cursor:pointer"> 
				<option value="No College" selected>No College</option> 
				<option value="Some College">Some College</option> 
				<option value="Graduate School">Graduate School</option> 
				<option value="College">College</option> 
			</select> 
		</td> 
	</tr> 
	<tr> 
		<td>Chicago</td> 
		<td>Mary Kay</td> 
		<td>35</td> 
		<td> 
			<select name="Education" style="cursor:pointer"> 
				<option value="No College">No College</option> 
				<option value="Some College">Some College</option> 
				<option value="Graduate School" selected>Graduate School</option> 
				<option value="College">College</option> 
			</select> 
		</td> 
	</tr> 
	<tr> 
		<td>Los Angeles</td> 
		<td>James Franco</td> 
		<td>28</td> 
		<td> 
			<select name="Education" style="cursor:pointer"> 
				<option value="No College">No College</option> 
				<option value="Some College">Some College</option> 
				<option value="Graduate School">Graduate School</option> 
				<option value="College" selected>College</option> 
			</select> 
		</td> 
	</tr> 
	<tr>
		<td>San Diego</td> 
		<td>Ellen Compell</td> 
		<td>20</td> 
		<td> 
			<select name="Education" style="cursor:pointer"> 
				<option value="No College">No College</option> 
				<option value="Some College" selected>Some College</option> 
				<option value="Graduate School">Graduate School</option> 
				<option value="College">College</option> 
			</select> 
		</td> 
	</tr> 
</table>

 

PlannerFw's code style include

Try to comment code in PlannerFw tags.

Add at least one whitespace between PlannerFw tag name and code.

Use multiple lines for an long expression.

Last code line or one line code only in PlannerFw tag does not need to be added semicolon at the end, PlannerFw is able to handle it.

If there are multiple nest of " and ' in PlanenrFw tags, using String.fromCharCode(34) for " and String.fromCharCode(39) for ' are available.

A template file can use dynamic data in PlannerFw tags, if dynamic data from outside of template file then it has to be JSON with name "pfDataSet", if dynamic data was defined in template file then there is no requirements for data type and naming, for example, data using in PFCSS files.

Apply JavaScript style for JavaScript, for example, Google JavaScript Style.

PlannerFw convert single-line comments to multiple-line comments at time of compilation, non whitespace(s), newline(\n), carriage return(\r) and tab(\t) characters before "//" would be considered as URL or path instead of single-line comment.

PlannerFw code style sample

	<!--%= users[i].name %-->

	<!--%=
		/* Output value of expression */
		users[i].name +
		"<br>" + 
		["novel", "magazine", "period"].join(" ; ") + 
		"<br>"+
		"Sum: " +
		(12 * 8 / 3 + 15) +
		(function(a,b){return a + b})(2, 5)
	%-->

	<!--%
		/* Nesting tags sample */
		var k = 2/6 + 3*5;
		<!-%= k / 2 %->;
	%-->

 

ECMAScript 6 supports multi-line strings and simple templating via template literals, it is working perfect with PlannerFw, for example, the previous code could be wrote as

	<!--%= users[i].name %-->

	<!--%=
		 /* 
          * Output value of expression 
          * 
          * ECMAScript 6 example of multi-line strings and 
          * simple templating via template literals with PlannerFw
          *
          */
	     `
           ${users[i].name}
           <br>
           ${["novel", "magazine", "period"].join(" ; ")} 
           <br>
           Sum: 
           ${(12 * 8 / 3 + 15)}
           ${(function(a,b){return a + b})(2, 5)}
         `
	%-->

	<!--%
		/* Nesting tags sample */
		var k = 2/6 + 3*5;
		<!-%= k / 2 %->;
	%-->

 

PlannerFw tags are used to implement data presentation, and data presentation have to be convert to normal web page before result page rendering completion, so following JavaScript APIs are not proper to use in PlannerFw's operation tags as they could break or delay PlannerFw processes,

Document objects.

Browser objects, for example, Navigator, History, Location.

Popup (boxes alert, confirm, prompt), Timing events(setInterval, setTimeout), XMLHttpRequest and WebSocket.

 

PlannerFw provides following processes to support RESTful model:

  1. Create JWT(JSON Web Token) to keep user information at the time of user sign-in.
  2. Save and transmit JWT with session Cookie pf_auth_token.
  3. Requests for HTTP server and WebSocket transmit Cookie automatically, so HTTP server can authenticate user with the data got from Cookie pf_auth_token and request, WebSocket server can authenticate user with the data got from Cookie pf_auth_token and request URL at the time of handshake.
  4. HTTP server and WebSocket server would compare timestamp of JWT to decide whether keeping, updating or deleting Cooke pf_auth_token.
  5. WebSocket server does not keep connection after output model, it immediately close connection after output data, so every WebSocket requests could be authenticated.
  6. User sign-out or time stamp expired will delete Cooke pf_auth_token.
  7. User can submit HTTP/HTTPS request with methods: "GET", "POST", "PUT" and "DELETE" through request header other than URI.
  8. User can implement pseudo-methods: "GET", "POST", "PUT" and "DELETE" for WebSocket server with well-designed URL.
HTTP request with PUT method

[HTTP request with PUT method]

WebSocket request headers with JWT

[WebSocket request headers with JWT(JSON Web Token)]

WebSocket response frame

[WebSocket response frame]

WebSocket PHP server

[PHP WebSocket server]

 

PFCSS is a system that compile and transform it's contents with same syntax and mechanism of PlannerFw's template, the contents of a PFCSS file are some CSS mixed with PlannerFw tags, PFCSS files can be compiled and transformed without model requirement as dynamic data was defined with data presentation together. PlannerFw's pfdevelop can watch PFCSS directories and files in development environment, automatically compile and transform files that are modified by user to normal CSS file.

Sass-like or Less-like mechanisms that PlannerFw implemented

Variable mechanism of dynamic CSS

/* Sample of variable mechanism */

<!--% 
	var clr = "#4D926F";
	var bg = "background-color";
	var h2 = "h2"; 
%-->
/* Apply Variables */
#header {
  color: <!--%= clr %-->;
}
<!--%= h2 %--> { 
  <!--%= bg %-->: white;
}

/* Result code of compilation and transformation */

#header {
  color: #4D926F;
}
h2	{ 
  background-color: white;
}
	

 

Mixins mechanism of dynamic CSS

/* Sample of mixins mechanism */

<!--% 
	function mixin(radius) {
		radius = radius || "5px"; 
		return  "-webkit-border-radius: " + radius + "; " +
				"-moz-border-radius: " + radius + "; " +
				"border-radius: "+ radius + "; ";
	}
%-->
#header {
  <!--%= mixin(); %--> 
}
<!--% /* Mixins with Arguments / Dynamic Mixins */ %-->
#footer {
  <!--%= mixin("10px"); %--> 
}

/* Result code of compilation and transformation */

#header {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}
#footer {
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
}	
	

 

Nesting mechanism of dynamic CSS

/* Sample of nesting mechanism */

<!--% 
var nest = " \
			#header { \
			  h1 { \
				font-size: 26px; \
				font-weight: bold; \
			  } \
			  p { \
				font-size: 12px; \
				a { \
				  text-decoration: none; \
				} \
				a:hover { \
					border-width: 1px; \
				} \
			  } \
			  background-color: #ccc; \
			} \
		";
/* Write parsed result to output stream */ 
<!-%= planner.parseNestedCss(nest); %->
%-->

/* Result code of compilation and transformation */

#header {
  background-color: #ccc;
}
#header h1 {
  font-size: 26px;
  font-weight: bold;
}
#header p {
  font-size: 12px;
}
#header p a {
  text-decoration: none;
}
#header p a:hover {
  border-width: 1px;
}
	

 

Functions mechanism of dynamic CSS

/* Sample of functions mechanism */

<!--% 
	/*  Arithmetic rules of CSS units calculation: 
		2em * 3in => Error 		 
		2em + 3em => 5em
		2em + 3   => 5em
		2in * 3in => 6in
		2in * 3   => 6in
		(3cm / 2em) * 4em => Error  
		2in + 3cm + 2pc => Error 	 
		3in / 2in => 1.5in
		3in / 2   => 1.5in
	*/
	var fontSize = "50%";
	var lineHeight = "10px";
%-->
.content h3 {
	color: #000;
	font-size: <!--%= planner.addCssUnit(planner.stripCssUnit(fontSize) * 2 + 60 , "%"); %-->;
	margin: <!--%= planner.addCssUnit(planner.stripCssUnit(lineHeight) * 4); %--> 0;
}

/* Result code of compilation and transformation */

.content h3 {
  color: #000;
  font-size: 160%; 
  margin: 40px 0; 
}
	

 

Operations mechanism of dynamic CSS

/* Sample of operations mechanism */

<!--% 
	/* Looping operation */ 
	for (var i = 1; i < 6; i++) {
		<!-%= 
			".border-#" + i + "0 {" + \
				" border: " + i + "px solid blue;" + \
			"}" 
		%->
	}
%-->

/* Result code of compilation and transformation */

.border-#10 { 
	border: 1px solid blue; 
} 
.border-#20 { 
	border: 2px solid blue; 
} 
.border-#30 { 
	border: 3px solid blue; 
} 
.border-#40 { 
	border: 4px solid blue;
} 
.border-#50 { 
	border: 5px solid blue; 
}
	

 

Comparison PFCSS and LESS

 

Double ring from http://codepen.io/fixcl/pen/lvCFr is a good sample of LESS, we rewrite it with PlannerFw and fix browser compatibility problems:

Double ring with LESS Double ring with PlannerFw


@import url(http://fonts.googleapis.com/css?
                   family=Open+Sans:700);

// =========================== VAR
@D:280px; // size control // diametro
@R:(@D/2); // radio
@B:(@R/8);
@O: 0.6; // opacity
@A: 2s; // accelerate

html,
body{
  height: 100%;
}

.hoja{
  color: rgb(220,220,226);
  position:absolute;
  top:50%; left:50%;
  margin-left:-@R;
  margin-top:-@R;
  width:@D;
  height:@D;
  text-align:center;
  font-family: 'Open Sans', sans-serif;
  font-size:@R/4;
  line-height:@D;
  -webkit-font-smoothing: antialiased;
}

.hoja:after, 
.hoja:before{
  content:"";
  border-radius:100%;
  position:absolute;
  top:0; left:0;
  width:100%;
  height:100%;
  transform-origin:center center;
}

.hoja:after{
  box-shadow: inset 0 @B 0 rgba(250, 250, 0, @O),
			  inset @B 0 0 rgba(250, 200, 0, @O),
			  inset 0 -@B 0 rgba(250, 150, 0, @O),
			  inset -@B 0 0 rgba(250, 100, 0, @O);
			  
  animation: rotar @A -0.5s linear infinite;
}

.hoja:before{
  box-shadow: inset 0 @B 0 rgba(0, 250, 250, @O),
			  inset @B 0 0 rgba(0, 200, 200, @O),
			  inset 0 -@B 0 rgba(0, 150, 200, @O),
			  inset -@B 0 0 rgba(0, 200, 250, @O);
			  
  animation: rotarIz @A -0.5s linear infinite;
}

@keyframes rotar{
  0%{
	transform:rotateZ(0deg) scaleX(1) scaleY(1);
  }
  
  50%{
	transform:rotateZ(180deg) scaleX(0.82) scaleY(0.95);
  }
  
  100%{
	transform:rotateZ(360deg) scaleX(1) scaleY(1);
  }
}

@keyframes rotarIz{
  0%{
	transform:rotateZ(0deg) scaleX(1) scaleY(1);
  }
  
  50%{
   transform:rotateZ(-180deg) scaleX(0.95) scaleY(0.85);
  }
  
  100%{
	transform:rotateZ(-360deg) scaleX(1) scaleY(1);
  }
}







			
<!--%
/* Setting basic variable to this dynamic CSS */
var base = {
  "D" :  "280px",
  "O" : .6,
  "A" :  "2s"
};
/* CSS units calculation of PlannerFw */
var R = planner.addCssUnit(planner.stripCssUnit(base.D) / 2, "px");
var B = planner.addCssUnit(planner.stripCssUnit(R) / 8, "px");
/* Mixins mechanism of PlannerFw */
function mixin1(a, b, c, d) {
  return "inset " + a + " " + b + " 0 rgba(250, " + 
          c + ", 0, " + d + ")";
}
function mixin2(a, b, c, d, e) {
  return "inset " + a + " " + b + " 0 rgba(0, " + 
          c + ", " + d + ", " + e + ")";
}
function mixin(rate, degree, scaleX, scaleY) {
  return rate + " {" + 
        "-webkit-transform:rotateZ("+ degree + ") scaleX("+ scaleX + 
		") scaleY(" + scaleY + ");" + "transform:rotateZ("+ degree + 
		") scaleX(" + scaleX + ") scaleY(" + scaleY + ");}";
}
%-->

@import url(http://fonts.googleapis.com/css?family=Open+Sans:700);
html, body{
  height: 100%;
}
.hoja{
  color: #dcdce2;
  position:absolute;
  top:50%; left:50%;
  margin-left: -<!--%= R %-->;
  margin-top: -<!--%= R %-->;
  width: <!--%= base.D %-->;
  height: <!--%= base.D %-->;
  text-align:center;
  font-family: 'Open Sans', sans-serif;
  font-size: <!--%= 
               planner.addCssUnit(planner.stripCssUnit(R) / 4, "px")
			 %-->;
  line-height: <!--%= base.D %-->;
  -webkit-font-smoothing: antialiased;
}
.hoja:after, .hoja:before{
  content:"";
  border-radius:100%;
  position:absolute;
  top:0; left:0;
  width:100%;
  height:100%;
  -webkit-transform-origin:center center;
  -ms-transform-origin:center center;
  transform-origin:center center;
}
.hoja:after{
  box-shadow: <!--%= mixin1(0, B, 250, base.O) %-->,
              <!--%= mixin1(B, 0, 200, base.O) %-->,
              <!--%= mixin1(0, "-" + B, 150, base.O) %-->,
              <!--%= mixin1("-" + B, 0, 100, base.O) %-->;
  -webkit-animation: rotar <!--%= base.A %--> -0.5s linear infinite;
  animation: rotar <!--%= base.A %--> -0.5s linear infinite;
}
.hoja:before{
  box-shadow: <!--%= mixin2(0, B, 250, 250, base.O) %-->,
              <!--%= mixin2(B, 0, 200, 200, base.O) %-->,
              <!--%= mixin2(0, "-" + B, 150, 200, base.O) %-->,
              <!--%= mixin2("-" + B, 0, 200, 250, base.O) %-->;
  -webkit-animation: rotar <!--%= base.A %--> -0.5s linear infinite;
  animation: rotar  <!--%= base.A %--> -0.5s linear infinite;
}
/* Standard syntax */ 
@keyframes rotar{
  <!--%= mixin("0%", "0deg", 1, 1); %-->
  <!--%= mixin("50%", "180deg", 0.82, 0.95); %-->
  <!--%= mixin("100%", "360deg", 1, 1); %-->
}
@keyframes rotarIz{
  <!--%= mixin("0%", "0deg", 1, 1); %-->
  <!--%= mixin("50%", "-180deg", 0.95, 0.85); %-->
  <!--%= mixin("100%", "-360deg", 1, 1); %-->
}
/* Chrome, Safari, Opera */ 
@-webkit-keyframes rotar {
  <!--%= mixin("0%", "0deg", 1, 1); %-->
  <!--%= mixin("50%", "180deg", 0.82, 0.95); %-->
  <!--%= mixin("100%", "360deg", 1, 1); %-->
}
@-webkit-keyframes rotarIz {
  <!--%= mixin("0%", "0deg", 1, 1); %-->
  <!--%= mixin("50%", "-180deg", 0.95, 0.85); %-->
  <!--%= mixin("100%", "-360deg", 1, 1); %-->
}

 

CSS files and Session Storage

PlannerFw include a group of API to retrieve external CSS files and save it to protected Session Storage, using URL of CSS files as key to interactive value in Session Storage, so user can use JavaScript to switch current CSS with contents saved in Session Storage without network connections, PlannerFw calls it as CSS Switch. A simple example of CSS Switch is:

Put style on head of the page with PlannerFw tags at first,

<!--%=
    /* 
     * If CSS code is not in Session Storage then retrieving it from URL, 
     * otherwise from Session Storage 
     */
    planner.importCss("/css/pfcss/pfm/a/theme-a.css")
%-->

then create JavaScript code to execute CSS contents switching with "/css/pfcss/pfm/a/theme-a.css" as key,

<script type="text/javascript">
  var num = 0;
  function doSwitch() {
    if (num % 2) {
      postplanner.switchCssByUrl("/css/pfcss/pfm/a/theme-a.css", "/css/pfcss/pfm/a/theme-a.css");
    } else {
      postplanner.switchCssByUrl("/css/pfcss/pfm/a/theme-a.css", "/css/pfcss/pfm/b/theme-b.css");
    }
  num += 1;
}
</script>	

last call switching function writeCalendar() at the place you want.

CSS Switch

 

Model member encryption/decryption

 

If data member name includes "__pfenc" in the end, page processor would consider its value was encrypted by PlannerFw's encryption mechanisms, automatically start decryption processes to replace the value with decrypted value and remove "__pfenc" suffix from name if passed encryption successfully. data type of model member has to be one of string, number, boolean and null to apply this feature, if value is an object, convert it to a string then encrypt it, Page processor would reset string back to object after decrypted the value automatically, so this feature could be applied to all JSON members. Some samples of model member encryption/decryption,

  Name   Value   Result name   Result value
"member84__pfenc" "djBhBH1BbjFmdmo3LkEaAE90dh0YDgErPj0WC1drUho4OQkOBmh0Vj08IxZTWmAtIx8ZIDdSAGkCLH9GfU0DKXIEfTcgAABSJAwWS1hoKjkeF1sHcXxFc1cyWhNGexxAU0YNAjUbXWhiQy0pVXNgcg8GbWFEamRgU3x3ACNTQn5FVEQvZQlhCl4qVXoReQkBfBx3S2FFMDEPVVNxDDB2Z1YWDwVOcVshMhMEWFNhQ1JmC0EqIgomHhtYPjA=" "member84" "NASA's Terra satellite passed over Karina in the eastern Pacific <br> Ocean on Aug. 14 at 2:40 pm. EDT when it was still a hurricane"
"member82__pfenc" "bBlXYGpDKDcqJzFGeQMKEwUxKEtLSgIoKnFQXhgsDxo8Ll9ZUiIwAyBvdFQCADVDcF0cLywOF2tbJjUTaAtbYXIEfXcERVEOY0cLP019fX9PNRcmY2swYUFIeURbfHwgV1MDUnVDBmgVS2d/SWQSAV8kdGFOGHBtRmsCVnJOMms1SVIraRwGD0sYC3FxcxwGfm1qTnR3bHEjMUR5U2EWY0NZWVNScU4XcV9QdlNqRA==" "member82" "The quick brown fox jumped over the lazy dog - 敏捷的棕色狐狸跳过了懒狗"
"member87__pfenc" "HURwYGpBPislYR9VbnJYRCgFYytMXyYEd2wkS0BrR1gab0hbR390DCItYyFATRFWZy8zaxNCFxsoZ2IyaAtZaWV1aGBxVkYFPBJWHk19fX82QRcmF2swY0Eze0RbDnxXU1NwInVIA2hiQTIpF2QVBV8gc2E0GXARQ2sFInJONmtFQ1JcEhx2CEtoCiBdLRwBDm0TSXRwG3EkRUQDJmFrE0NTXFNeBw==" "member87" ["gif格式", "jpg格式", "jpeg格式", "png格式", "tif格式"]
"image__pfenc" "HUZwYGpBKjAgYWhRbgApRFh2Y1xLX1YBOzkDCRcqR1gfIRsZCyMnOSE%2BKRYbWX4MMgpVfGRSABpOcGpRfwsKICNlPzFkQUYSZSMWS1gdeWgfBUYUcX00c1Y3HVMlJTwBCBdsBCUIViYxHyc1BG8%2FQR1EcQIbLzkgHyMiBT4XLz4FHhNLY380TQx%2FChJDLlthCg5lTmkAawsWFQNvDzQ0cFRTS0Ev" "image" {"src": "/images/pfm/karina_storm1.jpg", "altSrc": "http://media.eurekalert.org/multimedia_prod/pub/web/77823_web.jpg"}

 

Model member check

 

If data member name includes "__pfcrc" in the end and its value is an array with two elements, page processor would consider the second element would be a 32-bits CRC checksum to the first element, automatically start to calculate checksum of the first element and compare it with the second elements in array, and remove "__pfcrc" suffix from name if passed comparison. data type of model member has to be string or number to apply this feature. Some samples of model member check,

  Name   Value   Result name   Result value
"member62__pfcrc" ["/images/pfm/karina_storm1.jpg", "1856105291"] "member62" "/images/pfm/karina_storm1.jpg"
"member64__pfcrc" ["The quick brown fox jumped over the lazy dog - 敏捷的棕色狐狸跳过了懒狗", "1520042642"] "member64" "The quick brown fox jumped over the lazy dog - 敏捷的棕色狐狸跳过了懒狗"
"member66__pfcrc" ["Le rapide renard brun sauta par dessus le chien paresseux - Быстрая коричневая лиса прыгает через ленивую собаку", "2728971735"] "member66" "Le rapide renard brun sauta par dessus le chien paresseux - Быстрая коричневая лиса прыгает через ленивую собаку"
"member68__pfcrc" ["float: left; width: 100%; background: #333;", "2064493575"] "member68" "float: left; width: 100%; background: #333;"

 

Model member validation

 

If data member name includes "__pfvld" in the end and its value is an array with two elements, the second element in array is an object with "type" property or "type" and "constraint" properties, page processor would consider the second element would be validating conditions to the first element, automatically start to validate data type of the first element and remove "__pfvld" suffix from name if validation is matched. data type of model member has to be one of string, number, boolean or null to apply this feature. Member validation will not work with XML data model as PlannerFW may change data type at the time that converts XML data model to JSON data model.

Some samples of model member check

  Name   Value   Result name   Result value
"member30__pfvld" ["Sunny", {"type": "str", "constraint":
{"enums": ["Rainy", "Sunny", "Cloudy", "Windy"]}
}]
"member30" "Sunny"
"member35__pfvld" ["3.1415", {"type": "fltnum",
"constraint": {"minInc": 3.14, "maxInc": 3.1415}
}]
"member35" "3.1415"
"member50__pfvld" ["5385566611108568", {"type": "tokstr",
"constraint": {"pattern": /^5[1-5][0-9]{14}$/g}
}]
"member50" "5385566611108568"
"member10__pfvld" ["Karina在8月14日越过卡琳娜时它还是一个飓风",
{"type": "unistr"}]
"member10" "Karina在8月14日越过卡琳娜时它还是一个飓风"
"member5__pfvld" ["UGxhbm5lciBmcmFtZXdvcms=",
{"type": "b64str"}]
"member5" "UGxhbm5lciBmcmFtZXdvcms="

 

Data type and its label of validation

  Data type   Label   Examples or Comments
Stringstrdefault JSON member type
Number stringnumstr"1234.56789", "89.451"
Integer stringintstr"127", "8"
Float stringfltstr"3.14", "10.52"
Empty stringempstr"", ''
Exponential number stringexpstr"5.827e+3"
Hexadecimal number stringhexstr"0x60a2fb"
Octal number stringoctstr"03571040"
Binary number stringbinstr"11000010100"
Date or datetime stringdatstr"October 13, 2014 11:13:00"
Regular expression stringregstr"/^[a-z0-9_-]{6,18}$/gi"
URL encoded stringuecstr"PlannerFw%20CE"
Token stringtokstr"5385566611108568"
Normalized stringnrmstrstring without line feeds, carriage returns, and tab characters
   
JSON stringjsnstr'{"a":[5,3,1],"b":"test"}'
Base64 encoded stringb64str"UGxhbm5lciBmcmFtZXdvcms="
ASCII stringascstr"Karina in the eastern Pacific"
Unicode stringunistr"Karina在8月14日越过卡琳娜时它还是一个飓风"
Email stringemlstr"plannerfw@gmail.com"
Absolute URL stringurlstr"http://en.wikipedia.org/wiki/JSON"
CSS color stringclrstr"#ccc", "#cccccc", "rgb(255,0,0)"
CSS length stringlenstr"30px", "25pt", "15mm"
CSS angle stringangstr"45deg", "400grad"
   
Numbernumdefault JSON member type
Integerintnum-1, 0, -1
Positive Integerposnum1, 10, 100
Negative Integernegnum-1, -5, -10
Non Positive Integernpsnum0, -1, -5
Non Negative Integernngnum0, 1, 5
Floatfltnum3.1415
   
Booleanblndefault JSON member type
truetrubln
falseflsbln
   
nullnuldefault JSON member type

 

Data constraints and its labels of validation

  Data constraints   Label   Comments
maxExclusivemaxExcnumber
minExclusiveminExcnumber
maxInclusivemaxIncnumber
minInclusiveminIncnumber
totalDigitstotalDigia positive Integer
fractionDigits fractDigia float number
   
lengthlengthstring
maxLengthmaxLenstring
minLengthminLenstring
   
patternpatterna regular expression
enumerationenumsa specified set of values

 

PlannerFw defined two error levels or types from catchable JavaScript errors, all PlannerFw's error levels include:

Error Level Error Level Name Comments
0Uncatchable errorsErrors that can't be catched by try...catch statements but can be handled by window.onerror, for example, try { :( } catch(e) { }
1Other catchable errorsOther catchable errors except Catchable warning and catchable information
2Catchable warningCatched PlannerFw errors belong to PlannerFw warning
3Catchable informationCatched PlannerFw errors belong to PlannerFw information

 

PlannerFw error handling include

Catchable information would be printed to the browser console only.

In production status, PlannerFw would print all catchable warning to the browser console, continuously processes.

In testing status, PlannerFw would print all catchable warning to the page that pfConfig.clientErrorHandler represents, then stop further processes

In testing status, PlannerFw would print all catchable warning to the current screen, then stop further processes.

PlannerFw uses window.onerror to print other catchable errors and uncatchable errors to the page that pfConfig.clientErrorHandler represents, then stop further processes. If PlannerFw is in production status, web server would write client errors and user information to the log.

PlannerFw APIs and error levels

PlannerFw APIs with catchable information: hex2DecColor, dec2HexColor and addCssUnit

PlannerFw APIs with catchable warning: pfEncryption, pfDecryption, pfCheckCrc, secureModel and checkEnvironment

PlannerFw APIs with other catchable errors: responseJsonEncode, checkResponseData, checkAllowedRequest and checkEnvironment

The sources that generates other catchable errors and uncatchable errors include

Retrieve model, get template, parse template configuration, access WebSocket, convert XML data model to JSON data model, model decryption, generate result code from template and model, error transferred by model properties "errorLevel" and "errorMessage" from server to client.

JavaScript errors: Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError and URIError.

 

PlannerFw raw template is a HTML file or a HTML code snippet include PlannerFw tags, raw template could be automatically compiled into JavaScript file by PlannerFw Preprocessor, raw templates include single-page template, section template and layout template,layout template uses planner. section method to connect each section templates, a section template represents a part of html page. siteroot/layout/layout.json is a configuration file for setting relationship of a layout and its section templates.

Some rules of PlannerFw raw templates include:

Raw template does not include any PlannerFw tag, a general html file could be considered as a raw template.

Single-page templates and raw section templates would be stored in one or multiple modules in siteroot/template folder, layout templates would be stored in siteroot/layout folder.

If a raw section template does not include any PlannerFw tags, then related first parameter of planner.section in raw layout could be "", {} or null, and there is no data required for it from model.

PlannerFw Preprocessor only processor file with size greater than 10 bytes and less than 1MB

The web browser renders raw template with and without PlannerFw tags in same results because PlanenrFw tags belong to HTML comment, web browser and HTML editor would not parse them.

There is configuration required for single-page templates.

PFCSS have similar features of raw templates, and PFCSS is specific for CSS processing.

Using template and layout template in PlannerFw

There are two ways to use a template or a layout template in PlannerFw, the first way is that client assigns a template URL or a layout template URL as query string parameter in the request, the second way is that server assigns a template URL or a layout template URL to contents of be-index.html in the response when request without a template URL assigned, if request with a template URL assigned, server can transfer the template URL from the request to contents of be-index.html in the response. A typical scenario is that PlannerFw takes over server-side MVC framework, actions of the framework controller always output model and a template URL or a layout template URL as JavaScript variable in contents of be-index.html, in this scenario the server output contents of be-index.html instead of template or layout template because of PlannerFw's Data and presentation separated rule: There is no response including model and template contents together, model and template contents can not be output in one same response.

 

An example of raw layout template

 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-control" content="max-age=0">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache-Control" content="must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<title>PlannerFw Demo</title>
 
<!--  

  Copyright 2015-2016 W3plan Technologies, http://w3plan.net
  
-->

<link type="text/css" href="/css/pfcss/pfm/b/double-ring.css" rel="stylesheet" >

<!--%= 
  /* If CSS code is not in Session Storage then saving it from URL to Session Storage */
  planner.loadCss("/css/pfcss/pfm/b/theme-b.css")
%-->

<!--%= 
  /* 
   * If CSS code is not in Session Storage then saving it from URL to Session Storage then 
   * output style tag that includes css code in current position 
   */
  planner.importCss("/css/pfcss/pfm/a/theme-a.css")
%-->

<script type="text/javascript" src="/js/pfm/import-and-load-sample.js"></script>

<script type="text/javascript" src="/app/lib/postprocessor.min.js"></script>

<!--%= 
  /* 
   * section method only be used by PlannerFw output tag 
   * the first parameter is JSON object from model, if raw-template file 
   * without PlannerFw tags, it could be "", {} or null. The second parameter
   * is a raw-template file that was described by layout.json
   *
   */
  planner.section(pfDataSet.pfSet1, "/template/pfm/a/section/s-head.html")
%-->

</head>

<body>
<div id="container">
  <div id="header">
    <div style="float:left;">
    <img id="header_img" src="/app/plannerfw-logo.jpg" alt="Header image" width="120" height="60"
        style="padding:1px;border:1px solid #C0C0C0;cursor:crosshair;" title=" PlannerFw Logo ">
    </div>
    <div class="clock" id="clocktxt"></div>
    <h2>PlannerFw demo - Multiple sections model</h2>
  </div>
  <script type="text/javascript">
    startTime();
  </script>
  <div id="navigation">
    <ul>
      <li id="tool1" onclick="showHomeEn()">Single-byte-charaset</li>
      <li id="tool2" onclick="showHomeCn()">Multi-byte-charaset</li>
      <li id="tool4" onclick="showHomeRet()">PostProcessor</li>
      <li id="tool5" onclick="showDcss()">PFCSS</li>
    </ul>
  </div>
  
  <!--%= 
    /* one output tag can not include more then one section method */ 
    planner.section(pfDataSet.pfSet2, "/template/pfm/a/section/s-lang-en.html");
  %-->
  
  <!--%= planner.section(pfDataSet.pfSet3, '/template/pfm/a/section/s-lang-cn.html') %-->
  
  <div id="dynamic-css" class="content about-off">
    <div class="hoja">PlannerFw</div>
  </div>
  
  <div id="about-content" class="content about-off">
    <div class="inner-content">
    </div>
  </div>
</div>

<div id="footer">
  Copyright 2015 - <script>document.write(new Date().getFullYear())</script> 
  <a href="http://w3plan.net/" target="_blank">
    <img class="w3planIcon" src="/images/w3plan-logo-small.png" width="25" height="15">
  </a> W3plan Technologies
</div>
</body>
</html>

 

Construction APIs of Processor

 

importCss(url)

This method except executing loadCss would output style tag with key as id and value as contents of style, if key can not be found from Session Storage, then output a link tag with url as value of href.

Parameters:

  Name    Type    Description  
urlstringURL or URL path to retrieve CSS files

Return: string   HTML link tag with url as value of href

 

loadCss(url)

Processor use this method to retrieve external CSS file and use url as key to check Session Storage weather it exist in Session Storage, if does not exist then save url and css code as key/value pair to Session Storage

Parameters:

  Name    Type    Description  
urlstringURL or URL path to retrieve CSS files

Return: string   Empty string

 

loadPostProcessor()

Load postprocessor library

Return: string   HTML script tag to load Post-Processor library

 

section(mdl, tml)

A dummy function that is used in layout template

Parameters:

  Name    Type    Description  
mdlobject|string|number|boolean|nullAn JSON member value
tmlstringRaw template

Return: string   Empty string

 

 

XML-JSON APIs of Processor

 

string2Xml(xmlStr)

Convert XML string into XML document object

Parameters:

  Name    Type    Description  
xmlStrstringAn XML string

Return: object   An XML document object

 

xml2String(xmlObj)

Convert XML document object into serialized string

Parameters:

  Name    Type    Description  
xmlObjobjectAn XML document object

Return: string   A serialized string

 

xml2Json(xmlObj, verbosity, nesteAttr)

Convert XML document object into JSON

Parameters:

  Name    Type    Description  
xmlObjobjectAn XML document object
verbosityintegerAn optional verbosity level of conversion
nesteAttrbooleanTrue to nest attributes as value to keyAttrs, default is false

Return: object   A JSON

 

json2Xml(obj, namespaceURI, qualifiedName, documentType)

Convert JSON into XML document object

Parameters:

  Name    Type    Description  
objobjectA JSON
namespaceURIstringA optional namespace URI to XML document
qualifiedNamestringA optional prefix to root node of XML document
documentTypeobjectA optional XML documentType object

Return: object   An XML document object

 

 

PFCSS APIs of Processor

 

addCssUnit(expression, unit)

Return CSS amount and unit in success, or return "0" and print error message to browser console if failed

Parameters:

  Name    Type    Description  
expressionexpressionAn JavaScript expression
unitstringA option of CSS unit, default unit with px

Return: string   CSS amount and unit or "0"

 

dec2HexColor(expression)

Return color hexadecimal value in success, or return #fff and print error message to browser console if failed

Parameters:

  Name    Type    Description  
expressionexpressionAn JavaScript expression

Return: string   Formatted color hexadecimal value or $fff

 

hex2DecColor(hexColor)

Return color decimal value in success, or return 0 and print error message to browser console if failed

Parameters:

  Name    Type    Description  
hexColorstringColor hexadecimal value

Return: number   Formatted color decimal value or 0

 

isHexColor(color)

Return true if color is hexadecimal value, false if it is not

Parameters:

  Name    Type    Description  
colorstringCSS color value

Return: boolean   true or false

 

parseNestedCss(cssStr, option)

Parse generic nesting mechanism to CSS code

Parameters:

  Name    Type    Description  
cssStrstringNesting CSS code under a root node
optionstringA option object with minified property, default value: {minified: true}

Return: string   CSS code without nesting

 

prettyCss(cssStr)

Output pretty CSS code

Parameters:

  Name    Type    Description  
cssStrstringCompact CSS code

Return: string   CSS code

 

stripCssUnit(num)

Strip CSS unit to do unit calculation. Supported CSS units including cm, in, mm, pc, pt, ch, em, ex, gd, px, rem, vh, vw, vm, deg, grad, rad, turn, ms, s, hz and khz

Parameters:

  Name    Type    Description  
numstringCSS amount and unit

Return: string   CSS amount

 

getRemainedStorage()

Return size of Session Storage is able to stored new key/value pairs

Return: number   Integer or 0 if there is no JavaScript, CSS and HTML contents stored

 

getStoredValue(url, maxAge)

Get stored value from related URL and keeping stored time in maximum seconds if maxAge applied

Parameters:

  Name    Type    Description  
urlstringURL or URL path to retrieve CSS files
maxAgenumberOptional maximum seconds to store key/value pairs in Session Storage

Return: string   Stored value by PlannerFw or null if failed

 

getUrlStorageSize()

Return size of all key/value pairs in Session Storage

Return: number   Integer or -1 if there is no JavaScript, CSS and HTML contents stored

 

removeAllCollections()

Deletes all key/value pairs related with remote JavaScript, CSS and HTML files from Session Storage

Return: boolean   True at success or false if failed

 

removePartCollections(urls)

Converts URL string or array to key or key array, then deletes result key/value pairs in Session Storage

Parameters:

  Name    Type    Description  
urlsstring or objectA string or array of URLs

Return: boolean   True at success or false if failed

 

searchStoredKeys(regexp)

Return a object that contains key/related-URL pairs in Session Storage

Parameters:

  Name    Type    Description  
regexpstring or object or voidA regular expression object, a string will be converted to a regular expression object, or no parameter

Return: object   An object of key/related-URL pairs. Returns empty object if no match is not found.

 

searchStoredValues(regexp)

Return a object that contains URL/content pairs in Session Storage

Parameters:

  Name    Type    Description  
regexpstring or objectA regular expression object, or a string will be converted to a regular expression object

Return: object   An object of URL/content pairs. Returns empty object if no match is not found.

 

updateUrlStorage(url, value)

Saves URL or URL path as key and related file content as value to Session Storage

Parameters:

  Name    Type    Description  
urlstringURL or URL path to retrieve JavaScript, CSS and HTML files

Return: boolean   True at success or false if failed

 

 

Other Processor APIs

 

checkAllowedRequest(requ)

Return true if request URL in allowed hosts otherwise show error message and stop processing

Parameters:

  Name    Type    Description  
requstringRequest URL

Return: boolean  

 

checkEnvironment()

Check browser whether supports JSON, XMLHttpRequest and Cookie

Return: undefined  

 

checkResponseData(pfResp)

Check HTTP response data is received and data formats are legal otherwise show error message and stop processing

Parameters:

  Name    Type    Description  
pfRespJSONHTTP response data

Return: boolean   Return true if success

 

crc32CheckSum(str, radix)

Calculates a string's CRC32 checksum to check data integrity of stored data to see whether the data has been modified or changed and returns as a hexadecimal string

Parameters:

  Name    Type    Description  
strstringA string in Latin1 (ISO8859-1) character set
radixstring10 or 16, it can be omitted

Return: string   A decimal string if radix is 10 otherwise a hexadecimal string

 

errorHandler(errorLevel, errorMsg)

Show error message with Planner's error handler

Parameters:

  Name    Type    Description  
errorLevelstringPlannerFw defined error level from 0 to 3 errorMsgstringError message

Return: string   Escaped string

 

escapeRegExp(str)

Escape regular expression string

Parameters:

  Name    Type    Description  
strstringRegular expression

Return: string   Escaped string

 

minimizeCode(str)

Minimized code by strip white spaces and line breaks

Parameters:

  Name    Type    Description  
strstringCode to minimize

Return: string   Minimized code

 

omitProtocol(url)

Omit protocol from an URL

Parameters:

  Name    Type    Description  
urlstringA request UR

Return: string   An URL without protocol portion

 

pfCheckCrc(val, csm, Msg)

Check data weather matching gave CRC32 checksum, if does not match PlannerFw show error message and stop processing

Parameters:

  Name    Type    Description  
valstringOriginal data
csmstringdecimal string of CRC32 checksum
msgstringError message

Return: boolean   True

 

pfDecryption(str, pword)

Decrypt data and return result

Parameters:

  Name    Type    Description  
strstringPlain text
pwordstringOptional password for encryption

Return: string   cypher text

 

pfEncryption(str, pword)

Encrypt data and return result

Parameters:

  Name    Type    Description  
strstringPlannerFw encrypted data
pwordstringOptional password for decryption

Return: string   plain text

 

responseJsonEncode(rdata)

Return encoded JSON otherwise show error message and stop processing

Parameters:

  Name    Type    Description  
rdatastring or objectA string or JSON object

Return: object   A JSON

 

secureMode(jdata)

Strip type notations and return true if all JSON members are legal types otherwise show error message and stop processing

Parameters:

  Name    Type    Description  
jdataobjectAn JSON

Return: undefined  

 

tab2Space(str)

Convert tabs in CSS code into spaces

Parameters:

  Name    Type    Description  
strstringCSS code

Return: string   CSS code

 

 

Postprocessor APIs

 

Postprocessor APIs are APIs that would be used in result page that was generated by PlannerFw, you could use Postprocessor APIs to access CSS contents in Session Storage, switch style with contents from Session Storage, and access external files over HTTP or WebSocket.

 

errorHandler(errorLevel, errorMsg)

Show error message with Planner's error handler

Parameters:

  Name    Type    Description  
errorLevelstringPlannerFw defined error level from 0 to 3 errorMsgstringError message

Return: string   Escaped string

 

crc32CheckSum(str, radix)

Calculates a string's CRC32 checksum to check data integrity of stored data to see whether the data has been modified or changed and returns as a hexadecimal string

Parameters:

  Name    Type    Description  
strstringA string in Latin1 (ISO8859-1) character set
radixstring10 or 16, it can be omitted

Return: string   A decimal string if radix is 10 otherwise a hexadecimal string

Note: It is a same API in Processor Library

 

getQueryData(data)

Prepare data to send to server for httpRequest and wsRequest

Parameters:

  Name    Type    Description  
dataobjectA name/value pairs data object to send to server, valueS could include one of [value, "pfcrc"], [value, "pfenc"] or [value, "pfvld", "type_label"], for example, {"key1": ["book", "pfenc"], "key2": ["year 2016", "pfcrc"], "key3": [257, "pfvld", "intnum"]}

Return: string   Query string of key/value pairs

 

getRemainedStorage()

Return size of Session Storage is able to stored new key/value pairs

Return: number   Integer or 0 if there is no JavaScript, CSS and HTML contents stored

Note: It is a same API in Processor Library

 

getStoredValue(url)

Return stored value from related URL

Parameters:

  Name    Type    Description  
urlstringURL or URL path to retrieve external CSS files

Return: string   Stored value by PlannerFw or null if failed.

Note: It is a same API in Processor Library

 

httpRequest(url, data, method, responseType)

Submit HTTP requests with HTTP method, response type and data with encryption, crc32 checksum and validation

Parameters:

  Name    Type    Description  
urlstringAn URL without query part
dataobjectA name/value pairs data object to send to server, value could include one of "pfcrc", "pfenc" or ["pfvld", "type_label"]
methodstringOne of "Head", "Get", "POST", "PUT" and "DELETE"
responseTypestringOne of "", "text", "xml", "json", "document", "blob" and "arraybuffer"

Return: string or boolean   Return response contents or false if failed.

 

jscssRequest(url, type, cbfunc)

Load external JavaScript or CSS file then execute a callback function

Parameters:

  Name    Type    Description  
urlstringAn URL of external JavaScript or CSS file
typestringOne of "text/javascript" and "text/css"
dbfuncfunctionOptional callback function name

Return: string or boolean   Execute callback function after external file loaded or false if error

 

md5Hash(str)

Calculate the md5 hash of a string

Parameters:

  Name    Type    Description  
strstringA string

Return: string   A md5 hash string

 

pfCheckCrc(val, csm, Msg)

Check data weather matching gave CRC32 checksum, if does not match PlannerFw show error message and stop processing

Parameters:

  Name    Type    Description  
valstringOriginal data
csmstringdecimal string of CRC32 checksum
msgstringError message

Return: boolean   True

Note: It is a same API in Processor Library

 

pfDecryption(str, pword)

Decrypt data and return result

Parameters:

  Name    Type    Description  
strstringPlain text
pwordstringOptional password for encryption

Return: string   cypher text

Note: It is a same API in Processor Library

 

pfEncryption(str, pword)

Encrypt data and return result

Parameters:

  Name    Type    Description  
strstringPlannerFw encrypted data
pwordstringOptional password for decryption

Return: string   plain text

Note: It is a same API in Processor Library

 

searchStoredKeys(regexp)

Return a object that contains key/related-URL pairs in Session Storage

Parameters:

  Name    Type    Description  
regexpstring or object or voidA regular expression object, a string will be converted to a regular expression object, or no parameter

Return: object   An object of key/related-URL pairs. Returns empty object if no match is not found

Note: It is a same API in Processor Library

 

searchStoredValues(regexp)

Return a object that contains URL/content pairs in Session Storage

Parameters:

  Name    Type    Description  
regexpstring or objectA regular expression object, or a string will be converted to a regular expression object

Return: object   An object of URL/content pairs. Returns empty object if no match is not found

Note: It is a same API in Processor Library

 

secureField(target, data)

Replace input value with PlannerFw encryption, add new input elements with validation or CRC32 checksum as value to HTML form

Usage: add onsubmit="return postplanner.secureField(this, your_data_object)" to HTML form of web page

Parameters:

  Name    Type    Description  
targetobjectA form element to contain new input element(s)
dataobjectA name/value pairs object, name is a field name of query, value is one of "pfcrc", "pfenc" or ["pfvld", "type_label"], for example, {"field1": "pfenc", "field2": "pfcrc", "field3": ["pfvld", "unistr"]}

Return: boolean   True

 

secureUrl(url, data)

Replace value with PlannerFw encryption, add new validation or CRC32 checksum field(s) to use query string

Parameters:

  Name    Type    Description  
urlstringAn URL with query string
dataobjectA name/value pairs object, name is a field name of query, value is one of "pfcrc", "pfenc" or ["pfvld", "type_label"], for example, {"field1": "pfenc", "field2": "pfcrc", "field3": ["pfvld", "unistr"]}

Return: string   new URL

 

styleId2Url(id)

Converts style id to URL or URL path

Parameters:

  Name    Type    Description  
idstringStyle tag id that was generated by importCss method of Planner object

Return: string   URL or URL path

 

switchCssById(styleId, toStyleUrl)

Switches style with id generated by importCss method of PlannerFw object and style stored by Session Storage

Parameters:

  Name    Type    Description  
styleIdstringStyle tag id that was generated by importCss method of Planner object
toStyleUrlstringURL or URL path

Return: boolean   True at success or false if failed.

 

switchCssByUrl(styleUrl, toStyleUrl)

Convert URL to id then switches style with id generated by importCss method of PlannerFw and style stored by Session Storage

Parameters:

  Name    Type    Description  
styleUrlstringURL or URL path related to style tag id of current page
toStyleUrlstringURL or URL path

Return: boolean   True at success or false if failed.

 

url2StyleId(url)

Converts URL or URL path to style id used by current page

Parameters:

  Name    Type    Description  
urlstringURL or URL path

Return: string   Style id used by current page.

 

wsRequest(url, data)

Send request to WebSocket server then close connection after response received

Parameters:

  Name    Type    Description  
urlstringAn URL without query part
dataobjectA name/value pairs data object to send to server, valueS could include one of "pfcrc", "pfenc" or ["pfvld", "type_label"]

Return: string or boolean   Data string sent by WebSocket server or boolean if WebSocket communication failed.

 

 

Model except JSON could be XML format, PlannerFw does not directly use XML to template, PlannerFw transforms XML data into JSON at first then use it with template to generate result page, some constraints to XML data model include:

XML documents must have a root element, and root element's attributes would not be processed.

All XML elements must have a closing tag.

XML tags are case sensitive.

XML attribute values must be quoted.

Always replacing characters < > & ' " inside an XML node with < > & ' " respectively.

Comments in XML are similar to Comments in HTML.

XML data model has to include elements expiration, encryption, count and pfDataSet or pfDataSet1, pfDataSet2, pfDataSet3...

PlannerFw can transform general XML, XML Schema, WSDL, WDDX, RSS, XHTML and SVG into JSON and JSON to XML on the contrary, syntax rules of transforming XML and JSON include:

Only XML node types are ELEMENT_NODE(type 1),TEXT_NODE(type 3) and CDATA_SECTION_NODE(type 4) can be transformed.

Document prologue and comments would be neglected.

An XML element without value can be expressed as <node_name> <node_name> or <node_name />

If an XML element with attributes then attribute would be add a prefix _ on name, key name of element value would be "keyVal" in result JSON.

Date in XML would not be parsed.

XML element name can include prefix.

Other rules of XML elements.

PlannerFw does not support encryption of model in XML formats at present, XML data model is always transferred with plain text over network.

An example of transforming XML code to JSON,

XML JSON

<?xml version="1.0" encoding="UTF-8" ?>
<!-- An XML needs a root node with any valid name,
     and root node could be with namespace declaring -->
<root>
  <description>Example model with XML formats</description>
  <expiration>0</expiration>
  <pfDataSet>          
    <!-- Data part that would be used in templates  -->
    <item>NO1295</item>
    <price>19.25</price>
    <size type="Medium">
      <color image="red_cardigan.jpg">Red</color>
      <color image="burgundy_cardigan.jpg">Burgundy</color>
    </size>
    <script type="text/javascript">
      <![CDATA[function matchwo(a,b) {
        if (a < b && a < 0) { return 1; 
        } else { return 0; }}]]>
    </script>
  </pfDataSet>
</root>
			

{
"root":{
  "description": "Example model with XML formats",
  "expiration": 0,
  "pfDataSet":
  {
    "item": "NO1295",
    "price": 19.25,
    "size":{
      "color":[{"_image":"red_cardigan.jpg", "keyVal":"Red" },
        {"_image":"burgundy_cardigan.jpg","keyVal":"Burgundy"}
      ],
      "_type": "Medium"
    },
    "script":{
      "_type": "text/javascript",
      "keyVal": "function matchwo(a,b){if (a < b && a < 0) \
				{return 1;} else {return 0;}}"
    }
  }
}
}

XSLT stands for XSL Transformations, XSLT transforms XML into other XML documents such as HTML, We compare XMLT and PlannerFw solution with a sample from https://www.w3schools.com/xml/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog

Sample XML and XSLT

XML XSLT

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your heart</title>
    <artist>Bonnie Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <title>Greatest Hits</title>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <company>RCA</company>
    <price>9.90</price>
    <year>1982</year>
  </cd>
</catalog>

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <html>
  <body>
  <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th style="text-align:left">Title</th>
        <th style="text-align:left">Artist</th>
      </tr>
      <xsl:for-each select="catalog/cd">
      <tr>
        <td><xsl:value-of select="title"/></td>
        <td><xsl:value-of select="artist"/></td>
      </tr>
      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>

</xsl:stylesheet>

				

Equivalent XML data model and raw template in PlannerFw

XML data model Raw template

<?xml version="1.0" encoding="UTF-8"?>
<root>
<description>XSLT features with PlannerFw
</description>
<expiration>0</expiration>
<pfDataSet>
<catalog>
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your heart</title>
    <artist>Bonnie Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <title>Greatest Hits</title>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <company>RCA</company>
    <price>9.90</price>
    <year>1982</year>
  </cd>
</catalog>
</pfDataSet>
</root>

<!DOCTYPE html>
<html>

<body>
<h2>My CD Collection</h2>
<table border="1">
  <tr bgcolor="#9acd32">
    <th style="text-align:left">Title</th>
    <th style="text-align:left">Artist</th>
  </tr>
  <!--% 
    /* 
	 * Multiple line comments 
	 *
	 */
    pfDataSet = pfDataSet.catalog;
	
    for(var key in pfDataSet.cd) 
	{
      <!-%= 
          // Single line comment to output tag
          "<tr><td>" +  
          pfDataSet.cd[key].title + 
          "</td><td>" + 
          pfDataSet.cd[key].artist +
          "</td></tr>"
      %->
    }
  %--> 
</table>
</body>

</html>
            

PlannerFw uses URL /app/index.html?tml=/template/pfm/d/index.html.js&xmdl=/samples/xml-for-xslt-with-plannerfw.html to transform template and XML data model into HTML page, XSLT needs an XSLT Processor to implement transformation

 

HTTP request header

Referer always is Page Processor to all HTTP requests for model and template.

The relationship of X-Requested-With and HTTP Request type:

Value of X-Requested-With HTTP Request type
ModelXMLHttpRequestJSON data model
XModelXMLHttpRequestXML data model
TemplateXMLHttpRequestTemplate
LayoutTemplateXMLHttpRequestLayout template
PFCSSXMLHttpRequestPFCSS
OtherXMLHttpRequestAll others except previous requests
XML data model

[Response with XML data model]

 

User authentication

PlannerFw supports general HTTP user authentication and role based authentication and authorization policies, for example, session-based authentication, database-based authentication and authorization, PlannerFw also supports stateless authentication through JWT(JSON Web Token) and Cookie.

PlannerFw supports user authentication over WebSocket through JWT(JSON Web Token) and Cookie.

 

Data transmission

PlannerFw supports data transmission security with:

Encrypt model data on server, decrypt model data on client.

Add 32-bits CRC checksum to data member on server, check data with checksum on client

Add type and constraints to data member on server, validate data with type and constraints on on client

Encrypt data, add checksum or add type validation to data before sent by HTTP or WwebSocket requests, do related processes with pfcomponent on server.

Encrypted mode of JSON

[Encrypted mode of JSON]

 

Crossing-domains data access

PlannerFw models, templates and /app/index.html have to share the same host, client can not retrieve model or template from different host with /app/index.html having. web server can use server-side proxy accessing models and templates that are provided by different host to implement crossing-domains models or templates for the client

PlannerFw releases provide a example that server retrieves Google News RSS Feeds then output it as model in response for reference.

 

Session Storage

PlannerFw implemented following procedures to secure Session Storage:

Sets maximum seconds as expiration to all key/value pairs in Session Storage.

Applies checksum of CRC32, a 32-bit cyclic redundancy check to verify stored data integrity, against unauthorized data modification.

Checks remaining size of Session Storage before data append/update to it.

Disabled clear() method of sessionStorage to delete all stored data.

PlannerFW always check all these procedures before read, append or update key/value pairs in Session Storage so that ensures key/value pairs in Session Storage are readable and writeable for Page Preprocessor and read only in result pages safely.

Session Storage

[PlannerFw's Session Storage]

 

Install PlannerFw to development server

At first you need to sign in the computer with root user or administrator permission, then release PlannerFw archive, copy files and directories in siteroot folder to website root directory, copy pfcomponent folder and pfdevelop folde to the parent directory of website root directory, at last do setting for your copies according environment of development server, installed paths and user guide. Do not install pfcomponent folder and pfdevelop folder to website root directory.

Preprocessor server in pfdevelop folder could automatically compile and transform raw-template files to JavaScript files when file size was changed, build tool in pfdevelop folder would create an archive for product server from template, layout and css/pfcss under website root directory. Do not install pfdevelop folder to production server.

If URL Rewrite are set to the server, update setting to allow PlannerFw's URLs getting through it.

 

Install PlannerFw to production server

At first sign in production server with proper permission, then release PlanenrFw archive, copy files and directories in siteroot folder to website root directory of production server, delete invalid files and directories from copies, for example, test and docs folders, plannerfw-demos.html and plannerfw-demos-php.html files, then copy related server-language folder from pfcomponent folder to production server, at last do setting for your copies according environment of production server, installed paths and user guide. Do not install pfcomponent folder to website root directory.

If URL Rewrite are set to the server, update setting to allow PlannerFw's URLs getting through it.

 

Development, maintenance and upgrade

Servers only are model provider for all PlannerFw websites or web applications, web servers are not responsible for any creation of templates, so you are able to separate model creation from entire development or maintenance and take it as a isolation part from development or maintenance, user are able to follow these steps to speed development, maintenance and upgrade

  1. Create all static HTML pages and related CSS files.
  2. Design output model data formats according to your business logic, create some instance for designed models, save model instances to file extension with json, xml or other server supports in website root directory.
  3. Run Preprocessor server from pfdevelop/preprocessor/pfwatch.exe (there is no file extension to Unix and OS X).
  4. Write data presentation with PlannerFw tags to use model instances to static page files.
  5. Modify CSS code for static pages with mechanisms that PlannerFw supports.
  6. Use pfComponent in PlannerFw Exec to create your customized code for WebSocket and JWT(JSON Web Token) support, model member validation, check and encryption/decryption, server-side proxy and client errors log.
  7. Test templates and model instances with /app/index.html and /app/be-index.html
  8. Repeats step 4 to 7 to complete all development of raw templates and PFCSS files.
  9. Develop server-side code to provide real models instead of model instances.
  10. Testing and QA (Quality Assurance) processing.
  11. Run DeployBuilder from pfdevelop/deploybuilder/pfbuild.exe(there is no file extension to Unix and OS X) to generate deployment archive.
  12. Copy deployment archive to website root directory on production server, then release and overwrite existed directories and files on production server.
  13. Deploy server-side code for the project from development server to production server

If you have any questions or problems during development or maintenance, please refers PlannerFw documentation or send your question tickets to us.

 

Documentation and help

PlannerFw documentation: siteroot/docs/user-guide.html or http://w3plan.net/customer/plannerfw

General inquiries and feedback: contact@w3plan.net

Purchase and licensing inquiries: sales@w3plan.net

Send question tickets: http://w3plan.net/support/index

 

PlannerFw demo without web server required

To quickly evaluate main features of PlannerFw without web server support, you can download SiteRoot archive from https://github.com/w3plan/SiteRoot, release it on your computer then go to http://nwjs.io/downloads and download a normal version for your computer OS, install it, at last run NW.js executive file with the path that SiteRoot released as parameter from computer command line.

You can open DevTools for each examples with keyboard shortcut F12 on Windows and Linux or ++i on Mac.

 

Browser supports

Browser versions and supports:

Chrome Firefox IE Opera Safari iOS Safari Android Browser Blackberry Browser
        
5+3.5+8.0+10.5+4.0+4.1+2.1+7+
        

 

PlannerFw can track processing times from the browser received request for Page Processor(/app/index.html) and planner.min.js loading completion to result page rendered completion, PlannerFw's time tracking mechanism include printing processes elapsed time in milliseconds on the browser console, save processing times to session Cookie pf_times at time of page rendering, web server would can receive Cookie pf_times at next request for Page Processor.

PlannerFw uses property trackExecutedTime of Processor configure to control processes and time tracking, if its value is false, Page Processor will not track processes and time, but still printing total elapsed time on the browser console at result page rendered completion. A calculation formula of PlannerFw processes and time would be:

Total elapsed time = Phase 1 + Phase 2 + Phase 3 + Phase 4 + Phase 5

The value format of Cookie pf_times are Phase 1 . Phase 2 . Phase 3 . Phase 4 . Phase 5

PlannerFw processes and times tracking

 

The value format of Cookie pf_times are Phase 1 . Phase 2 . Phase 3 . Phase 4 . Phase 5, each phase includes processes are:

 

Processes in Phase 1

Check the browser support, parse URL of template and URL of model from query string, check URLs, set process routes

Processes in Phase 2

Retrieve model from URL over HTTP/HTTPS or WebSocket/WebSocket Secure connection, if model is XML then transform it into JSON, model member decryption, CRC checksum check and type validation.

Processes in Phase 3

Retrieve template from URL.

Processes in Phase 4

Use URL of importCss or loadCss method as key to check whether CSS contents exists in Session Storage, if exists then read it out from Session Storage otherwise retrieve it from network and save URL and CSS contents as name/value pair to Session Storage.

Processes in Phase 5

Generate result page code from model and template, render result page code, load postprocess library and other JavaScript libraries, load related CSS files, images, media or flash animation.

 

PlannerFw Exec's Page Processor be-index.html could take over all views of server-side framework as long as controller pass variables of template URL and model to view be-index.html(extension depends on server types, for example, php, jsp, aspx), we will introduce how PlannerFw works with different server-side frameworks based on suppose that controller pass variables with "/template/pfm/a/personlist.html.js" and following value to views.

{
    "description": "Example of PlannerFw model",
    "expiration": 0,
    "pfDataSet": {
        "id": 1,
        "name": "Rod Wiley", 
        "gender": "Male", 
        "dob": "1985-09-09"
    }
}

 

1. PlannerFw with PHP frameworks

 

1.1. PlannerFw working with CodeIgniter

Actions of controllers include:

    // assigned PlannerFw template url
    $data['pftml'] = "/template/pfm/a/personlist.html.js";
    
    // assigned PlannerFw model variable 
    $person = array(
                    "id" => 1, 
                    "name" => "Rod Wiley", 
                    "gender" => "Male", 
                    "dob" => "1985-09-09"
                );
   	$person = json_encode($person);
    $data['pfmdl'] = <<<EOD
{
  "description": "Example of PlannerFw model",
  "expiration": 0,
  "pfDataSet": $person
}
EOD;
    $this->load->view('be-index', $data);

View file application/views/be-index.php includes:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>View of CodeIgniter</title>

    <!-- Apply Page Processor Library -->
    <script src="/app/lib/planner.min.js"></script>

    <!-- Output a template URL and model -->
    <script type="text/javascript">
        var pftml = "<?php echo $pftml ?>";
        var pfmdl = <?php echo $pfmdl ?>;
    </script>

    <!-- Apply Page processor -->
    <script src="/app/lib/be-processor.min.js"></script>

    </head>
    <body>
    </body>
    </html>

 

1.2. PlannerFw working with Zend Framework

Actions of controllers include:

    // assigned PlannerFw template url
    $this->view->data['pftml'] = "/template/pfm/a/personlist.html.js";
    
    // assigned PlannerFw model variable 
    $person = array(
                    "id" => 1, 
                    "name" => "Rod Wiley", 
                    "gender" => "Male", 
                    "dob" => "1985-09-09"
                );
   	$person = json_encode($person);
    $this->view->data['pfmdl'] = <<<EOD
{
  "description": "Example of PlannerFw model",
  "expiration": 0,
  "pfDataSet": $person
}
EOD;
    $this->load->view('be-index', $data); 

View file application/views/be-index.php includes:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>View of Zend Framework</title>

    <!-- Apply Page Processor Library -->
    <script src="/app/lib/planner.min.js"></script>

    <!-- Output a template URL and model -->
    <script type="text/javascript">
        var pftml = "<?php echo $this->data['pftml'] ?>";
        var pfmdl = <?php echo $this->data['pfmdl'] ?>;
    </script>

    <!-- Apply Page processor -->
    <script src="/app/lib/be-processor.min.js"></script>

    </head>
    <body>
    </body>
    </html> 

 

2. PlannerFw with Java

Java servlets include:

    // assigned PlannerFw template url
    request.setAttribute("pftml", "/template/pfm/a/personlist.html.js");
    
    StringWriter out = new StringWriter();
    
    HashMap pa = new HashMap();
    pa.put("id", 1);
    pa.put("name", "Rod Wiley");
    pa.put("gender", "Male");
    pa.put("dob", "1985-09-09");
    
    JSONObject pajs = new JSONObject(pa);
    
    pajs.writeJSONString(out);
    String jsonPa = out.toString();
    
    JSONObject mdl = new JSONObject();
    mdl.put("description", "Example of PlannerFw model");
    mdl.put("expiration", 0);
    mdl.put("pfDataSet", jsonPa);
    
    mdl.writeJSONString(out);    
    String pfmdl = out.toString();
    
    // assigned PlannerFw model variable
    request.setAttribute("pfmdl", pfmdl);
    request.getRequestDispatcher("/WEB-INF/be-index.jsp").forward(request, response);

Jsp file /WEB-INF/be-index.jsp includes:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    <title>View of Java framework</title>

    <!-- Apply Page Processor Library -->
    <script src="/app/lib/planner.min.js"></script>

    <!-- Output a template URL and model -->
    <script type="text/javascript">
        var pftml = "${pftml}";
        var pfmdl = ${pfmdl};
    </script>

    <!-- Apply Page processor -->
    <script src="/app/lib/be-processor.min.js"></script>

    </head>
    <body>
    </body>
    </html> 

 

3. PlannerFw with ASP.NET

Actions of controllers include:

    ViewData["pftml"] = "/template/pfm/a/person-list.html.js";
    
    public class Person {
        public string id {
            get;
            set;
        }
        public string name {
            get;
            set;
        }
        public string gender {
            get;
            set;
        }
        public string dob {
            get;
            set;
        }
    }
    
    Person person = new Person();
    person.id = 1;
    person.name = "Rod Wiley";
    person.gender = "Male";
    person.dob = "1985-09-09";
    
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    
    String mdl1 = "{\"description\":\"Example of PlannerFw model\", \"expiration\":0, \"pfDataSet\":";
    string mdl2 = "}";    
    ViewData["pfmdl"] = mdl1 + serializer.Serialize(person).ToString() + mdl2;
    
    return View("be-index"); 

view file includes:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>View of ASP.NET framework</title>

    <!-- Apply Page Processor Library -->
    <script src="/app/lib/planner.min.js"></script>

    <!-- Output a template URL and model -->
    <script type="text/javascript">
        var pftml = '<%= ViewData["pftml"] %>';
        var pfmdl = <%= ViewData["pfmdl"] %>;
    </script>

    <!-- Apply Page processor -->
    <script src="/app/lib/be-processor.min.js"></script>

    </head>
    <body>
    </body>
    </html> 

 

4. PlannerFw with Ruby

Actions of controllers include:

    def be_index
        @pftml = "/template/pfm/a/personlist.html.js"
        
        person = Hash[
                    "id" => 1, 
                    "name" => "Rod Wiley", 
                    "gender" => "Male", 
                    "dob" => "1985-09-09"
                ]        
        mdl = Hash[
                    "description" => "Example of PlannerFw model",
                    "expiration" => 0,
                    "pfDataSet" => person
                ]
        
        require 'json'        
        @pfmdl = mdl.to_json
    end 

View file app/views/novel/be_index.rhtml includes

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>View of Ruby framework</title>

    <!-- Apply Page Processor Library -->
    <script src="/app/lib/planner.min.js"></script>

    <!-- Output a template URL and model -->
    <script type="text/javascript">
        var pftml = "<%= @pftml %>";
        var pfmdl = <%= @pfmdl %>;
    </script>
    
    <!-- Apply Page processor -->
    <script src="/app/lib/be-processor.min.js"></script>
    
    </head>
    <body>
    </body>
    </html>