// CreateFragment returns a DocumentFragment with the given html dom func CreateFragment(html string) *js.Object { //if we are not in a browser,dont do anything. if !detect.IsBrowser() { return nil } // div := doc.CreateElement("div") div := CreateElement("div") //build up the html right in the div SetInnerHTML(div, html) //create the document fragment fragment := CreateDocumentFragment() //add the nodes from the div into the fragment nodes := ChildNodeList(div) ContextAppendChild(fragment, nodes...) //unwrap all the special Text UnknownELement we are using // UnWrapSpecialTextElements(fragment) return fragment }
// init intializes the internal state management variables used in dispatch. func init() { if detect.IsBrowser() { history = History(HashSequencer) // Initiate to the current path. history.Follow(GetLocation()) } }
// BrowserSupportsPushState checks if browser supports pushState func BrowserSupportsPushState() bool { if !detect.IsBrowser() { return false } return (js.Global.Get("onpopstate") != js.Undefined) && (js.Global.Get("history") != js.Undefined) && (js.Global.Get("history").Get("pushState") != js.Undefined) }
// New returns a new design.Resources activating the DOMRenderer if its gets called // on the browser or else on the server. func New() *design.Resources { if detect.IsBrowser() { return design.New(&redom.DOMRenderer{ Document: dom.GetWindow().Document(), }) } return design.New() }
// CancelAnimationFrame provides a cover for RAF using the // js api cancelAnimationFrame. func CancelAnimationFrame(id int, f ...func()) { if !detect.IsBrowser() { return } js.Global.Call("cancelAnimationFrame", id) for _, fx := range f { fx() } }
// RequestAnimationFrame provides a cover for RAF using the js // api for requestAnimationFrame. func RequestAnimationFrame(r Mux, f ...func()) int { if !detect.IsBrowser() { return -1 } id := js.Global.Call("requestAnimationFrame", r).Int() for _, fx := range f { fx() } return id }
// GetLocation returns the path and hash of the browsers location api else // panics if not in a browser. func GetLocation() (host string, path string, hash string) { if !detect.IsBrowser() { return } loc := js.Global.Get("location").String() ups, err := url.Parse(loc) if err != nil { return } host = ups.Host path = ups.Path hash = ups.Fragment return }
func panicBrowserDetect() { if !detect.IsBrowser() { panic("expected to be used in a dom/browser env") } }
func init() { if detect.IsBrowser() { rafPolyfill() } }
// init initalizes properties and functions necessary for package wide varaibles. func init() { if detect.IsBrowser() { initScrollProperties() } }
// Patch takes a dom string and creates a documentfragment from it and patches a existing dom element that is supplied. This algorithim only ever goes one-level deep, its not performant // WARNING: this method is specifically geared to dealing with the haiku.Tree dom generation func Patch(fragment, live *js.Object, onlyReplace bool) { //if we are not in a browser,dont do anything. if !detect.IsBrowser() { return } if !live.Call("hasChildNodes").Bool() { // if the live element is actually empty, then just append the fragment which // actually appends the nodes within it efficiently ContextAppendChild(live, fragment) return } shadowNodes := ChildNodeList(fragment) liveNodes := ChildNodeList(live) // FIXED: instead of going through the children which may be many, patchloop: for n, node := range shadowNodes { if node == nil || node == js.Undefined { continue } if node.Get("constructor") == js.Global.Get("Text") { if _, empty := EmptyTextNode(node); empty { ContextAppendChild(live, node) continue patchloop } var liveNodeAt *js.Object if n < len(liveNodes) { liveNodeAt = liveNodes[n] } if liveNodeAt == nil || liveNodeAt == js.Undefined { ContextAppendChild(live, node) } else { InsertBefore(live, liveNodeAt, node) } continue patchloop } //get the tagname tagname := GetTag(node) // get the basic attrs var id, hash, class, uid string // do we have 'id' attribute? if so its a awesome chance to simplify if HasAttribute(node, "id") { id = GetAttribute(node, "id") } if HasAttribute(node, "class") { id = GetAttribute(node, "class") } // lets check for the hash and uid, incase its a pure template based script if HasAttribute(node, "hash") { hash = GetAttribute(node, "hash") } if HasAttribute(node, "uid") { uid = GetAttribute(node, "uid") } // if we have no id,class, uid or hash, we digress to bad approach of using Node.IsEqualNode if allEmpty(id, hash, uid) { AddNodeIfNone(live, node) continue patchloop } // eliminate which ones are empty and try to use the non empty to get our target if allEmpty(hash, uid) { // is the id empty also then we know class is not or vise-versa if allEmpty(id) { // log.Printf("adding since class") // class is it and we only want those that match narrowing our set no := QuerySelectorAll(live, class) // if none found we add else we replace if len(no) <= 0 { ContextAppendChild(live, node) } else { // check the available sets and replace else just add it AddNodeIfNoneInList(live, no, node) } } else { // id is it and we only want one // log.Printf("adding since id") no := QuerySelector(live, fmt.Sprintf("#%s", id)) // if none found we add else we replace if no == nil || no != js.Undefined { ContextAppendChild(live, node) } else { ReplaceNode(live, node, no) } } continue patchloop } // lets use our unique id to check for the element if it exists sel := fmt.Sprintf(`%s[uid='%s']`, strings.ToLower(tagname), uid) // we know hash and uid are not empty so we kick ass the easy way targets := QuerySelectorAll(live, sel) // if we are nil then its a new node add it and return if len(targets) == 0 { ContextAppendChild(live, node) continue patchloop } for _, target := range targets { if onlyReplace { ReplaceNode(live, node, target) continue } //if we are to be removed then remove the target if HasAttribute(node, "NodeRemoved") { tgName := node.Get("tagName").String() // If its a tag in our header kdis, attempt to remove from head. if headerKids[tgName] { dom := js.Global.Get("document").Call("querySelector", "head") RemoveChild(dom, node) } // If its a script attempt to remove from head and body. if tgName == "script" { body := js.Global.Get("document").Call("querySelector", "body") head := js.Global.Get("document").Call("querySelector", "head") RemoveChild(head, node) RemoveChild(body, node) } // Lastly attempt to remove from target itself. RemoveChild(target, target) continue } // if the target hash is exactly the same with ours skip it if GetAttribute(target, "hash") == hash { continue } nchildren := ChildNodeList(node) //if the new node has no children, then just replace it. if len(nchildren) <= 0 { ReplaceNode(live, node, target) continue } //here we are not be removed and we do have kids //cleanout all the targets text-nodes CleanAllTextNode(target) //so we got this dude, are we already one level deep ? if so swap else // run through the children with Patch // if level >= 1 { // live.ReplaceChild(node, target) attrs := Attributes(node) for key, value := range attrs { SetAttribute(target, key, value) } children := ChildNodeList(target) if len(children) == 0 { SetInnerHTML(target, "") ContextAppendChild(target, nchildren...) // continue patchloop continue } Patch(node, target, onlyReplace) } } }
// Init intializes all attached resources under its giving resource list. func (rs *Resources) Init(useHashOnly ...bool) *Resources { var watchHash bool if len(useHashOnly) != 0 { watchHash = useHashOnly[0] } dispatch.Subscribe(func(pd dispatch.PathDirective) { if !watchHash { psx := dispatch.UseDirective(pd) if psx.String() == rs.lastPath.String() { return } rs.lastPath = psx if rs.renderer != nil { rs.renderer.Render(rs.Resolve(psx)...) } return } psx := dispatch.UseHashDirective(pd) if psx.String() == rs.lastPath.String() { return } rs.lastPath = psx if rs.renderer != nil { rs.renderer.Render(rs.Resolve(psx)...) } }) var dslList []DSL collection.cl.Lock() { dslList = collection.collection collection.root = rs collection.collection = nil } collection.cl.Unlock() for _, dsl := range dslList { res := newResource(rs, dsl) res.Init() } collection.cl.Lock() { collection.root = nil } collection.cl.Unlock() if detect.IsBrowser() && rs.renderer != nil { du := rs.Resolve(dispatch.GetLocationHashAsPath()) rs.renderer.Render(du...) } return rs }