text:
<?php
namespace nano;

/**********************************************************************************

Low level functions that provide login, database connexion and error handeling

***********************************************************************************/

// setup the global variables and check the user access and load the config files
function init($use)
{
  global $lbl;
  global $schemas;
  global $languages;
  global $lang;
  global $databases;
  global $user;
  global $script_use;
  global $project;

  $languages=[];
  $databases=[];

  //setup user for first query
  $user=["_id"=>0, "user"=>"" , "level"=>0 ,"is_admin"=>false , "group"=>["_id"=>1] ];
  $user["groups"]=[0];

  $schemas=(object) array();
  $script_use=$use;

  // ***** get language or use the french
  $lang="fr";
  if (isset($_COOKIE["lang"])) $lang=$_COOKIE["lang"];

  // include the structure and the labels
  if(file_exists (__DIR__."/../nano_config.php"))
  {
    include(__DIR__."/../nano_config.php");
  }
  else
  {
    die("There must be a nano config file in your project");
  }

  // Check if config file is ok
  if($project=="") die ("Give a name to your project in nano_config.php file!");
  if(!isset($databases["nano"])) die ("Please configure a nano database connection in nano_config.php!");

  // include languages translations
  if(file_exists (__DIR__."/../nano_label_$lang.php")) include(__DIR__."/../nano_label_$lang.php");

  // define the defaut tables
  $schemas->_users=
  [
    "name"=>"_users",
    "database"=>"nano",
    "table"=>"_users",
    "structure"=>
    [
      [
        "name"=>"user",
        "type"=>"string",
        "regex"=>"^[0-9a-zA-Z]{1,10}$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("user"),
        "display"=>["list","form","find","div"]
      ],
      [
        "name"=>"email",
        "type"=>"string",
        "regex"=>"^[a-zA-Z0-9._-]*@[a-zA-Z0-9.-]*\.[a-zA-Z]{2,4}$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("email"),
        "display"=>["list","form","find","div"]
      ],
      [
        "name"=>"password",
        "type"=>"password",
        "regex"=>"^.*$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("password"),
        "display"=>["list","form","find","div"]
      ],
      [
        "name"=>"level",
        "type"=>"number",
        "regex"=>"^[0-9]*$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("level"),
        "display"=>["list","form","find","div"]
      ],
      [
        "name"=>"is_admin",
        "type"=>"boolean",
        "regex"=>"^[0-1]$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("Is admin"),
        "display"=>["list","form","find","div"]
      ],
      [
        "name"=>"group",
        "type"=>"key",
        "schema"=>"_group",
        "regex"=>"^.*$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("Main group"),
        "display"=>["list","form","find","div"]
      ],
      [
        "name"=>"groups",
        "type"=>"multiplekey",
        "schema"=>"_group",
        "regex"=>"^.*$",
        "optional"=>true,
        "col"=>"col-xs-12",
        "clearall"=>true,
        "label"=>lbl("Groups"),
        "display"=>["list","form","find","div"]
      ],
    ]
  ];

  $schemas->_group=
  [
    "name"=>"_group",
    "database"=>"nano",
    "table"=>"_group",
    "structure"=>
    [
      [
        "name"=>"name",
        "type"=>"string",
        "regex"=>"^.*$",
        "optional"=>false,
        "col"=>"col-xs-4",
        "clearall"=>false,
        "label"=>lbl("genre"),
        "display"=>["find","list", "form", "div"]
      ]
    ]
  ];

  // add the user defined tables
  if(file_exists (__DIR__."/../nano_schemas.php")) include(__DIR__."/../nano_schemas.php");
  // add the users library
  if(file_exists (__DIR__."/../".$project.".php")) include(__DIR__."/../".$project.".php");

  // Connect to the datasource
  $users=new data(true);
  $users->connect($schemas->_users);

  // Create the tables
  $users->create($schemas->_users);
  $users->create($schemas->_group);

  // check if the user is logged in
  $email="";
  $password="";
  if (isset($_COOKIE["email"])) $email=$_COOKIE["email"];
  if (isset($_COOKIE["password"])) $password=$_COOKIE["password"];

  $query=["email"=>$email, "password"=>$password];
  $result=$users->select($schemas->_users,$query);
  if(count($result)==0)
  {
    $query=[];
    $result=$users->select($schemas->_users,$query);
    if(count($result)==0)
    {
      // add the default admin group
      $data=(object) array("name"=>"admin");
      $users->upsert($schemas->_group,[$data]);

      // add the default admin user
      $data=(object) array("user"=>"admin","email"=>"admin@admin.com","password"=>"admin","level"=>"999","is_admin"=>1, "group"=> (object) array("_id"=>1, "name"=>"admin"), "groups"=>[] );
      $users->upsert($schemas->_users,[$data]);
    }
    echo header();
    echo loginForm();
    echo footer();
    die();
  }
  else
  {
    $user=$result[0];
    // add default group to groups
    $user["groups"][]=$user["group"]["_id"];
  }

}

// simple login form
function loginForm()
{

  $html="

    <form method='post' action='nanofw/nano_login.php' class='col-xs-4 col-xs-offset-4' >

".lbl("Sign in")."

        ".lbl("Email")."
        <input id='email' type='email' name='email' class='form-control'  placeholder='".lbl("Email")."'>

        Password
        <input id='password' type='password' name='password' class='form-control'  placeholder='".lbl("password")."'>

      <input type='submit' class='btn btn-primary' value='".lbl("Login")."'>
    </form>

";

  return $html;

}

// handle the errors messages
function error($message,$code)
{
  switch($GLOBALS["script_use"])
  {
    case "webservice":
      $return=[];
      $return["end"]=microtime();
      $return["code"]=$code;
      $return["message"]=$message;
      $return["status"]="ERROR";
      return JSON_encode($return);
      break;
    case "page":
    $html=header();
    $html.="

    Error $code : $message

";
    $html.=footer();
    return $html;
    break;
  }

}

// provide translated text
function lbl($txt)
{
  $lbl=$GLOBALS['lbl'];
  if(isset($lbl[$txt])) return $lbl[$txt];
  return $txt; //ucfirst(trim(str_replace("_"," ",$txt)));
}

// provide database connection and management
class data
{

  // we need the schemas
  public function __construct($is_admin)
  {
    $this->schemas=$GLOBALS['schemas'];
    $this->databases=$GLOBALS['databases'];
    $this->languages=$GLOBALS['languages'];
    $this->lang=$GLOBALS['lang'];;
    $this->user=$GLOBALS['user'];
    $this->admin=$this->user['is_admin'];

    // of if we force it (to search for login)
    if($is_admin) $this->admin=true;

  }

  // connect to the database
  function connect($schema)
  {
    $dsn=$this->databases[$schema["database"]]["dsn"];
    $user=$this->databases[$schema["database"]]["user"];
    $password=$this->databases[$schema["database"]]["password"];
    $dbname=$this->databases[$schema["database"]]["dbname"];

    // connection to the server
    try
    {
      $this->conn = new \PDO($dsn,$user,$password);
      $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
      $this->conn->exec("SET CHARACTER SET utf8");
      $this->conn->exec("SET lc_time_names =".$this->languages[$this->lang]["sql_format"]);
    }
    catch (Exception $e)
    {
      die(error($e->getMessage(),-1));
    }

    // use the database
    try
    {
      $query="create database if not exists $dbname;";
      $stmt = $this->conn->prepare($query);
      $stmt->execute();
      $this->conn->exec("use $dbname");
    }
    catch (Exception $e)
    {
      die(error($e->getMessage(),-1));
    }

    return true;

  }

  // Create the table dans column in the database using a schema
  function create($schema)
  {
    // alter table
    $alters=[];

    // the schema must have been defined
    if (!isset($schema["table"])) die(error("There must be a table name",-1));

    // the schema must have been defined
    if (!isset($this->databases[$schema["database"]]["create"])) die(error("Please setup the 'create' flag of your database",-1));
    if ($this->databases[$schema["database"]]["create"]==false) return  false; //die(error("Create is not allowed on this table",-1));

    // create the table if it does not exist
    //
    // status are the following
    // 0 visible
    // 7 locked
    // 9 deleted
    //

    // do not allow use of the table is it is only for admin
    if (!$this->admin) if(substr($schema["table"],0,1)=="_") die(error("error trying to create table ".$schema["table"]." : tables starting with _ are for internal use only!",-11));

    //Créate the table
    $query="create table if not exists `".$schema["table"]."` (";
    $query.=" `_id` int(10) unsigned primary key auto_increment,";
    $query.=" `_user_id` int(10),";
    $query.=" `_group_id` int(10),";
    $query.=" `_level` int(10),";
    $query.=" `_ip` varchar(16),";
    $query.=" `_time` datetime,";
    $query.=" `_color` varchar(8),";
    $query.=" `_status` int(2) unsigned default 0";
    $query.=");";

    try
    {
      $stmt = $this->conn->prepare($query);
      $stmt->execute();
    }
    catch (Exception $e)
    {
      die(error($e->getMessage()." : ".$query,-9));
    }

    // Get the columns in the table
    $query="show columns from `".$schema["table"]."`;";
    $stmt = $this->conn->prepare($query);
    $stmt->execute();
    $stmt->setFetchMode(\PDO::FETCH_ASSOC);
    $db_schema = $stmt->fetchAll();

    // Create columns if it does not exists according to the schema
    foreach($schema["structure"] as $field)
    {
      $field_name=$field["name"];

      // do not allow creation of columns starting with _
      if(substr($field_name,0,1)=="_") die(error("error trying to create column ".$field_name." of ".$schema["table"]." : columns starting with _ are for internal use only!",-10));

      $tmp_languages=[""=>""];
      if($field["type"]=="translate") $tmp_languages=$this->languages;
      foreach($tmp_languages as $lkey=>$lvalue)
      {
        $field_name=$field["name"];

        if($field["type"]=="translate") $field_name="_".$lkey."_".$field_name;

        // check if column exist
        $exist=false;
        foreach($db_schema as $db_column) if(strtoupper($db_column["Field"])==strtoupper($field_name)) $exist=true;

        // field name starting with à "_" are reserved for internal use only
        if($exist==false)
        {
          $query_action="add `".$field_name."` ";
          switch($field["type"])
          {
          case "key":
            $alters[]=$query_action." int(10) unsigned";
            break;
          case "integer":
          case "number":
            $alters[]=$query_action." int(10)";
            break;
          case "double":
          case "float":
            $alters[]=$query_action." float(10,4)";
            break;
          case "translate":
          case "string":
          case "hidden":
            $alters[]=$query_action." varchar(255)";
            break;
          case "text":
            $alters[]=$query_action." text";
            break;
          case "boolean":
          case "checkbox":
            $alters[]=$query_action." tinyint(1) unsigned";
            break;
          case "date":
            $alters[]=$query_action." date";
            break;
          case "dateTime":
            $alters[]=$query_action." datetime";
            break;
          case "select":
          case "list":
            if (!is_array($field["enum"])) die(error("field ".$field_name." should have and enum array!",-13));
            $alters[]=$query_action." enum('".(implode("','",$field["enum"]))."')";
            break;
          case "password":
            $alters[]=$query_action." varchar(255)";
            break;
          case "multiplekey":
          case "json":
            // once Mysql 5.7 will be awalable we will change this to json type
            $alters[]=$query_action." TEXT";
            break;
          }
        }
      }
    }

    if(count($alters)>0)
    {
      $query="alter table `".$schema["table"]."` ".implode(",",$alters).";";

      //echo $query;

      try
      {
        $stmt = $this->conn->prepare($query);
        $stmt->execute();
      }
        catch (Exception $e)
      {
        die(error($e->getMessage(),-1));
      }
    }

    return  true;
  }

  // allow to select data from the table query is the same format as MongoDb querys
  function select($schema,$query)
  {
    unset($this->query);
    $this->query=$query;
    $table=$schema["table"];
    $columns=[];
    $joins=[];
    $where_rights=[];

    //check if right where set and setup default
    if(!isset($schema["sgroup"])) $schema["sgroup"]="W";
    if(!isset($schema["slevel"])) $schema["slevel"]="W";
    if(!isset($schema["sother"])) $schema["sother"]="W";

    if (!$this->admin) if(substr($table,0,1)=="_") die(error("error trying to read table ".$schema["table"]." : tables starting with _ are for internal use only!",-11));

    if(is_array($schema["default_query"])) $this->query=array_merge($this->query,$schema["default_query"]);

    // get the columns and joins
    $reply=$this->columns($schema,$table);
    $columns=array_merge($columns,$reply["columns"]);
    $joins=array_merge($joins,$reply["joins"]);

    $where_rights[]=" `$table`.`_user_id`='".$this->user["_id"]."' ";
    if($schema["sgroup"]!="N") $where_rights[]=" `$table`.`_group_id` in (".implode(",",$this->user["groups"]).") ";
    if($schema["slevel"]!="N") $where_rights[]=" `$table`.`_level`<'".$this->user["level"]."' ";
    if($schema["sother"]!="N") $where_rights[]=" true ";

    // add some restriction to the query depending of the right the user have on this table
    $this->query["_status"]=['$lt'=>9 ];

    // build the query
    $query="select ".implode(",",$columns)." from `".$schema["table"]."` ".implode(" ",$joins)." where (".implode(" or ",$where_rights).") and ".$this->where($this->query,$schema).$this->groupby($this->query,$schema).$this->orderby($this->query,$schema).";";

    //echo $query."

";

    try
    {
      $stmt = $this->conn->prepare($query);
      $stmt->execute();
      $stmt->setFetchMode(\PDO::FETCH_ASSOC);
    }
    catch(Exception $e)
    {
      die(error($e->getMessage(),-3));
    }

    $dataset=[];

    // build the dataset
    foreach($stmt->fetchAll() as $line) $dataset[]=$this->dataset($schema,$line,$table);

    // return the dataset
    return $dataset;

  }

  protected function columns($schema,$prefix)
  {
    $table=$schema["table"];
    $columns=[];
    $joins=[];
    $if_rights=[];
    $where_rights=[];
    $or=[];
    $aggregation="";

    // allways add the internal filed
    $columns[]="`$table`.`_id` as `".$prefix."._id`";

    //check if right where set and setup default
    if(!isset($schema["sgroup"])) $schema["sgroup"]="W";
    if(!isset($schema["slevel"])) $schema["slevel"]="W";
    if(!isset($schema["sother"])) $schema["sother"]="W";

    $if_rights[]=" `$table`.`_user_id`='".$this->user["_id"]."'";
    if($schema["sgroup"]=="W") $if_rights[]="`$table`.`_group_id` in (".implode(",",$this->user["groups"]).")"; //$or[]=["_group_id"=>['$eq'=>"".$this->user["group"]["_id"]]];
    if($schema["slevel"]=="W") $if_rights[]="`$table`.`_level`<'".$this->user["level"]."'"; //$or[]=["_level"=>['$lt'=>"".$this->user["level"]]];
    if($schema["sother"]=="W") $if_rights[]=" true ";

    $columns[]="if( ".implode(" or ",$if_rights)." ,`$table`.`_status`,'7') as `".$prefix."._status`";

    foreach($schema["structure"] as $field)
    {

      //add the aggregation function
      $aggregation="";
      if(isset($this->query["\$groupby"]["\$aggregate"][$field["name"]])) $aggregation=$this->query["\$groupby"]["\$aggregate"][$field["name"]];

      switch($field["type"])
      {
        case "relation":
          break;
        case "key";
          $subschema=$this->schemas->{$field["schema"]};
          $subtable=$subschema["table"];

          $this->create($subschema);
          $reply=$this->columns($subschema,$prefix.".".$field["schema"]);
          $columns=array_merge($columns,$reply["columns"]);
          $joins=array_merge($joins,$reply["joins"]);

          // Subtable right management
          //check if right where set and setup default
          if(!isset($subschema["sgroup"])) $subschema["sgroup"]="W";
          if(!isset($subschema["slevel"])) $subschema["slevel"]="W";
          if(!isset($subschema["sother"])) $subschema["sother"]="W";

          $where_rights[]=" `$subtable`.`_user_id`='".$this->user["_id"]."' ";
          if($subschema["sgroup"]!="N") $where_rights[]=" `$subtable`.`_group_id` in (".implode(",",$this->user["groups"]).")";
          if($subschema["slevel"]!="N") $where_rights[]=" `$subtable`.`_level`<'".$this->user["level"]."' ";
          if($subschema["sother"]!="N") $where_rights[]=" true ";

          array_unshift($joins," left join `$subtable` on `$subtable`.`_id`=`$table`.`".$field["name"]."` and (".implode(" or ",$where_rights).") and `$subtable`.`_status`<'9'  ");

          break;
        case "password";
          $columns[]="'!!NOCHANGE!!' as `".$prefix.".".$field["name"]."` ";
          break;
        case "translate";
          $columns[]="`$table`.`_".$this->lang."_".$field["name"]."` as `".$prefix.".".$field["name"]."` ";
          foreach($this->languages as $lkey=>$lvalue) $columns[]="`$table`.`_".$lkey."_".$field["name"]."` as `".$prefix."._".$lkey."_".$field["name"]."` ";
          break;
        case "section_start":
        case "section_end":
        case "html":
        case "button":
          break;
        default:
          //alter the column format according to the group by field format
          if(isset($this->query["\$groupby"]["\$fields"][$field["name"]]))
          {
            $columns[]=$this->sqlFunction($this->sqlFunction("`$table`.`".$field["name"]."`",$this->query["\$groupby"]["\$fields"][$field["name"]]),$aggregation)." as `".$prefix.".".$field["name"]."` ";
          }
          else
          {
            $columns[]=$this->sqlFunction("`$table`.`".$field["name"]."`",$aggregation)."  as `".$prefix.".".$field["name"]."` ";
          }
          break;
      }

    }

    return [ "columns"=>$columns, "joins"=>$joins];
  }

  // this function is used in the select function to create the where part of the query
  protected function where($query,$schema)
  {
    $table=$schema["table"];
    $where_columns=[];
    $or_columns=[];

    foreach($query as $field=>$value)
    {

      foreach($schema["structure"] as $s) if($s["type"]=="translate" and $s["name"]==$field) $field="_".$this->lang."_".$field;

      switch(true)
      {
        case is_array($value):
          switch (true)
          {
            case $field=='$or':
              $or_columns=[];
              foreach($value as $subvalue) $or_column[]=$this->where($subvalue,$schema);
              $where_columns[]=" (".implode(" or ",$or_column).") ";
            break;
            case $field=='$and':
              $or_columns=[];
              foreach($value as $subvalue) $or_column[]=$this->where($subvalue,$schema);
              $where_columns[]=" (".implode(" and ",$or_column).") ";
            break;
          }
        case is_object($value):
          switch (true)
          {
            case isset($value['$eq']):
              $where_columns[]="`$table`.`$field`='".$value['$eq']."'";
              break;
            case isset($value['$gt']):
              $where_columns[]="`$table`.`$field`>'".$value['$gt']."'";
              break;
            case isset($value['$lt']):
              $where_columns[]="`$table`.`$field`<'".$value['$lt']."'";
              break;
            case isset($value['$gte']):
              $where_columns[]="`$table`.`$field`>='".$value['$gte']."'";
              break;
            case isset($value['$lte']):
              $where_columns[]="`$table`.`$field`<='".$value['$lte']."'";
              break;
            case isset($value['$ne']):
              $where_columns[]="`$table`.`$field`!='".$value['$ne']."'";
              break;
            case isset($value['$in']):
              $where_columns[]="`$table`.`$field` in ('".implode("','",$value['$in'])."')";
              break;
            case isset($value['$nin']):
              $where_columns[]="`$table`.`$field` not in ('".implode("','",$value['$nin'])."')";
              break;
            case isset($value['$lk']): // not in mongodb spec
              $where_columns[]="`$table`.`$field` like '%".$value['$lk']."%'";
              break;
          }
          break;
        default:
          $where_columns[]="`$table`.`$field`='$value'";
          break;
      }
    }

    return  implode(" and ",$where_columns);

  }

  // this function is used in the select function to create the where part of the query
  protected function orderby($query,$schema)
  {
    $table=$schema["table"];
    $orderby_columns=[];

    if(!isset($query["\$orderby"])) return "";

    foreach($query["\$orderby"] as $field=>$value)
    {

      foreach($schema["structure"] as $s)
      {
        if($field==$s["name"])
        {
          if($s["type"]=="translate" and $s["name"]==$field) $field="_".$this->lang."_".$field;

          if($value==-1)
          {
            $orderby_columns[]=" `$table`.`$field` desc ";
          }
          elseif($value==1)
          {
            $orderby_columns[]=" `$table`.`$field` ";
          }
        }
      }

    }

    return  " order by ".implode(",",$orderby_columns);

  }

  // group by
  protected function groupby($query,$schema)
  {
    $table=$schema["table"];
    $groupby_columns=[];

    if(!isset($query["\$groupby"])) return "";

    foreach($query["\$groupby"]["\$fields"] as $field=>$value)
    {

      if(is_numeric($field)) $field=$value;

      foreach($schema["structure"] as $s)
      {
        if($field==$s["name"])
        {
          if($s["type"]=="translate" and $s["name"]==$field) $field="_".$this->lang."_".$field;
          $groupby_columns[]=$this->sqlFunction("`$table`.`$field`",$value);
        }
      }
    }

    return  " group by ".implode(",",$groupby_columns);
  }

  protected function sqlFunction($field,$fuction)
  {
        switch($function)
        {
          case "\$year":
              return " date_format($field,'%Y') ";
            break;
          case "\$month":
            return " date_format($field,'%m') ";
            break;
          case "\$monthname":
            return " date_format($field,'%M') ";
            break;
          case "\$week":
            return " date_format($field,'%u') ";
            break;
          case "\$weekday":
            return " date_format($field,'%w') ";
            break;
          case "\$sum":
            return " sum($field) ";
            break;
          case "\$count":
            return " count($field,) ";
            break;
          case "\$avg":
            return " avg($field) ";
            break;
          default:
            return " $field ";
            break;
          }

  }

  protected function dataset($schema,$line,$prefix,$relation=true)
  {
    $query="";
    $return="";
    $groupby=false;
    $grouptake=false;
    $aggregate="";

    if(isset($this->query["\$groupby"]["\$fields"])) $groupby=true;

    foreach($schema["structure"] as $field)
    {

      // when group by is used only return aggregated or group by columns in json
      $grouptake=false;
      if(isset($this->query["\$groupby"]["\$fields"][$field["name"]])) $grouptake=true;
      if(isset($this->query["\$groupby"]["\$fields"])) if(in_array($field["name"],$this->query["\$groupby"]["\$fields"])) $grouptake=true;
      if(isset($this->query["\$groupby"]["\$aggregate"][$field["name"]])) $grouptake=true;

      //if($grouptake==true) $return[$aggregate."_".$field["name"]]=$line[$aggregate.".".$prefix.".".$field["name"]];

      if($grouptake==true or $groupby==false)
      //if($groupby==false)
      {
        switch($field["type"])
        {
          case "relation":
            if($relation)
            {
              $this->create($this->schemas->{$field["schema"]});
              unset($query);
              $query[$field["key"]]=$line[$prefix."._id"];
              $return[$field["name"]]=$this->select($this->schemas->{$field["schema"]},$query);
            }
            break;
          case "multiplekey":
          case "json":
            $return[$field["name"]]=json_decode(stripslashes(html_entity_decode(preg_replace('/u([\da-fA-F]{4})/', '&#x\1;', $line[$prefix.".".$field["name"]]))));
            break;
          case "key":
            if($relation)
            {
              $return[$field["name"]]=$this->dataset($this->schemas->{$field["schema"]},$line,$prefix.".".$field["schema"],false);
            }
            break;
          case "translate":
            if($line[$prefix.".".$field["name"]])
            {
              $return[$field["name"]]=$line[$prefix.".".$field["name"]];
            }
            else
            {
              $translate="";
              foreach($this->languages as $lkey=>$lvalue) $translate.=$line[$prefix."._".$lkey."_".$field["name"]]." ";
              $return[$field["name"]]=trim($translate);
            }
            break;
          case "section_start":
          case "section_end":
          case "html":
          case "button":
            break;
          default:
            $return[$field["name"]]=$line[$prefix.".".$field["name"]];
            break;
        }
      }
    }

    // add the _id and status
    if(!$groupby) $return["_id"]=$line[$prefix."._id"];
    if(!$groupby) $return["_status"]=$line[$prefix."._status"];
    return $return;
  }

  // This function is used to upsert data into the database
  function upsert($schema,$dataset,$parent=[])
  {

    if (!isset($this->databases[$schema["database"]]["upsert"])) die(error("Please setup the 'write' flag of your database",-1));
    if ($this->databases[$schema["database"]]["upsert"]==false) die(error("write to this database is not allowed",-1));

    //for insert;
    $insert_columns=[];
    $insert_values=[];
    $rights=[];

    //for update
    $update_values=[];

    $table=$schema["table"];

    if (!$this->admin) if(substr($table,0,1)=="_") die(error("error trying to write in table ".$schema["table"]." : tables starting with _ are for internal use only!",-11));

    // other
    $structure=$schema["structure"];
    $match=false;
    $_id=0;
    $_status=0;

    //dataset to return
    $return=[];

    // name of the columns to insert
    foreach($structure as $field)
    {
      switch($field["type"])
      {
        case "relation":
          break;
        case "translate":
          $insert_columns[]="`_".$this->lang."_".$field["name"]."`";
          break;
        case "section_start":
        case "section_end":
        case "html":
        case "button":
          break;
        default:
          $insert_columns[]="`".$field["name"]."`";
          break;
      }
    }

    // add the standards columns
    $insert_columns[]="`_ip`,`_time`,`_color`";
    $insert_columns[]="`_user_id`,`_group_id`,`_level`";

    // parse each line in the dataset
    foreach($dataset as $line)
    {
      //reset the query arrays
      $insert_values=[];
      $update_values=[];
      $_id=0;

      // if dataset have a parent relation add the id of the parent in the corresponding field
      //if(count($parent)>0) $line->{$parent->{"field"}}=$parent->{"_id"};

      // parse the structure
      foreach($structure as $field)
      {
        $match=false;

        if ( $field["type"]=="section_start" or $field["type"]=="section_end" or $field["type"]=="html" or $field["type"]=="button") $match=true;

        // Parse each column in the line
        foreach($line as $column=>$value)
        {
          // look for id to choose between insert or update
          if ($column=="_id") $_id=$value;
          if ($column=="_status") $_status=$value;

          // Column in the object must be referenced in the structure
          if($column==$field["name"])
          {
            $match=true;

            //echo $column."
";

            // Choose what to do depending on the column type
            switch($field["type"])
            {
              case "relation":
                if(!is_array($value))
                {
                  // only make a check. Recording this into the database will be done later
                  die(error($field["name"]." should be a array",-4));
                }
                break;
              case "boolean":
                if($value==1)
                {
                  $insert_values[]="1";
                  $update_values[]="`".$column."`=1";
                }
                else
                {
                  $insert_values[]="0";
                  $update_values[]="`".$column."`=0";
                }
                break;
              case "multiplekey":
              case "json":
                  // we store all the mess as a sting
                  $insert_values[]="'".addslashes(json_encode($value))."'";
                  $update_values[]="`".$column."`='".addslashes(json_encode($value))."'";
                break;
              case "select":
              case "list":
                if(!isset($field["enum"])) die(error($field["name"]." need and enum array!",-5));
                if(!is_array($field["enum"])) die(error($field["name"]." enum element is not an array!",-5));
                if(in_array($value,$field["enum"]))
                {
                  $insert_values[]="'".addslashes($value)."'";
                  $update_values[]="`".$column."`='".addslashes($value)."'";
                }
                else
                {
                  die(error("$value in ".$field["name"]." not in the list",-5));
                }
                break;
              case "key":
                  $insert_values[]="'".addslashes($value->_id)."'";
                  $update_values[]="`".$column."`='".addslashes($value->_id)."'";
                break;
              case "translate":
                $column="_".$this->lang."_".$column;
              default:
                if(preg_match("#".$field["regex"]."#",$value) or $status=9) //allow not matching the regex if we delete the line
                {
                  $insert_values[]="'".addslashes($value)."'";
                  if ($value!="!!NOCHANGE!!" or is_numeric($value) ) $update_values[]="`".$column."`='".addslashes($value)."'";
                }
                else
                {
                  die(error("$value in ".$field["name"]." does not match ".$field["regex"],-5));
                }
                break;
            }
          }
        }

        // if we dont find the value then add empty or trigger an error if not optional

        if ($match==false)
        {
          if($field["optional"]==false)
          {
            die(error("No value for ".$field["name"]." was found",-6));
          }
          else
          {
            $insert_values[]="null";
          }
        }

      }

      //check if right where set and setup default
      if(!isset($schema["sgroup"])) $schema["sgroup"]="W";
      if(!isset($schema["slevel"])) $schema["slevel"]="W";
      if(!isset($schema["sother"])) $schema["sother"]="W";

      $rights[]=" `$table`.`_user_id`='".$this->user["_id"]."'";
      if($schema["sgroup"]=="W") $rights[]="`$table`.`_group_id` in (".implode(",",$this->user["groups"]).")";
      if($schema["slevel"]=="W") $rights[]="`$table`.`_level`<'".$this->user["level"]."'";
      if($schema["sother"]=="W") $rights[]=" true ";

      // add default values
      //$insert_columns[]="`_ip`,`_time`,`_color`";
      $insert_values[]="'".$_SERVER["REMOTE_ADDR"]."','".date("Y-m-d H:i:s")."',''";
      $update_values[]="`_ip`='".$_SERVER["REMOTE_ADDR"]."',`_time`='".date("Y-m-d H:i:s")."',`_color`=''";

      // choose between update and insert
      switch(true)
      {
        case $_id==0 and $_status<9: // can not create deleted lines

          //$insert_columns[]="`_user_id`,`_group_id`,`_level`";
          $insert_values[]="'".$this->user["_id"]."','".$this->user["group"]["_id"]."','".$this->user["level"]."'";

          $query="insert into `$table` (".implode(",",$insert_columns).") values (".implode(",",$insert_values).");";
          break;
        case $_id!=0:
          $query="update `$table` set ".implode(",",$update_values).",`_status`='$_status' where `_id`='$_id' and (".implode(" or ",$rights).") and `_status`<7;"; //can not update locked or deleted lines
          break;
      }

      //echo $query;

      try
      {
        $stmt = $this->conn->prepare($query);
        $stmt->execute();
      }
      catch(Exception $e)
      {
          die(error($e->getMessage()." : ".$query,-3));
      }

      // add id to the dataset line
      if($_id==0) $line->_id=$this->conn->lastInsertId();

      //do the same recursively to each relation data
      foreach($line as $column=>$value)
      {
        foreach($structure as $field)
        {
          if($field["name"]==$column)
          {
            if($field["type"]=="relation")
            {
              //$description=$structure[$column];

              /*
              $child=new bi();
              $child->connect();
              $child->create($schemas->{$description["table"]});
              $child->upsert($schemas->{$description["table"]},$value);
              */

              $relation=(object) array();
              $relation->field=$field["key"];
              $relation->_id=$line->_id;

              //print_r($value);

              // $child=new bi();
              // $child->connect();
              $this->create($this->schemas->{$field["schema"]});
              $child=$this->upsert($this->schemas->{$field["schema"]},$value,$relation);

              $line->{$column}=$child;

            }
          }
        }
      }

      // add the completed line to the return
      $return[]=$line;

    }

    return $return;
  }

}

function implode_match($glue,$array,$regex)
{
  $reply="";

  foreach($array as $element) if(preg_match("#".$regex."#",$element)) $reply.=$element.$glue;
  rtrim($reply,$glue);

  return $reply;
}

/**********************************************************************************

Mid level functions provide building blocs for html pages

***********************************************************************************/

// Html header and setup schemas for javascrip
// include javascript
function header($background="#FFFFFF")
{
$lbl=$GLOBALS['lbl'];
$user=$GLOBALS['user'];
$schemas=$GLOBALS['schemas'];
$project=$GLOBALS['project'];
$lang=$GLOBALS['lang'];

/*

  <meta http-equiv='content-language' content='$language' />
  <meta name='language' content='$language' />
*/

if($project=="") error("Project name is not set please do it in nano_config.php",-8);

/*

<link rel='stylesheet' href='https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css' integrity='sha384-XXXXXXXX' crossorigin='anonymous'>

*/

//bootstrap 3
/*

<link rel='stylesheet' href='./bootstrap/css/bootstrap.min.css'>
<link rel='stylesheet' type='text/css' media='print' href='./bootstrap/css/bootstrap.min.css'>

*/

$html="<!DOCTYPE html>

<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' charset='utf-8' >

<meta name='apple-mobile-web-app-capable' content='yes'>
<meta name='apple-mobile-web-app-status-bar-style' content='black'>
<link rel='apple-touch-icon' href='flat_berdoz.png'>

<link rel='stylesheet' href='./bootstrap/css/bootstrap.min.css'>
<link rel='stylesheet' type='text/css' media='print' href='./bootstrap/css/bootstrap.min.css'>

<link href='./font-awesome-4.6.1/css/font-awesome.min.css' rel='stylesheet'>

<link rel='stylesheet' href='nanofw/nano.css'>
<link rel='stylesheet' href='$project.css'>

";

$html.="

";

return $html;
}

// Html footer and setup the standard modal dialog for custom fields
function footer()
{
  // add the nano standard popups
  $html=modal("nano_key_choose_popup",lbl("Choose"),"

","");

  $html.="";
  return $html;
}

// Html Navebar provide a home button a ajax working indication and language switch
function navbar($links)
{
$languages=$GLOBALS['languages'];
$user=$GLOBALS['user'];
$lang=$GLOBALS['lang'];

$lang_html="";
foreach($languages as $key=>$value)
{
  if($lang!=$key) { $lang_html.="
".$value["name"]."
"; }
  else {  $lang_html.="
".$value["name"]."
"; }
}

$html="

    <button type='button' class='navbar-toggle collapsed' data-toggle='collapse' data-target='#navbar' aria-expanded='false' aria-controls='navbar'>
      Toggle navigation

    </button>

Home

      $lang_html

Logout
";
      if ($user["is_admin"]) $html.= "

Admin
";
   $html.= "

";

return $html;
}

// Modal dialog from boostrap
function modal($modal_name,$modal_label,$modal_content,$modal_js)
{
$lbl=$GLOBALS['lbl'];
$html="

        <button type='button' class='close' data-dismiss='modal' aria-label='Close'>
          ×
        </button>

$modal_label

        $modal_content

";
        if ($modal_js!="")
        {
          $html.="
";
          $html.="<button type='button' class='btn btn-default' data-dismiss='modal'>".lbl("close")."</button>";
          $html.="<button type='button' class='btn btn-primary' onclick='$modal_js'>".lbl("save")."</button>
";
        }
      $html.="

";

return $html;
}

// Build a form according to the schema definition
function form($schema,$action,$default_values,$form)
{
  $schemas=$GLOBALS["schemas"];
  // col is the bootstrap column
  // action is an array of object for button [{"label":"edit","function":"client_edit"}]
  // structure describe the form components

  $disabled="disabled";
  $html="<form nano_mode='lock'  nano_schema='".$schema["name"]."' name='$form' onsubmit='return nano.submit();' >
";
  $nbr=0;
  $tabs="";

  $section=0;

  $structure=$schema["structure"];

  if(!is_array($structure)) die(error("Your structure is not an array",-1));

  foreach($structure as $s)
  {
    $nbr++;

    $collapse="in";
    $collapsed="";
    if(isset($s["collapse"])) if($s["collapse"]==true)
    {
      $collapse="";
      $collapsed="collapsed";
    }

    $display=["list","form"];
    if(isset($s["display"])) $display=$s["display"];

    $id="";
    if(isset($s["id"])) $id="id='".$s["id"]."'";

    $type="text";
    if(isset($s["type"])) $type=$s["type"];

    $label="label_$nbr";
    if(isset($s["label"])) $label=$s["label"];

    $name="name_$nbr";
    if(isset($s["name"])) $name=$s["name"];

    $col="col-xs-3";
    if(isset($s["col"])) $col=$s["col"];

    $placeholder="";
    if(isset($s["placeholder"])) $placeholder=" placeholder='".$s["placeholder"]."'";

    $pattern="";
    if(isset($s["regex"])) $pattern=" pattern='".$s["regex"]."'";

    $onchange="";
    if(isset($s["onchange"])) $onchange=" onchange='".$s["onchange"]."(this)'";

    $required="";
    if(isset($s["required"])) if($s["required"]==true) $required=" required";

    $onclick="";
    if(isset($s["onclick"])) $onclick="onclick='".$s["onclick"]."(this)'";

    $btn="btn-primary";
    if(isset($s["btn"])) $btn=$s["btn"];

    //$nano_default="";
    //if(isset($s["value"])) $nano_default="nano_default='toto'"; //".$s["value"]."

    if(isset($s["clearall"])) if($s["clearall"]==true) $html.="

";

    $default_value="";

    if(isset($s["value"])) $default_value=$s["value"]; //".$s["value"]."
    $hidden="";

    foreach($default_values as $dkey=>$dvalue)
    {
      if($dkey==$s["name"])
      {
        $default_value=$dvalue;
        $hidden="hidden";
      }
    }

    if(in_array("form",$display))
    {
      switch($type)
      {
        case "translate":
        case "string":
        case "double":
        case "float":
          $html.="
";
          $html.="$label";
          if($onclick!="") $html.="
"; // form-control-static //form-control
          $html.="<input nano_type='$type' class='form-control' type='text' nano_default='$default_value' name='$name'  $placeholder $onchange $pattern $required $disabled $hidden>";
          if($onclick!="") $html.="<button type='button' class='btn btn-primary' onclick='".$s["onclick"]."($form.$name)'>".lbl($s["onclick"])."</button>
";
          $html.="
";
          break;
        case "text":
          $html.="
";
          $html.="$label";
          if($onclick!="") $html.="
"; // form-control-static //form-control
          $html.="<textarea nano_type='$type' class='form-control' nano_default='$default_value' name='$name'  $placeholder $onchange $pattern $required $disabled $hidden></textarea>";
          //if($onclick!="") $html.="<button type='button' class='btn btn-primary' onclick='".$s["onclick"]."($form.$name)'>".lbl($s["onclick"])."</button>
";
          $html.="
";
          break;
        case "html":
          $html.="
";
          $html.=$s["html"];
          $html.="
";
          break;
        case "button":
          $html.="
";
          $html.=" ";
          $html.="<input type='button' nano_type='button' class='form-control btn $btn' name='$name' value='$label' $onclick $disabled $hidden>";
          $html.="
";
          break;
        case "password":
          $html.="
";
          $html.="$label";
          $html.="<input nano_type='$type' class='form-control' type='password' nano_default='$default_value' name='$name' $onchange $pattern $required $disabled $hidden>";
          $html.="
";
          break;
        case "number":
        case "integer":
          $html.="
";
          $html.="$label";
          if($onclick!="") $html.="
";
          $html.="<input nano_type='$type' class='form-control' type='number' nano_default='$default_value' name='$name'  $placeholder $onchange $pattern $required $disabled $hidden>";
          if($onclick!="") $html.="<button type='button' class='btn btn-primary' onclick='".$s["onclick"]."($form.$name)'>".lbl($s["onclick"])."</button>
";
          $html.="
";
          break;
        case "date":
          $html.="
";
          $html.="$label";
          $html.="<input nano_type='$type' class='form-control' type='date' nano_default='$default_value' name='$name' placeholder='yyyy-mm-dd'  $onchange $pattern $required $disabled $hidden>";
          $html.="
";
          break;
        case "hidden":
        case "relation":
          $html.="<input $id nano_type='$type' type='text' nano_default='$default_value' name='$name' hidden>";
          break;
        case "json":
          $html.="<input $id nano_type='$type' type='text' nano_default='$default_value' name='$name' nano_value='' hidden>";
          $html.="

";
          break;
        case "select":
        case "list":
          $html.="
";
          $html.="$label";
          $html.="<select nano_type='$type' class='form-control' nano_default='$default_value' name='$name' $onchange $disabled $hidden>";
          $html.="<option value=''>".lbl("choose a value")."</option>";
          if(isset($s["enum"])) foreach($s["enum"] as $e) $html.="<option value='$e'>".lbl($name."_".$e)."</option>";
          $html.="</select>";
          $html.="
";
          break;
        case "key":
          $html.="
";
          $html.="$label";
          $html.=" ".lbl("type ? for the list")."";

          $html.="<input nano_type='$type' class='form-control' type='text' nano_default='$default_value' $placeholder  name='$name' nano_schema='".$s["schema"]."' onchange='nano.key_find(this,\"onfind\");'  $pattern $required $disabled $hidden>";
          $html.="
";
          break;
        case "boolean":
        case "checkbox":
          $html.="
";
          $html.=" ";
          $html.="
";
          $html.="<input nano_type='$type' type='checkbox' nano_default='$default_value' name='$name' $onchange   $required $disabled $hidden> $label";
          $html.="
";
          $html.="
";
          break;
        case "section_start":
          $html.="
";
          $html.="
"; //class='btn btn-default btn-sm nano-btn $collapsed'
          $html.="";
          $html.=" $label";
          $html.="
";
          //$html.="
";
          $html.="

";
          $section++;
          break;
        case "section_end":
          $html.="
";
          $html.="
";
          $section--;
          break;
        case "multiplekey":
          $html.="
";
          $html.="$label
";
          $html.="".lbl("Hold ctrl for mulptiple selection.")."";

          $keylist=new data(false);
          $keylist->connect($schemas->{$s["schema"]});
          $keylist->create($schemas->{$s["schema"]});

          $html.="<select nano_type='$type' size='10' class='form-control' nano_default='$default_value' name='$name' $onchange  $disabled $hidden multiple>";
          foreach($keylist->select($schemas->{$s["schema"]},[]) as $list) $html.="<option value='".$list["_id"]."'>".implode_match(' ',$list,"^[^0-9].*$")."</option>";
          $html.="</select>";
          $html.="
";
          break;
      }
    }
  }

  for($i=0;$i<$section;$i++) $html.="

"; //if user forget to close section we do it for him

  $html.="<input nano_type='hidden' type='text' name='_id' value=0 hidden>";
  $html.="<input nano_type='hidden' type='text' name='_status' value=0 hidden>";

  foreach($action as $a)
  {
    $col="col-xs-6";
    $onclick='nano_submit';
    if(isset($a["col"])) $col=$a["col"];
    if(isset($a["onclick"])) $onclick=$a["onclick"];

    if(isset($a["clearall"])) if($a["clearall"]==true) $html.="

";

    $btn="btn-primary";
    if(isset($a["btn"])) $btn=$a["btn"];

    switch($a["type"])
    {
        case "button":
          $html.="
";
          $html.=" ";
          $html.="<input type='button' nano_type='button' class='form-control btn $btn' name='".$a["name"]."' onclick='$onclick(this.form)' value='".$a["label"]."'>";
          $html.="
";
          break;
    }

  }

  $html.="
</form>";

  return $html;
}

/**********************************************************************************

High level fuction use building blocs to provide out of the box CRUD

***********************************************************************************/

// full CRUD on a schema
function quickList($schema,$param=[])
{
  $html="";
  $schemas=$GLOBALS['schemas'];

  $default_query="{}";
  if (isset($param["default_query"])) $default_query=$param["default_query"];

  $show_buttons=true;
  if (isset($param["show_buttons"])) $show_buttons=$param["show_buttons"];

  $autoload;
  if (isset($param["autoload"])) $autoload=$param["autoload"];

  $default_values=[];
  if (isset($param["default_values"]))
  {
    unset($default_values);
    $default_values=$param["default_values"];
    //print_r($default_values);
  }

  // create the form for client edit
  /*$action=
  [
  ["name"=>"prev", "label"=>"<", "col"=>"col-xs-3", "type"=>"button", "clearall"=>true, "onclick"=>"nano.form_prev(this.form);" ],
  ["name"=>"next", "label"=>">",  "col"=>"col-xs-3","type"=>"button", "onclick"=>"nano.form_next(this.form);" ],
  ["name"=>"edit", "label"=>"Edit", "col"=>"col-xs-3","type"=>"button", "onclick"=>$schema."_edit_mode(this.form);" ],
  ["name"=>"search", "label"=>"Search", "col"=>"col-xs-3","type"=>"button", "onclick"=>$schema."_find_mode(this.form);" ],
  ["name"=>"delete", "label"=>"Delete", "col"=>"col-xs-3","type"=>"button", "onclick"=>"form_delete(this.form);" ],
  ["name"=>"save", "label"=>"Save", "col"=>"col-xs-3","type"=>"button", "onclick"=>$schema."_save(this.form);" ],
  ];*/

  $action=
  [
    ["name"=>"prev", "label"=>"<", "col"=>"col-xs-3", "type"=>"button", "clearall"=>true, "onclick"=>"nano.form_prev(this.form);" ],
    ["name"=>"next", "label"=>">",  "col"=>"col-xs-3","type"=>"button", "onclick"=>"nano.form_next(this.form);" ],
    ["name"=>"ok", "label"=>"OK", "col"=>"col-xs-3 col-xs-offset-3","type"=>"button", "onclick"=>$schema."_ok();" ],
  ];

  $form=form($schemas->{$schema},$action,$default_values,$schema."_edit_form");
  $html.=modal($schema."_edit_popup",lbl($schema."_edit_popup"),$form,""); //$schema."_save(document.".$schema."_edit_form);"

  // prepare the find popup
  $html.=modal($schema."_find_popup",lbl($schema."_find_popup"),"

",$schema."_find_validate(document.".$schema."_find_form);");

  if($show_buttons)
  {
    $html.="

".lbl($schema)."

        <button class='form-control btn btn-primary' type='button' onclick='".$schema."_new_mode({});'>".lbl("new")."</button>

        <button class='form-control btn btn-default' type='button' onclick='".$schema."_sub_selection()'>".lbl("sub selection")."</button>

        <button class='form-control btn btn-default' type='button' onclick='".$schema."_mass_delete()'>".lbl("delete")."</button>

        <button class='form-control btn btn-default' type='button' onclick='".$schema."_find_mode()'>".lbl("find")."</button>

    ";
  }

  $html.="

";

  $html.="";

  return $html;

}

function quickListFunctions($schema)
{

  // Std nano open in edit mode
  $html="
  function ".$schema."_edit_mode(ix)
  {
      var form=document.".$schema."_edit_form;
      if(ix) schemas.$schema.ix=ix;
      nano.form_prepare(form,'edit',{});
      nano.form_load(schemas.$schema.data[schemas.$schema.ix],form);
      $('#".$schema."_edit_popup').modal('show');
  }
  ";

  // std nano open in find mode
  $html.="
  function ".$schema."_find_mode()
  {
      var form=document.".$schema."_edit_form;
      nano.form_reset(form);
      nano.form_prepare(form,'find',{});
      $('#".$schema."_edit_popup').modal('show');
  }
  ";

  // Std nano open in new mode
  $html.="
  function ".$schema."_new_mode(default_values)
  {
    var form=document.".$schema."_edit_form;
    nano.form_reset(form);
    nano.form_prepare(form,'new',default_values);
    if(schemas.$schema.onload) window[schemas.$schema.onload]();
    $('#".$schema."_edit_popup').modal('show');
  }
  ";

  // Std nano ok
  $html.="
  function ".$schema."_ok()
  {
    var form=document.".$schema."_edit_form;
    var mode=form.attributes.nano_mode.value;
    var valide=false;

    if(form._status.value>=7) mode='lock';

    switch(mode)
    {
      case 'new':
      case 'edit':
        valide=".$schema."_save(form);
        break;
      case 'find':
        valide=".$schema."_find(form);
        break;
      case 'lock':
        break;
    }

    if(valide)
    {
      $('#".$schema."_edit_popup').modal('hide');
      nano.form_prepare(document.".$schema."_edit_form,'lock',{});
    }
  }
  ";

  // std nano sub select function
  $html.="
  function ".$schema."_sub_selection()
  {
    var i;
    var tmp_array=[];

    for(i in document.getElementsByName('".$schema."_checkbox'))
    {
      if(document.getElementsByName('".$schema."_checkbox')[i].checked==true) tmp_array.push(schemas.$schema.data[document.getElementsByName('".$schema."_checkbox')[i].value]);
    }

    schemas.$schema.data=tmp_array;

    var param=
    {
      'onclick':'".$schema."_edit_mode',
      'multiselect':true
    }

    $('#".$schema."_list_div').html(nano.table(schemas.$schema,schemas.$schema.data,param));
  }
  ";

  // std nano delete function
  $html.="
  function ".$schema."_mass_delete()
  {
    var i;
    var tmp_array=[];
    var checkboxes=document.getElementsByName('".$schema."_checkbox');

    if(!schemas.$schema.data) return false;

    if(schemas.$schema.data.length==0) return false;

    if(window.confirm(nano.lbl('Do you really want to delete')))
    {
      for(i in checkboxes)
      {
        if(checkboxes[i].checked==true && checkboxes[i].type=='checkbox' )
        {
          schemas.$schema.data[checkboxes[i].value]._status=9;
          tmp_array.push(schemas.$schema.data[checkboxes[i].value]);
        }
      }

      nano.save('$schema',tmp_array,function(){});
      ".$schema."_display(schemas.$schema.data);
    }
  }
  ";

   // std nano delete function
  $html.="
  function ".$schema."_delete()
  {
    var tmp_array=[];

    if(schemas.$schema.data.length==0) return false;

    if(window.confirm(nano.lbl('Do you really want to delete')))
    {
      schemas.$schema.data[schemas.$schema.ix]._status=9;
      tmp_array.push(schemas.$schema.data[schemas.$schema.ix]);
      schemas.$schema.data.splice(schemas.$schema.ix,1)

      nano.save('$schema',tmp_array,function(){});

      ".$schema."_display(schemas.$schema.data);
    }
  }
  ";

  //std nano find
  $html.="
  function ".$schema."_find(form)
  {
    var query=nano.form_query(form);
    nano.load('$schema',query,".$schema."_display)
    return true;
  }
  ";

   // std nano display
  $html.="
  function ".$schema."_display(reply)
  {
    schemas.$schema.data=reply;

    var param=
    {
      'onclick':'".$schema."_edit_mode',
      'multiselect':true
    }

    $('#".$schema."_list_div').html(nano.table(schemas.$schema,schemas.$schema.data,param));
    //$('#".$schema."_list_div').html(nano.div_list(schemas.$schema,schemas.$schema.data,param));

    if(typeof ".$schema."_callback == 'function') ".$schema."_callback();

  }
  ";

  // Std nano save function
  $html.="
  function ".$schema."_save(form)
  {
    var json;
    var c;
    var data='?';
    var _id=0;
    if(form._id) _id=form._id.value;

    var callback=function(reply)
    {

      if(_id==0)
      {
        schemas.$schema.data.push(reply[0]);
        schemas.$schema.ix=schemas.$schema.data.length-1;
      }
      else
      {
        schemas.$schema.data[schemas.$schema.ix]=reply[0];
      }

      ".$schema."_display(schemas.$schema.data);

    }

    if(nano.form_validate(form))
    {
      json=nano.form_save(form);
      var dataset=[];
      dataset.push(json);
      nano.save('$schema',dataset,callback);
    }
    else
    {
      return false;
    }

    return true;
  }

  ";

  return $html;

}

function quickEdit($schema,$param=[])
{
  $html="";
  $schemas=$GLOBALS['schemas'];
  $tabs;
  $tabs_panes;

  $action=[];

  $mode="div";
  if(isset($param["mode"])) $mode=$param["mode"];

  $onload="load";
  if(isset($param["onload"])) $onload=$param["onload"];

  $default_query="{}";
  if (isset($param["default_query"])) $default_query=$param["default_query"];

  $show_buttons=true;
  if (isset($param["show_buttons"])) $show_buttons=$param["show_buttons"];

  $autoload;
  if (isset($param["autoload"])) $autoload=$param["autoload"];

  // add form actions button
  if($mode=="form")
  {
    $action[]=["name"=>"cancel", "btn"=>"btn-default", "label"=>"Cancel", "col"=>"col-xs-2 col-xs-offset-8","type"=>"button", "onclick"=>$schema."_cancel();" ];
    $action[]=["name"=>"ok", "btn"=>"btn-primary","label"=>"OK", "col"=>"col-xs-2","type"=>"button", "onclick"=>$schema."_ok();" ];
  }
  elseif($mode=="div")
  {
    $action[]=["name"=>"ok", "btn"=>"btn-primary","label"=>"OK", "col"=>"col-xs-2 col-xs-offset-10","type"=>"button", "onclick"=>$schema."_ok();" ];
  }

  $html.="
".lbl($schema)."
";

  if($mode=="div")
  {
    $html.="

";
  }
  elseif($mode=="form")
  {
    $html.=form($schemas->{$schema},$action,[],$schema."_edit_form");
  }

  if($show_buttons)
  {
    $html.="

        <button class='form-control btn btn-primary' type='button' onclick='nano.form_prev(document.".$schema."_edit_form); ".$schema."_callback();'><</button>

        <button class='form-control btn btn-primary' type='button' onclick='nano.form_next(document.".$schema."_edit_form); ".$schema."_callback();'>></button>

        <button class='form-control btn btn-primary' type='button' onclick='".$schema."_new_mode({}); ".$schema."_hide_buttons(); '>".lbl("new")."</button>

        <button class='form-control btn btn-default' type='button' onclick='".$schema."_edit_mode(null); ".$schema."_hide_buttons();'>".lbl("edit")."</button>

        <button class='form-control btn btn-default' type='button' onclick='".$schema."_delete()'>".lbl("delete")."</button>

        <button class='form-control btn btn-default' type='button' onclick='".$schema."_find_mode(); ".$schema."_hide_buttons();'>".lbl("find")."</button>

    ";
  }

  if($mode=="div")
  {
    $form=form($schemas->{$schema},$action,[],$schema."_edit_form");
    $html.=modal($schema."_edit_popup",lbl($schema."_edit_popup"),$form,""); //$schema."_save(document.".$schema."_edit_form);"
  }

  $tabs="
";
  $tabs_panes="
";
  $active="active";
  foreach($schemas->{$schema}["structure"] as $s)
  {
    if($s["type"]=="relation")
    {
      $action=
      [
        ["name"=>"prev", "label"=>"<",  "col"=>"col-xs-3", "type"=>"button", "clearall"=>true, "onclick"=>"nano.form_prev(this.form); ".$s["schema"]."_relation_reload(); " ],
        ["name"=>"next", "label"=>">",  "col"=>"col-xs-3","type"=>"button", "onclick"=>"nano.form_next(this.form); ".$s["schema"]."_relation_reload();" ],
        ["name"=>"ok", "label"=>"OK", "col"=>"col-xs-3 ","type"=>"button", "onclick"=>$s["schema"]."_ok();" ],
      ];

      $tabs.="
".$s["label"]."
";
      $tabs_panes.="
";
      $active="";
      //$tabs_panes.="
";
      $tabs_panes.="

          <button class='form-control btn btn-primary' type='button' onclick='".$s["schema"]."_new_mode({});'>".lbl("new")."</button>

          <button class='form-control btn btn-default' type='button' onclick='".$s["schema"]."_sub_selection()'>".lbl("sub selection")."</button>

          <button class='form-control btn btn-default' type='button' onclick='".$s["schema"]."_mass_delete()'>".lbl("delete")."</button>

      ";
      $tabs_panes.="

";
      $tabs_panes.="
";

      $html.="";
      $form=form($schemas->{$s["schema"]},$action,[$s["key"]=>0],$s["schema"]."_edit_form");
      $html.=modal($s["schema"]."_edit_popup",lbl($s["schema"]."_edit_popup"),$form,"");
    }
  }
  $tabs.="
";
  $tabs_panes.="
";

  $html.=$tabs;
  $html.=$tabs_panes;

  //$html.="

";

  $html.="";

  return $html;

}

// full CRUD on a schema
function bulkInsert($schema,$param=[])
{
  $html="";
  $schemas=$GLOBALS['schemas'];

  $default_query="{}";
  if (isset($param["default_query"])) $default_query=$param["default_query"];

  $show_buttons=true;
  if (isset($param["show_buttons"])) $show_buttons=$param["show_buttons"];

  $html.="
".lbl($schema)."
";

  $action=
  [
    ["name"=>"add", "label"=>"add", "clearall"=>false,"col"=>"col-xs-6 col-xs-6","type"=>"button", "onclick"=>$schema."_add_line();" ],
    ["name"=>"del", "label"=>"del", "btn"=>"btn-default", "clearall"=>false,"col"=>"col-xs-6 col-xs-6","type"=>"button", "onclick"=>$schema."_del_line();" ],
  ];

  $html.=form($schemas->{$schema},$action,[],$schema."_edit_form");

  $html.="

";

  $html.="

      <button class='form-control btn btn-primary' type='button' onclick='".$schema."_bulk_save()'>".lbl("Save")."</button>

      <button class='form-control btn btn-default' type='button' onclick=''>".lbl("Clear")."</button>

  ";

  $html.="";

  return $html;

}

?>


text:
var p1 = new Promise(function(resolve, reject) {
  resolve('Success');
});

p1.then(function(value) {
  console.log(value); // "Success!"
  return Promise.reject('oh, no!');
}).catch(function(e) {
  console.log(e); // "oh, no!"
  return Promise.reject('oh, no! 2');
}).then(function(){
  console.log('after a catch the chain is restored');
}, function () {
  console.log('Not fired due to the catch');
});

redpajama_set_name: RedPajamaGithub


text:
/*!
 * \defgroup    securitylayerprotocol Security layer protocol implementation
 * \{
 *
 * \file
 * \brief       Header definitions for the security layer protocol example with type 0
 *
 *              The protocol defines the following message format
 *
 * \verbatim
        0               1               2               3               Byte
        0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 bit
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |Typ|PCT| TL  |C|                    Permission Code (Type 0)
        |       0       |     PCT       | Security Index|   Perm Index
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           Permission   | Key Set index |                               /
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               \
        \                                                               /
        /                           Payload*                            \
        \                                                               /
        /                                                               \
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        |                                                               |
        |                         MAC (20 Bytes)                        |
        |                                                               |
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 \endverbatim
 * - Type: protocol type
 * - Permission code type (PCT): type of permission code
 * - Token Length (TL): Length of token
 * - CipherFlag (C): Flag indicates, if the key usage field is attached. If not, default key usage shall be used.
 * - Permission Code  (PC):
 * - Token: TL Bytes
 * - **Key Set** index: the index of the used key set for this message. This field size is 1 byte.
 * - Payload: Plaintext, not encrypted
 * - MAC: use HMAC, which should be 20 Bytes. If SHA-256 is used, just cut the hash value and take the first 20 bytes.
 *     - MAC is over transport layer payload, that means Transport layer security header + payload
 * - Totally, 5 Bytes header overhead + MAC (20 Bytes)
 *
 * For this example implementation, the first byte of header is always 0 to achieve a minimal setting.
 *
 * \author      Wenwen Chen
 */
#ifndef __SECURITY_LAYER_EXAMPLE_H__
#define __SECURITY_LAYER_EXAMPLE_H__

#include "treestate.h"
#ifdef  __cplusplus
extern "C" {
#endif

#define SECURITY_LAYER_TYPE_EXAMPLE 0
#define SECURITY_LAYER_HEADER_LEN_EXAMPLE 6
#define SECURITY_LAYER_PERMCODE_LEN_EXAMPLE 4
#define MAC_LEN_EXAMPLE 20
const uint8_t PERMCODE_TYPE;
#pragma pack(1)
  typedef struct permCodeExample{
    uint8_t permcode_type;
    uint8_t secret_index;
    uint8_t perm_index;
    uint8_t perm;/*!<Right informations*/
  }permCodeExample;

  typedef struct securityHeaderExample{
    uint8_t indicator;
    permCodeExample perm_code;
    uint8_t key_index;
  }securityHeaderExample;
#pragma pack()

  unsigned int parseHeaderExample(unsigned int security_descriptor, unsigned char * msg, unsigned int *p_msg_size);
  unsigned int generateHeaderExample(unsigned int security_descriptor, unsigned char* msg, unsigned int msg_size);
  unsigned int generatePermCodeExample(unsigned int security_descriptor, unsigned char* msg, unsigned int msg_size);

  void * getHeaderExample(unsigned int security_descriptor, unsigned int *size);
  void * getPermCodeExample(unsigned int security_descriptor, unsigned int * size);
  TREE_STATE_TYPE getSecretIndexExample(unsigned int security_descriptor);
  TREE_STATE_TYPE getPermIndexExample(unsigned int security_descriptor);
  RIGHT_TYPE getPermExample(unsigned int security_descriptor);
  TREE_STATE_TYPE getKeyIndexExample(unsigned int security_descriptor);

  unsigned int setSecretIndexExample(unsigned int security_descriptor, TREE_STATE_TYPE index);
  unsigned int setPermIndexExample(unsigned int security_descriptor, TREE_STATE_TYPE index);
  unsigned int setPermExample(unsigned int security_descriptor, RIGHT_TYPE index);
  unsigned int setKeyIndexExample(unsigned int security_descriptor, TREE_STATE_TYPE index);
  unsigned int setPermCodeExample(unsigned int security_descriptor, unsigned char* code, unsigned int code_size);

#ifdef  __cplusplus
}
#endif /* __cplusplus */

#endif /* __SECURITY_LAYER_EXAMPLE_H__ */
/*!
 * \}
 */


text:
package expressivathon;

import org.junit.Assert;

public class ProfitBuilder {

	private PriceAndVatBuilder buySide;
	private PriceAndVatBuilder sellSide;

	private NoVatPrice profit;

	private final StringBuilder journal = new StringBuilder();

	private ProfitBuilder() {
		journal.append("\nGiven ");
	}

	public static ProfitBuilder given() {
		return new ProfitBuilder();
	}

	public ProfitBuilder and() {
		journal.append("\nAnd ");
		return this;
	}

	public ProfitBuilder when() {
		journal.append("\nWhen ");
		return this;
	}

	public ProfitBuilder then() {
		journal.append("\nThen ");
		return this;
	}

	public ProfitBuilder aBuy(PriceAndVatBuilder buySide) {
		this.buySide = buySide;
		journal.append("a Buy " + buySide);
		return this;
	}

	public ProfitBuilder aSell(PriceAndVatBuilder sellSide) {
		this.sellSide = sellSide;
		journal.append("a Sell " + sellSide);
		return this;
	}

	public ProfitBuilder profit() {
		profit = profit(buySide.getPrice(), buySide.getVat(), sellSide.getPrice(), sellSide.getVat());
		journal.append("profit ");
		return this;
	}

	private NoVatPrice profit(WithVatPrice buyPrice, double buyVat, WithVatPrice sellPrice, double sellVat) {
		return sellPrice.withoutVat(sellVat).profitGiven(buyPrice.withoutVat(buyVat));
	}

	public String journal() {
		return journal.toString();
	}

	public void isEqualTo(NoVatPrice expected) {
		journal.append("is equal to " + expected);
		if (!profit.equals(expected)) {
			System.err.println(journal() + "\nKO: expected: " + expected);
			Assert.assertEquals("Actual profit does not match the expected profit", expected, profit);
		}
		System.out.println(journal() + "\nOK");
	}
}


text:
Basic Content Elements

HTML only represents a fraction of the effort that goes into building
  an effective web site, because the HTML vocabulary that you usually need is
  small. Altogether that vocabulary amounts to 24 elements —
  all of which are easy to remember and use, and nearly half of which are
  nearly identical in behavior to other elements on the list.

Many of these basic elements define structures that are familiar to users
  of word processing software; in addition to headings they include lists,
  paragraphs, and in bold/italic formatting.

There are also two container elements that HTML4.01 makes
  essential to effective stylesheets. In HTML5 those are supplanted by sectioning elements,
  which are introduced here and explained in the Advanced Topics.

Review and Related Concepts

The HTML Vocabulary is large, but the fraction of it that you need to
    know contains hardly more than a dozen unique elements.

The number of non-global attributes that you need to know to use that
    basic HTML vocabulary can be counted on the fingers of one hand.

HTML elements take default style values, which can be overridden with
    custom CSS.

When you use container elements well, stylesheets are easier
    to write.

A number of HTML5 elements cover the needs not only of containing content,
    but also dviding it into sections of varying purpose and
    importance.

Headings are used to introduce content at descending levels of scope;
    the lower the heading level, the narrower the scope of the content it introduces.

Inline frames often provide an environment for third-party content like
    video, social media buttons, and advertising.

Web browsers typically support four image formats, each suited to certain
    uses.

Hypertext Links: the a Element

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Anchor
    Required
    href (in practice)
    Phrasing/inline

Must not contain interactive elements such as form fields

Must only contain inline and image elements (HTML4.01)

It could be said that a is the grandaddy of all HTML elements,
  because — unlike the others — it does the work the web was
  meant for: describing human-readable links to other pages.

The contents of a link can amount to almost anything, especially in HTML5.

Attributes

hrefContains a valid URL that defines the destination of the link.

relTakes values that define the relationship of the link to other resources.

targetSet when the destination of the link should open in an inline frame.  The onclick event handler should be used if the link is to open in a new tab or window.

hreflangDeclares the language at the destination of the link; best used when the language in question is different than the language of the current page.

typeLike the hreflang attribute, advises about the characteristics of the link’s destination — in this case, with respect to MIME type.

mediaWhere type advises about the destination’s MIME type, this attribute advises about its intended medium, for example print or screen.

Publishing Effective Links

You should remember two rules when publishing a link: to be effective,
  it must be recognizable and lead to a meaningful destination.

A properly signaled link usually relies on two visual cues: color and
  underlining, these days controlled with the CSS color and text-decoration properties.
  Other links are instead designed to look like buttons, though they don’t
  often jitter like buttons when followed.

There is also the navigation area common to most sites, which usually
  appears in an upper corner of the page, or as a row of links underneath
  the title of the site.

Homepage Links and Self-Links

The title of a site often takes the form of a link back to its homepage.
  A quick survey of the Google News, NBC News, CNN, Fox News, BBC, and Australian
  Broadcasting Corporation News sites all demonstrate this behavior, as do
  popular non-news destinations like Amazon, Wikipedia, and Flickr.

Older pages also contain links to the pages on which they’re published.
  From time to time you might look at the status bar of your browser and
  see “go to # on this page” or something similar, especially
  on sites meant to function well in older browsers, which provide better
  scripting interfaces for links than for other elements.

div

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Document division
    Required
    None, but id or class should usually be present
    Flow/block
    None

The div element is generic by design, and does a lot of HTML’s
  heavy lifting. Unlike paragraphs, divs don’t carry any
  inherent meaning or default styles, instead relying on their id and class values
  to offer some kind of clue as to their purpose and appearance.

When browsers render a div off the bat, the result has no margins
  and stretches horizontally across the entire canvas.
### div and
the Style Cascade

In HTML4.01 especially, the purpose of div elements is to serve
  as containers that can be referenced in stylesheet selectors xref.
  If you create:

<div id="sidebar">
  <p>The bowl was full of <a href="http://apple.com/">apples</a> and <a
  href="http://www.orange.com/">oranges</a></p>
</div>

…You can then create a style rule for all of the hyperlinks that
  might lie inside #sidebar, like the following:

#sidebar a { color: #633; }

…As an alternative to creating a class for all of the
  links inside of #sidebar.

Sectioning Elements: header, nav, footer, article, section, and aside

[class="html5"]

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English names
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Page header, site navigation, page footer, etc.
    Required
    None
    Flow/Block

None, but it’s polite to make a habit of putting aside after,
          or at the end of, article

These elements are functionally identical to div, but they
  offer something that div doesn’t: just as browsers know
  what to do with lists and form elements, they will eventually be able to
  do special things with these elements for which div can’t
  provide any baked-in cues.

Note that the head and header elements are entirely
  different.

The “HTML5 Shim”

Older versions (6–8) of Microsoft Internet Explorer don’t
  define the sectioning elements as parts of HTML, and cannot style them
  off the bat. To fix this problem, several people have written what are
  called “HTML5 Shim” utilities with JavaScript. Shim scripts
  fall into a broader class of scripts called “polyfills” that
  aid small tasks or cover gaps in browser support.

Two especially popular HTML5 Shim scripts can be found at http://code.google.com/p/html5shim/ and
  https://github.com/ahume/html5-shim.

Paragraphs

p

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Paragraph
    Optional
    None
    Flow/block

Can only contain phrasing/inline elements, images, and HTML5 A/V elements

When we consider the structure of written language, there’s an obvious
  progression of scope: word, phrase, sentence, paragraph, passage. Because
  paragraphs are meant to set off discrete ideas, they need to be set off
  in web documents as in all other kinds… and that place is carved
  out by the p element.

Print Paragraphs and Web Paragraphs

Web pages scroll while printed pages stand still. This difference
  explains default paragraph styling in web browsers, where paragraphs are
  easier to read if they’re clearly set apart.

If you want to attempt a “traditional” look, apply the following
  CSS rule:

p { margin: 0; text-indent: 1.618em; text-align: justify; }

Introducing Collapsed Margins and Resets

When two HTML elements with non-zero margins lie aside one another, whether
  in a stack or a row, those margins collapse so that only
  the larger of the two parallel margins is visible on the rendered page.
  Since this behavior makes it more difficult to control page layout, professional
  developers often create what they call reset styles.

This author doesn’t use reset styles, for one reason: they rely
  on negation, even where consistency is more desirable.

Headings

h1 … h6

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Heading (level 1–6)
    Required
    None
    Flow/Block (heading)

The levels of successive headings within the same parent element should
          always descend.

Pages need titles, but the same is true of individual document sections,
  which is where headings come in. Well-composed headings can break up your content to
  make it both more readable, and more legible. More importantly, they also
  help the search engine performance of your pages.

h1 elements are most-significant; there should only be one
  on each page. The h4 element, on the other hand has always been
  styled by default at the same size as normal text, so it might be better
  to use h3 or h4 as your most typical heading level,
  then increase heading levels further as their scope broadens. See attached illustration.

Yes, Headings Are Ugly

Like word processors, web browsers tend to make the higher-level headings
  too big. The good news is that CSS allows you control the appearance of
  headings with the font-size  xref, font-weight  xref,
  and margin  xref properties.

Using More Than One Typeface on a Page

A common design technique in practically all textual media is to use one
  style of type for body copy, and another style for headings — for
  example, setting a newspaper article in Times or Palatino, while setting
  its title in Helvetica or Univers. The CSS font-family property xref and @font-face rule
  syntax xref allow you to do the same thing on your pages.

Lists

ul and ol

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Unordered / ordered list
    Required
    None
    Flow/block (list)

Must only contain list item (li) elements xref (which
          in turn are more flexible) and whitespace

We constantly encounter lists: to-do lists, shopping lists, feature lists,
  Top Ten lists… so lists were an obvious feature when Tim Berners-Lee
  started to create the technologies that we now call the web.

Web browsers support three explicit types of lists, of which two —
  unordered and ordered lists — are under discussion here. The only
  differences between these two are their tags (ul and ol)
  and the markers that are displayed when you use one or the other (bullets
  as opposed to numerals or letters).

Navigation links in HTML4.01 pages are often marked up within lists, which
  can be skipped or ignored by assistive technology.

Attributes (HTML4.01 only)

typeTakes the enumerated values 1, I, i, A, and a. The list-style-type CSS property xref should be preferred whenever possible.

startWhen used with an integer value, changes the numbering of all the items on the list. Supplanted by CSS3 Lists and Numbering, which are notoriously difficult to understand.

### Choosing the Correct List Type

Instinct usually offers the right judgement of the type of list that ought to be used in a particular situation.  The best thing to remember is that when an order is clearly meant, an ordered list should be used.

Markers and Margins

This author often uses CSS rules similar to what follows:

ul { padding-left: .618em; list-style-type: none; }
li { text-indent: -.618em; }

The default browser style typically left-justifies list item content and right-justifies list markers, both to a common margin.

Many browsers set the default padding-left value of lists to an absolute rather than proportional value, so lists set with an especially large or small font-size value might require some tweaking in this regard.

List Items

li

    <col id="engName" />
    <col id="endTag" />
    <col id="reqdAttr" />
    <col id="elementType" />
    <col id="elementReqs" />

        English name
        Closing tag
        Required attributes
        Type
        Notable Requirements

        List item
        Optional
        None
        Flow/block (list item)

Must only have a ul or ol element as an immediate parent

Must only have other li elements for siblings

List items are simple at first glance but don’t always stay that way, because lots of things can — and should — be drawn up in lists.  Outlines are nested lists.  Rules or guidelines of conduct or procedure often appear in lists, contain multiple paragraphs (or even sections), and might require headings.  Site navigation is often presented in lists for accessibility reasons.

List items require flexibility, so the specifications treat them like page divisions and sections: pretty much anything goes with respect to their contents.

Attributes

startTakes an integer value; when used within an ordered list, changes the numbering of that item and all successive list items. (HTML4.01 only)

Nesting Lists

To create a nested list, put a new list inside a list item, like this:

<ul>
  <li>Berries
    <ul><li>Blackberries</li><li>Strawberries</li><li>Raspberries</li></ul>
  </li>
  <li>Orchard fruit
    <ul><li>Apples</li><li>Pears</li><li>Plums</li></ul>
  </li>
</ul>

I’ve collapsed my carriage returns here, but it might be possible to set it differently in production.

The outermost list will have top and bottom margins as usual, but the nested lists will not.

The markup pattern shown above can recurse as many times as needed; this author has created his share of lists nested six and seven levels deep.

Images

img

    <col id="engName" />
    <col id="endTag" />
    <col id="reqdAttr" />
    <col id="elementType" />
    <col id="elementReqs" />

        English name
        Closing tag
        Required attributes
        Type
        Notable Requirements

        Image
        Forbidden

src

alt

        Embedded/replaced

alt must be present, but its value should be left empty (i.e., alt="") if the image cannot be assigned a meaningful text description

Like most of the other elements introduced in this section, the img element is pretty simple if you want it that way:  reference a valid image URL with the src attribute, and the browser will insert the image it finds at that location onto the page.

Attributes

srca valid URL referencing the image to be rendered; can be either an http:// or data: URL

alta string that provides a text description of the image

widthan integer or percentage value; integers are parsed as the desired display width in pixels, percentages as the desired proportion of the display width encoded in the image

heightcomparable to width

Current web browsers all support images encoded in the PNG (Portable Network
  Graphics), GIF (Graphics Interchange Format), JPEG (Joint Photographic
  Experts Group), and SVG (Scalable Vector Graphics) formats. The filenames
  of images encoded in these formats typically end in .png, .gif, .jpg,
  and .svg respectively.

Things to Remember About Images

If an image request returns a Not Found (404) error or a corrupt stream,
    browsers typically render the image’s alt value in its
    place.

For best results, width and height values should
    be provided as integers (representing pixels) to avoid undesirable layout
    effects.

The img element should be used for unambiguous content,
    and design accents relegated to CSS background-image attributes.

The four formats described above are each best suited to specific types
    of presentation: PNG when a transparent canvas/matte is called for, GIF
    to flat-color illustrations like infographics and cartoons, JPEG to photography
    and “photorealistic” illustrations, and SVG to vector drawings
    and vector animations. GIF images can also be animated, but often consume
    more network resources than an equivalent SVG or Flash presentation.

SVG images can be exported to a raster format (PNG, GIF, JPEG) without
    loss of quality at a given size, but exports in the opposite direction
    cannot.

JPEG compression is “lossy” so that all other factors being
    equal, an image exported to the JPEG format will result in lower image
    fidelity.

Resized copies of raster images will always be of lower quality than the
    originals, but good tools offer ways to minimize this loss of quality.

Used properly, data-URL encoded PNGs can conserve network
    resources, thus reducing page render time. data-URL encoding
    is discussed in the Advanced Topics.

Inline Frames

iframe

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Inline frame
    Required

src

width

height

    Embedded/replaced

CSS, JavaScript, and hypertext links cannot interact with iframe content
          from a different domain than the site that publishes the iframe itself

The iframe element lays out like images in practice, you won’t
  often see iframe elements containing pages that are served from
  the same site that serves the element itself. However, there are a lot
  of pages with inline frames in them, because they make excellent containers
  for third-party content like advertising and social media buttons.

Attributes

srcthe URL of the page to be served in the inline frame

widththe width in pixels of the inline frame

heightthe height in pixels of the inline frame

namea string that allows the inline frame to respond to hypertext links with target values indentical to the value set on this attribute

This list omits several attributes that are either too new to be supported
  broadly, too esoteric to justify inclusion in this book, or supplanted
  by CSS properties.

Using Inline Frames

Inline frames can be used to break many of the assumptions under which
  web browsers work, but they have one outstanding feature: they can be used
  to take content from Site ”B” — often an advertisement
  or a video — for secure display in the visual context of Site ”A”,
  presumably yours.

The most obvious purpose of an iframe is to create a single
  instance of a persistent site feature for repetition throughout the site,
  but there are other ways to do this that don’t violate any of the
  assumptions under which web browsers operate xref.

Emphasis and Strong Emphasis

em

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Emphasis
    Required
    None
    Phrasing/Inline
    None

The em element conveys emphasis of a word, phrase, or sentence.
  In visual (screen, print) media, this usually takes the form
  of italic or oblique (skewed-clockwise) text. Where present emphasis is
  especially valuable to assistive technology users, who will instantly know
  the difference between emphasis and text that’s italicized for other
  reasons.

Underlined text can also suggest emphasis, but this should be avoided
  since underlines are traditionally used to identify hypertext links.

strong

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Strong emphasis
    Required
    None
    Phrasing/inline
    None

As its long-form name suggests, the strong element conveys
  even stronger emphasis than the em element. The typical visual
  styling for this element is bold text.

strong elements should be preferred to b elements
  where appropriate, for the same reasons that em should be preferred
  to i.

Full capitalization offers another popular way to convey strong emphasis
  in text is full capitalization, but this should also be avoided, since
  long stretches of all-caps are harder to read than mixed-case text.

Other practical styling possibilities for all forms of emphasis are increased
  size and increased contrast, which are controlled with the font-size, color,
  and background-color CSS properties.

Small text and Spans

small

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

      English name
      Closing tag
      Required attributes
      Type
      Notable Requirements

      Small text
      Required
      None
      Phrasing/inline
      None

There are elements to provide emphasis, but what about de-emphasis, like
  spoken comments that would be made sotto voce? This voice is best
  taken up with the small element, and like its emphasized cousins,
  its default styles can be replaced with styles that reduce contrast rather
  than size.

This author is deluged with a stream of consciousness that tends to overrun
  its banks, so a visit to the product site for this book will probably uncover
  a few examples of this usage.

span

  <col id="engName" />
  <col id="endTag" />
  <col id="reqdAttr" />
  <col id="elementType" />
  <col id="elementReqs" />

    English name
    Closing tag
    Required attributes
    Type
    Notable Requirements

    Generic span
    Required
    None (but class is likely)
    Phrasing/inline
    None

Like its counterpart div  xref, span is
  a generic element most often used to attach JavaScript hooks or CSS selectors
  to a particular stretch of content, or spot on the page.

Another use for span is to avoid anonymous elements.
  Consider something like:

<div class="photo-with-caption"><img src="photo1.jpg" width="192" height="144"
alt="A smiling face.">Supporters greet the candidate at the airport.</div>

This use of markup is considered a best practice in HTML4.01, but presents
  enough problems that the figure/img/figcaption complex xref was
  specified for HTML5. The text content of the example is rendered in a separate
  element, but that element is never exposed to CSS or JavaScript. Why allow
  that, when you can put an element of your own in there?


text:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="20dp"
    android:height="20dp"
    android:viewportWidth="20"
    android:viewportHeight="20"
    android:tint="?attr/colorControlNormal">
  <path
      android:fillColor="@android:color/white"
      android:pathData="M6,18.375 L4.208,16.583 10.792,10 4.208,3.417 6,1.625 14.375,10Z"/>
</vector>


text:
DROP TABLE IF EXISTS job2_earthquake_agg;

CREATE TABLE job2_earthquake_agg AS
SELECT
    name, ST_AsGeoJson(boundaryshape) AS geojson_boundary, count(*) cnt FROM counties
JOIN
    earthquake
WHERE
    ST_Contains(counties.boundaryshape, earthquake.shape)
GROUP BY
    counties.name, counties.boundaryshape
ORDER BY
    cnt DESC;

SELECT name,cnt FROM job2_earthquake_agg;


text:
//-----------------------------------------------------------------------
// <copyright file="CardTestUtilities.cs" company="PokerCo">
//     PokerCo.
// </copyright>
// <summary>This is the CardTestUtilities class.</summary>
//-----------------------------------------------------------------------
namespace TestPoker
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Poker;

    /// <summary>
    /// Class containing the necessary utilities for building the unit tests for the Poker project.
    /// Some of the optimizations were made after several tests were implemented so tests must be refactored to use the latest optimized
    /// methods and properties from this class.
    /// </summary>
    public static class CardTestUtilities
    {
        /// <summary>
        /// Dictionary containing all possible card faces.
        /// </summary>
        public static readonly Dictionary<int, Poker.CardFace> Faces = new Dictionary<int, Poker.CardFace>()
        {
            { 2, CardFace.Two },
            { 3, CardFace.Three },
            { 4, CardFace.Four },
            { 5, CardFace.Five },
            { 6, CardFace.Six },
            { 7, CardFace.Seven },
            { 8, CardFace.Eight },
            { 9, CardFace.Nine },
            { 10, CardFace.Ten },
            { 11, CardFace.Jack },
            { 12, CardFace.Queen },
            { 13, CardFace.King },
            { 14, CardFace.Ace },
        };

        /// <summary>
        /// Dictionary containing all possible card suits.
        /// </summary>
        public static readonly Dictionary<int, Poker.CardSuit> Suits = new Dictionary<int, Poker.CardSuit>()
        {
            { 1, CardSuit.Clubs },
            { 2, CardSuit.Diamonds },
            { 3, CardSuit.Hearts },
            { 4, CardSuit.Spades },
        };

        /// <summary>
        /// List containing all the 52 cards in a poker game.
        /// </summary>
        public static readonly IList<Poker.ICard> AllCards = GeneratAllCards();

        /// <summary>
        /// Dictionary containing all possible valid combinations of poker hands.
        /// The key is the hand and the value is HandCategory type for this hand.
        /// </summary>
        public static readonly IDictionary<Poker.IHand, Poker.HandCategory> AllHandsAndTypes = GenerateAllHandsAndTypes();

        /// <summary>
        /// Checks if all cards passed as a parameter array are different.
        /// </summary>
        /// <param name="cards">Array or comma separated parameters with all the cards to check.</param>
        /// <returns>Returns true if all cards are different, and false if there is at least one repeating card.</returns>
        public static bool AllDifferentCards(params Poker.ICard[] cards)
        {
            for (int cardChecked = 0; cardChecked < cards.Length; cardChecked++)
            {
                for (int otherCard = cardChecked + 1; otherCard < cards.Length; otherCard++)
                {
                    if (cards[cardChecked] == cards[otherCard])
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// Generates a list of all the 52 cards in poker game.
        /// </summary>
        /// <returns>List of all valid cards.</returns>
        private static IList<Poker.ICard> GeneratAllCards()
        {
            IList<ICard> cardsList = new List<ICard>();
            foreach (var cardFace in CardTestUtilities.Faces)
            {
                foreach (var cardSuit in CardTestUtilities.Suits)
                {
                    cardsList.Add(new Card(cardFace.Value, cardSuit.Value));
                }
            }

            return cardsList;
        }

        /// <summary>
        /// Generates a dictionary containing all possible valid combinations of poker hands.
        /// </summary>
        /// <returns>Dictionary containing all valid poker hands.
        /// The key is the hand and the value is HandCategory type for this hand.</returns>
        private static IDictionary<Poker.IHand, Poker.HandCategory> GenerateAllHandsAndTypes()
        {
            IDictionary<IHand, HandCategory> hands = new Dictionary<IHand, HandCategory>();
            HandCategory handType;
            IHand hand;
            for (var firstCard = 0; firstCard < CardTestUtilities.AllCards.Count; firstCard++)
            {
                for (var secondCard = firstCard + 1; secondCard < CardTestUtilities.AllCards.Count; secondCard++)
                {
                    for (var thirdCard = secondCard + 1; thirdCard < CardTestUtilities.AllCards.Count; thirdCard++)
                    {
                        for (var forthCard = thirdCard + 1; forthCard < CardTestUtilities.AllCards.Count; forthCard++)
                        {
                            for (var fifthCard = forthCard + 1; fifthCard < CardTestUtilities.AllCards.Count; fifthCard++)
                            {
                                handType = HandCategory.HighCard;
                                bool allSequential =
                                    (((firstCard / 4) + 4) == (fifthCard / 4)) &&
                                    (((secondCard / 4) + 3) == (fifthCard / 4)) &&
                                    (((thirdCard / 4) + 2) == (fifthCard / 4)) &&
                                    (((forthCard / 4) + 1) == (fifthCard / 4));

                                bool lowCardAce = (firstCard / 4 == 0) && (secondCard / 4 == 1) &&
                                    (thirdCard / 4 == 2) && (forthCard / 4 == 3) && (fifthCard / 4 == 12);

                                allSequential = allSequential || lowCardAce;

                                bool oneSuit =
                                    ((firstCard % 4) == (fifthCard % 4)) &&
                                    ((secondCard % 4) == (fifthCard % 4)) &&
                                    ((thirdCard % 4) == (fifthCard % 4)) &&
                                    ((forthCard % 4) == (fifthCard % 4));

                                int[] cardRanks = new int[]
                                    {
                                        firstCard / 4,
                                        secondCard / 4,
                                        thirdCard / 4,
                                        forthCard / 4,
                                        fifthCard / 4
                                    };

                                IDictionary<int, int> countOfRepetitions = new Dictionary<int, int>();

                                foreach (var rank in cardRanks)
                                {
                                    if (countOfRepetitions.ContainsKey(rank))
                                    {
                                        countOfRepetitions[rank]++;
                                    }
                                    else
                                    {
                                        countOfRepetitions[rank] = 1;
                                    }
                                }

                                bool fourOfAKind = countOfRepetitions.Values.Contains(4);
                                bool fullHouse = countOfRepetitions.Values.Contains(3) && countOfRepetitions.Values.Contains(2);
                                bool threeOfKind = countOfRepetitions.Values.Contains(3);
                                bool twoPair = countOfRepetitions.Where(x => x.Value == 2).Count() == 2;
                                bool onePair = countOfRepetitions.Values.Contains(2);

                                if (allSequential)
                                {
                                    if (oneSuit)
                                    {
                                        handType = HandCategory.StraightFlush;
                                    }
                                    else
                                    {
                                        handType = HandCategory.Straight;
                                    }
                                }
                                else if (oneSuit)
                                {
                                    handType = HandCategory.Flush;
                                }
                                else if (fourOfAKind)
                                {
                                    handType = HandCategory.FourOfAKind;
                                }
                                else if (fullHouse)
                                {
                                    handType = HandCategory.FullHouse;
                                }
                                else if (threeOfKind)
                                {
                                    handType = HandCategory.ThreeOfAKind;
                                }
                                else if (twoPair)
                                {
                                    handType = HandCategory.TwoPair;
                                }
                                else if (onePair)
                                {
                                    handType = HandCategory.OnePair;
                                }

                                hand = new Hand(
                                        new List<ICard>()
                                        {
                                            new Card(CardTestUtilities.AllCards[firstCard]),
                                            new Card(CardTestUtilities.AllCards[secondCard]),
                                            new Card(CardTestUtilities.AllCards[thirdCard]),
                                            new Card(CardTestUtilities.AllCards[forthCard]),
                                            new Card(CardTestUtilities.AllCards[fifthCard]),
                                        });

                                hands.Add(hand, handType);
                            }
                        }
                    }
                }
            }

            return hands;
        }
    }
}