Skip to content

A portable load-balancing-focused JavaScript rendering framework.

Notifications You must be signed in to change notification settings

SysError99/Se.JS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Se.JS

A portable load-balancing-focused JavaScript rendering framework.

This is just an experimental project, and being used for my own website. But you can try it :)


Getting Started

Just clone this project to your folder. Actually, only se.js file is required. Then, create an HTML file as follows:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Se.JS!</title>
</head>
<body>
    Hello Se.JS!
</body>
</html>

You can choose any name as you desrired to. For this guide, I will choose index.html. Now we can create our JavaScript file for this page.

//If you put se.js file in other part of your site directory, just change it to your directory.
import * as Se from "./js/se.js"

/* The rest of code goes here. */

Now we can save this file. You can also choose any name you wanted, but I'd prefer the same name of the page, so it is index.js. Then, put this HTML code to your page we've created recently. Prefer <head> part first.

    <script type="module" src="index.js"></script>

And we are all set! Now we can learn more from here.


Page Module

When you design some of your websites, you may think about how to maintain all your pages. For example, you want to create many pages that have the same navigation bar. In simplest approach, you duplicate all of them eache pages, which is not a good way to do. In server-side, you may write your HTML header in some parts of your server code, then render them in response. In client-side, you can also use jQuery trick to achieve this:

$.get("navigation.html", function(data){
    $("#nav-placeholder").replaceWith(data);
});

But in Se.JS, it is 'dead simple' Just put an attribute in any HTML tags you wanted to show. For example, if we have web page like this:

<body>
    <!--Navigation bar goes here!-->
    <div class="navbar"></div>
    <!--Content goes here!-->
    <div class="content"></div>
    <!--Footer goes here-->
    <div class="footer"></div>
</body>

If you want to put your navigation bar file, for now it is named navbar.html, also has a structure like this:

<div class="topnav">
    <a> ≡ Navigator</a>
    <div class="topnav-menu" id="navlinks">
        <a href="index.html"> Home</a>
        <a href="images.html"> Photos</a>
        <a href="map.html"> Map</a>
        <a href="reserve.php"> Reserve</a>
        <a href="scr_auth.php"> Login</a>
        <a href="scr_auth.php"> Report</a>
        <a href="contact.php"> Contact Us</a>
    </div>
</div>

To import it, you just put se-html attribute in the tag like this:

<body>
    <!--Navigation bar goes here!-->
    <div class="navbar" se-html="navbar.html"></div>
    <div class="content">
        Some of cool content goes here!
    </div>
    <div class="footer"></div>
</body>

For other components, like CSS file, you can also put se-css attribute somewhere in your HTML page, maybe <body> or <head> tag, like <head se-css="awesome.css">. In the complete web page, it should look like this:

<html>
<!-- CSS is here! -->
<head se-css="awesome.css">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Se.JS!</title>
</head>
<body>
    <div class="navbar" se-html="navbar.html"></div>
    <div class="content">
        Some of cool content goes here!
    </div>
    <div class="footer"></div>
</body>
</html>

Vala, we have done with the Page Module!

Note: Due to security concerns, put <script> tag inside page module will not work. I'd suggest you to put <script src="navbar.js"></script> alongside your <div> of the page module, or using Se.JS'scomponent, which I'll talk more about it later.


Component

In dynamic websites, they take request from users, process them, then send results back. Basically, in some web servers, they take care all of tasks and send them to user statically. Everything happened request by request, a new request means an entire new webpage to be created and send back to user. This may not be a problem for websites that are no need using real-time data. But when you need a real-time responsive website. Constantly forcing the web page to reload is probably not ideal. So, another approach need to be applied to achieve this.

Basically, using jQuery/AJAX can achieve this and give a result you expected, like this:

var obj = jQuery.parseJSON(result);
if(obj != ''){
    $("#body").empty();
    $.each(obj, function(key, val) {
        var tr = "<tr>";
        tr = tr + "<td>" + val["CustomerID"] + "</td>";
        tr = tr + "<td>" + val["Name"] + "</td>";
        tr = tr + "<td>" + val["Email"] + "</td>";
        tr = tr + "<td>" + val["CountryCode"] + "</td>";
        tr = tr + "<td>" + val["Budget"] + "</td>";
        tr = tr + "<td>" + val["Used"] + "</td>";
        tr = tr + "</tr>";
        $('#table > tbody:last').append(tr);
    });
}

This may not look like a big deal. But what if your data have plenty of them, or they are stacked up, like this social media's post:

let data = {
    id: 1234,
    name: "johnmccrane0420",
    desc: "Nice trip today! :D",
    year: 2020,
    month: 4,
    day: 20,
    hour: 17,
    min: 30,
    img: "https://cdn.social.media/0123456789.png",
    like: 5,
    comment:[
        {
            id: 5678,
            name: "jenny9228",
            desc: "That looks cool really!",
            year: 2020,
            month: 4,
            day: 20,
            hour: 19,
            min: 10,
            img: "",
            like: 2,
            comment:[
                {
                    id:9024,
                    name:"elmotttt",
                    desc: "I think so too!",
                    year: 2020,
                    month: 4,
                    day: 20,
                    hour: 20,
                    min: 04,
                    img: "",
                    like: 0
                },
                {
                    id:3333,
                    name:"nickrareman",
                    desc: "Couldn't agree more!",
                    year: 2020,
                    month: 4,
                    day: 20,
                    hour: 20,
                    min: 33,
                    img: "",
                    like: 0
                }
            ]
        }
    ]
}

This can be a nightmare, and extremely hard to maintain a clean and reusable code. But in Se.JS, this can be solved quickly and easily. Let's get started.

In your web page javascript file, use function Se.res() to create a reusable component resource for the page, then write base of HTML component for this data.

Let's start with creating a new component with Se.res() first!

import * as Se from "./js/se.js"

//Create a component for our post.
var postComponent = Se.res("comp",`
    <!--HTML Goes Here--->
`)

When "comp" stands for component (other res types can also be used, including "css", "html", but they are more easier using attributes in the HTML file). But before continue, we need to understand how the component works first.


Object Properties

Every object in JavaScript has an ability to store their properties. For Se.res(), it will be used to render values in the component, by simply use a dollar sign ($), followed by your object property name. It can be used at any parts in your HTML component code.

If we figure out how the data looks like, so there are 3 sub parts of them, so our HTML component should look like this:

    <div id="$id-post" class="post">

        <!-- Post -->
        <div id="$id-header">
            <h1>$name</h1>
            $day-$month-$year $hour:$min
        </div>

        <p>$desc</p>
        
        <div class="post-img"> <img src="$img"> </div>
        
        <div class="post-comment">
            
            <!-- Comments -->
            <div id="$id-post" class="comment">

                <div id="$id-header">
                    <h2>$name</h2> $day-$month-$year $hour:$min
                </div>
                    
                <p>$desc</p>
                
                <div id="post-img"> <img src="$img"> </div>
                
                <div class="post-sub-comment">
                
                    <!-- Sub Comments -->
                    <div id="$id-post" class="sub-comment">
                        
                        <div id="$id-header">
                            <h3>$name</h3> $day-$month-$year $hour:$min
                        </div>
                            
                        <p>$desc</p>
                        
                        <div id="post-img"> <img src="$img"> </div>
                        
                    </div>
                    <!-- End Sub Comments -->

                </div>

            </div>
             <!-- End Comments -->

        <div>

    </div>

Arrayed Component

Now we're able to render our object properties in our component, but what about array? In the example data, we have 2 stacks of the array, so what to do next? Simple, just put them in an array bracket! Let's have a look at our data again, now I will simplify them just to do you get the point:

let data = {
    //... data object ...//
    comment:[
        {
            //... comment object...//
            comment:[
                {
                    //... sub-comment number 1 ...//
                },
                {
                    //... sub-comment number 2 ...//
                }
            ]
        }
    ]
}

Now in our component, an array bracket has a stucture like this:

$arrayName{
    <Contents-Here>
}arrayName$

When arrayName stands for array name you want to render them. For example, you want to render comment array, your array bracket should look like this:

$comment{
    <Contents-Here>
}comment$

An array bracket can also be stackable. In this example, we have 2 stacks of them, to the rest of it should looke like this:

    $comment{
        <Contents-Here>
        $comment{
            <Sub-Contents-Here>
        }comment$
    }comment$

Now let's put array brackets in! It should look like this:

    <div id="$id-post" class="post">

        <!-- Post -->
        <div id="$id-header">
            <h1>$name</h1>
            $day-$month-$year $hour:$min
        </div>

        <p>$desc</p>
        
        <div class="post-img"> <img src="$img"> </div>
        
        <div class="post-comment">
        
        $comment{ <!-- Comments -->
            
            <div id="$id-post" class="comment">

                <div id="$id-header">
                    <h2>$name</h2> $day-$month-$year $hour:$min
                </div>
                    
                <p>$desc</p>
                
                <div id="post-img"> <img src="$img"> </div>
                
                <div class="post-sub-comment">
                
                $comment{ <!-- Sub Comments -->
                    
                    <div id="$id-post" class="sub-comment">
                        
                        <div id="$id-header">
                            <h3>$name</h3> $day-$month-$year $hour:$min
                        </div>
                            
                        <p>$desc</p>
                        
                        <div id="post-img"> <img src="$img"> </div>
                        
                    </div>
                    
                }comment$ <!-- End Sub Comments -->

                </div>

            </div>
            
        }comment$ <!-- End Comments -->

        <div>

    </div>

And that's it! Now you have complete reusable component for your web page. Let's put some code to make a magic happen! Do you still remember component name of this example? It is postComponemt! Let's build a component with Se.comp()!

//This command creates a component, then instantly show the result.
let post = new Se.comp("objPost", postComponent, data)

When "objPost" stands for element ID in HTML (string can also be used), postComponent stands for component object that wil be used, and data stands for data to be bound (can be left empty)

To summarise, all of the rest should look like this:

import * as Se from "./js/se.js"

//Create a component named "post"
let postComponent = Se.res("comp",`
    <div id="$id-post" class="post">

        <!-- Post -->
        <div id="$id-header">
            <h1>$name</h1>
            $day-$month-$year $hour:$min
        </div>

        <p>$desc</p>
        
        <div class="post-img"> <img src="$img"> </div>
        
        <div class="post-comment">
        
        $comment{ <!-- Comments -->
            
            <div id="$id-post" class="comment">

                <div id="$id-header">
                    <h2>$name</h2> $day-$month-$year $hour:$min
                </div>
                    
                <p>$desc</p>
                
                <div id="post-img"> <img src="$img"> </div>
                
                <div class="post-sub-comment">
                
                $comment{ <!-- Sub Comments -->
                    
                    <div id="$id-post" class="sub-comment">
                        
                        <div id="$id-header">
                            <h3>$name</h3> $day-$month-$year $hour:$min
                        </div>
                            
                        <p>$desc</p>
                        
                        <div id="post-img"> <img src="$img"> </div>
                        
                    </div>
                    
                }comment$ <!-- End Sub Comments -->

                </div>

            </div>
            
        }comment$ <!-- End Comments -->

        <div>

    </div>
`)
//Create data object
let data = {
    id: 1234,
    name: "johnmccrane0420",
    desc: "Nice trip today! :D",
    year: 2020,
    month: 4,
    day: 20,
    hour: 17,
    min: 30,
    img: "https://cdn.social.media/0123456789.png",
    like: 5,
    comment:[
        {
            id: 5678,
            name: "jenny9228",
            desc: "That looks cool really!",
            year: 2020,
            month: 4,
            day: 20,
            hour: 19,
            min: 10,
            img: "",
            like: 2,
            comment:[
                {
                    id:9024,
                    name:"elmotttt",
                    desc: "I think so too!",
                    year: 2020,
                    month: 4,
                    day: 20,
                    hour: 20,
                    min: 14,
                    img: "",
                    like: 0
                },
                {
                    id:3333,
                    name:"nickrareman",
                    desc: "Couldn't agree more!",
                    year: 2020,
                    month: 4,
                    day: 20,
                    hour: 20,
                    min: 33,
                    img: "",
                    like: 0
                }
            ]
        }
    ]
}
//Create component using "post" component, and bind the data.
let post = new Se.comp("objPost", postComponent, data)

And we are all set for this part!

Note: Any value assigned to the component with dollar sign ($) must be exist. Se.JS component will not automatically delete them if assigned data do not exist. At least, they must be empty, or you will have a strange tags or dollar sign values like $something


Reactive Component

In modern JavaScript Frameworks, like Vue.js, it provides many cool things to make a development much easier, one of them is 'reactivity'. But in Se.JS, it's called a reactive component.

A reactive component is a form of the component that is "reactive", means that the object has instant reaction with data they received. No need to trigger any events or watchers to make them happen. Se.JS also provides a reactive component, which can be useful in some cases, like minor data update. The component can be declared with Se.reactComp() prototype.

var comp = new Se.reactComp("compId", compObject, data, target)

When "compId" stands for component ID, componentObject stands for component object, data stand for data to be bound (can be left empty), and target stands for ID to target to be bound by the component.

Everytime you want to get or set some data, simply type comp.data followed by anything you want, like comp.data.name = "John". When you change the value, the component will get updated instantly.

If you have to assign many values in one time, or you don't need a reactive website, I'd suggest you to use regular Se.comp() instead, and use Se.compSet() to update data manually.


Empty Object Properties

Data received from the server can be varied. To dealing with empty data, simply put empty bracket, then put your HTML code as you desire. Let's take a look for an example:

var data = {
    name: ""
}

For the structure of empty bracket, it looks like this:

!propertyName{
    <Your-contents-here>
}propertyName!

For this example, name of the property is name, now apply to HTML code for the component:

$name{
    <h1> Your name is $name. </h1>
}$name
!name{
    <h1> I don't know your name! </h1>
}name!

Single-value Arrayed Component

To access single-valued data inside array, simply create arrayed component, then put $[] inside the component where you want the data to be shown. Using $@ will tell a position of the data. For this example, we have data like this:

var data = {
    contact:["John", "Leona", "Bruce"]
}

In HTML code, it looks like this:

<div id="contact">
    <ol>
        $contact{
            <li> $[] (Number $@) </li>
        }contact$
    </ol>
    !contact{
            <h1> No contacts! </h1>
    }contact!
</div>

Now, you may notice some flaws in this HTML, that <ol> tag is "NOT" supposed to be here when there is no data in the array. This is how conditional component comes in place. Let's continue on!


Conditional Component

In many cases, conditions need to be applied for the component to behave as we desire. In Se.JS, a conditional component has a structure like this:

?condition(expression){
    <Contents-Here>
}?

For condition (conditional statement) in Se.JS, there are 3 of them:

  • if condition.
  • elif (else if) condition.
  • else condition.

For expression, it can be anything in JavaScript, from common expressions, to all functions that return values.

Let's go for some examples! Here is our data.

var data = {
    fruits: ["Apple","Banana","Cherry","Durian"]
}

Now for our HTML component:

?if($fruits.length !== 0){
    <h1> Our Basket </h1>
    <ol>
        $fruits{
            <li> $[] </li>
        }fruits$
    </ol>
}?
?else{
    <h1> Our basket is empty! <h1>
}?

Here is a full source code:

import * as Se from "./js/se.js"

let fruitComponent = Se.res("comp",`
?if($fruits){
    <h1> Our Basket </h1>
    <ol>
        $fruits{
            <li> $[] </li>
        }fruits$
    </ol>
}?
?else{
    <h1> Our basket is empty! <h1>
}?
`)
var data = {
    fruits: ["Apple","Banana","Cherry","Durian"]
}
new Se.comp('myFruit',fruitComponent,data)

Event Handling

There are no specific event handling methods designed for Se.JS. To be honest, at least for me, I don't really find them useful. But that does not mean it is impossible to implement event handling for Se.JS. I provided some ways to make event handling for the component. Let's get started!

First, let's understand how browser's JavaScript work. In the browser, there are at leaast 2 scopes of area

  1. module scope, where Se.JS is working.
  2. browser, global or window scope, where the browser and regular scripts are in.

Generally, both of these are seperated, and can't be accessed by each other, especially in browser scope. JavaScript module also provides window as browser scope in the module, and can be accessed directly. We can use this area to communicate between module and browser.

However, using window to declare things is considerd a "bad practice", since it increases JavaScript engine overhead, reduces code maintainability, and risk of unexpected default scope changes. So there is a solution for this.

In Se.JS, there is a space called Se.global, which is used for declaring anything you wanted for the browser environment (since putting anything in global scope is a bad practice) In a module script, we can provide a new thing inside Se.global like this:

import * as Se from "./js/se.js"

//declare something
Se.global.x = 0
Se.global.arr = []
Se.global.obj = {}
Se.global.greet = () => { //function
    alert('Hello World!')
}

Now in HTML component/page, we can use them via Se object:

    <!-- Call greet() from Se.global in module -->
    <button onclick="Se.greet()">Greet!</button>

To do a combination with component we can use symbol $@ to define a position we want to interact with. Let's see an example, a fruit basket application:

import * as Se from "./js/se.js"

//component resource
let fruitBasketApp = Se.res("comp",`
<h2> Fruit Basket </h2>
<input id="fruit-name" type="text"><br><br>

<!-- We use only "Se." for browser scope -->
<button onclick="Se.fruitAdd()">Add this fruit</button>

?if($fruits.length !== 0){
<ol>
    $fruits{
        <li>
            <!-- $@ is used to declare data position, $[] is used to pull data from position -->
            $[] <button onclick="Se.fruitEdit($@)">Edit</button>
        </li>
    }fruits$
</ol>
}?
?else{
    <h4> No fruits. </h4>
}?
`)

//component
let comp = new Se.reactComp('myApp', fruitBasketApp,{
    fruits:[]
})

//functions
Se.global.fruitAdd = function(){ //We use "Se.global." in module scope.
    var fruitName = Se.ele('fruit-name').value //Se.ele() is a shorthand of document.getElementById()
    comp.data.fruits.push(fruitName) //push new fruit to array
}
Se.global.fruitEdit = function(pos){
    comp.data.fruits[pos] = prompt('Enter new fruit name: ')
}

Component ID

Unlike many JavaScript frameworks, in Se.JS, there is no way specifically provided for components to have their own functions (You can implement them, but you can't use them) So, to make components be able to use the same function, you have to assign a component ID for a component.

Component ID being used for a component can be number, or string:

var object = new Se.comp("myName",...)

You can also find a component using ID with Se.where() function

var objFound = Se.where("myName")

In a component structure, to access a component ID, simply put $#? in anywhere you want to delare it:

Se.res("comp","myComponent",`
    <h1> My ID is $#?. </h1>
`)

To implement functions be able to be used by many of components, we can declare an array for storing components Let's take a look for an example. We need many post components:

import * as Se from "./js/se.js"

Se.res("comp","postComponent",`
<div id="$#?" "class="post">
    <h6> Post number $#? <h6>
    $text<br>
    <button onclick="Se.postLike($#?)">$like Like</button>
    <button onclick="Se.postComment($#?)">Comment</button><br>
    $comment
</div>
`)
//create an array for post components.
const posts = []
function newPost (){
    //We use posts.length for assigning ID.
    posts.push(new Se.reactComp( posts.length,"postComponent",{
        text: "This is a post.",
        like: 0,
        comment: ""
    }))
}
//create functions for components
Se.global.postLike = function(pos){
    posts[pos].data.like++
    alert("You have liked post number "+pos)
}
Se.global.postComment = function(pos){
    posts[pos].data.comment = prompt("Your comment: ")
}
//let's simulate two posts
var rp = 2
while(rp--) newPost()

Now components are able to use same functions!

This method is 'highly' recommended if you want to create dynamically loaded contents for your website, since it is more device friendly and does not take much of system resources.


Other Things

Se.JS also provides additional features to make development easier.

Resource Category

  1. Se.addRequestHeader(header, value) Add a request header with value for XMLHTTPRequest.

  2. Se.clearRequestHeader() Clear all request headers.

  3. Se.request(method, target, data, onsuccess, onfail) performs XMLHTTPRequest

  • method Request method (such as 'GET', 'POST')
  • target Request target (URL of a target)
  • data Data to be sent.
  • onsuccess Function to be called when a request is successful (this also provides responseText attribute)
  • onfailed Function to be called when a request is failed.
  1. Se.invoke() Invoke all se-* attributes in HTML file This performs automatically once

  2. Se.res(type, name, str, element) Add a resource with a string.

  • type Resource type (html, css,comp)
  • name Resource name.
  • str Data to be used as resource.
  • element Element to be added in (for css and comp, this is an optional)
  1. Se.resFile(type, name, element) Add a resource with a text-based file.
  • type Resource type (html, css,comp)
  • name Text-based file location.
  • element Element to be added in (for css and comp, this is an optional)
  1. Se.unload(type, name) Remove resource.
  • type Type of resource:
  • css Delete all CSS
  • comp Delete comp that is named name
  • Others, cleanup innerHTML of an element.

Element Category

  1. Se.elesClass(class) Find elements by class name.

  2. Se.elesName(name) Find elements with name attribute

  3. Se.elesNS(ns,name) Find elements with namespace.

  4. Se.elesTag(tag) Find elements with HTML tag.

  5. Se.ele(id) Find an element with ID.

  6. Se.qs(query) Performs document.querySelector() method.

Global Value Category

  1. Se.global A Global value for Se.JS.

Component Cagetory

  1. Se.where(id) Find a component by ID.

  2. new Se.comp(id,comp,target, data) Create a static component.

  • id Component ID.
  • comp Component name.
  • target Target element to be bound with a component.
  • data Data to be used for a component.
  1. (Prototype) @comp.set(data) Set a component using data

  2. (Prototype) @comp.visibility(visible) Set style.visibility for a component.

  3. (Prototype) @comp.display(display) Set a component's style.display attribute.

  4. (Prototype) @comp.clean() Wipe innerHTML inside a component.

  5. (Prototype) @comp.kill() Kill (delete) a component.

  6. new Se.reactComp(id,comp,target, data) Create a reactive component.

  • id Component ID.
  • comp Component name.
  • target Target element to be bound with a component.
  • data Data to be used for a component

About

A portable load-balancing-focused JavaScript rendering framework.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published