<commit_before>
# init
alight.version = '{{{version}}}'
alight.debug =
    scan: 0
    directive: false
    watch: false
    watchText: false
    parser: false


alight.directivePreprocessor = (attrName, args) ->
    # html prefix data
    if attrName[0..4] is 'data-'
        name = attrName[5..]
    else
        name = attrName

    j = name.indexOf '-'
    if j < 0
        return { noNs: true }

    ns = name.substring 0, j
    name = name.substring(j+1).replace /(-\w)/g, (m) ->
        m.substring(1).toUpperCase()

    raw = null
    $ns = args.cd.scope.$ns
    if $ns and $ns.directives
        path = $ns.directives[ns]
        if path
            raw = path[name]
            if not raw
                if not $ns.inheritGlobal
                    return { noDirective: true }
        else
            if not $ns.inheritGlobal
                return { noNs: true }

    if not raw
        path = alight.d[ns]
        if not path
            return { noNs: true }

        raw = path[name]
        if not raw
            return { noDirective: true }

    dir = {}
    if f$.isFunction raw
        dir.init = raw
    else if f$.isObject raw
        for k, v of raw
            dir[k] = v
    else throw 'Wrong directive: ' + ns + '.' + name
    dir.priority = raw.priority or 0
    dir.restrict = raw.restrict or 'A'

    if dir.restrict.indexOf(args.attr_type) < 0
        throw 'Directive has wrong binding (attribute/element): ' + name

    dir.$init = (cd, element, value, env) ->

        doProcess = ->
            l = dscope.procLine
            for dp, i in l
                dp.fn.call dscope
                if dscope.isDeferred
                    dscope.procLine = l[i+1..]
                    break
            null

        dscope =
            element: element
            value: value
            cd: cd
            env: env
            ns: ns
            name: name
            args: args
            directive: dir

            isDeferred: false
            procLine: alight.hooks.directive
            makeDeferred: ->
                dscope.isDeferred = true
                dscope.env.stopBinding = true   # stop binding
                dscope.doBinding = true         # continue binding

                ->
                    dscope.isDeferred = false
                    doProcess()

        if dir.stopBinding
            dscope.env.stopBinding = true

        doProcess()
        return
    dir


do ->
    ext = alight.hooks.directive

    ext.push
        code: 'init'
        fn: ->
            if @.directive.init
                if alight.debug.directive
                    if @.directive.scope or @.directive.ChangeDetector
                        console.warn "#{@.ns}-#{@.name} uses scope and init together, probably you need use link instead of init"
                @.env.changeDetector = @.cd
                @.cd.scope.$changeDetector = @.cd
                result = @.directive.init @.cd.scope, @.element, @.value, @.env
                if result and result.start
                    result.start()
                @.cd.scope.$changeDetector = null
            return

    ext.push
        code: 'templateUrl'
        fn: ->
            ds = @
            if @.directive.templateUrl
                callback = @.makeDeferred()
                f$.ajax
                    cache: true
                    url: @.directive.templateUrl
                    success: (html) ->
                        ds.directive.template = html
                        callback()
                    error: callback
            return

    ext.push
        code: 'template'
        fn: ->
            if @.directive.template
                if @.element.nodeType is 1
                    @.element.innerHTML = @.directive.template
                else if @.element.nodeType is 8
                    el = document.createElement 'p'
                    el.innerHTML = @.directive.template.trim()
                    el = el.firstChild
                    f$.after @.element, el
                    @.element = el
                    @.doBinding = true
            return

    ext.push
        code: 'scope'
        fn: ->
            # scope: false, true, 'root'
            if not @.directive.scope
                return

            parentCD = @.cd
            scope = alight.Scope
                changeDetector: null

            switch @.directive.scope
                when true
                    childCD = parentCD.new scope
                when 'root'
                    childCD = alight.ChangeDetector scope

                    parentCD.watch '$destroy', ->
                        childCD.destroy()
                else
                    throw 'Wrong scope value: ' + @.directive.scope

            @.env.parentChangeDetector = parentCD
            scope.$parent = parentCD.scope
            scope.$rootChangeDetector = childCD
            @.cd = childCD

            @.env.stopBinding = true
            @.doBinding = true
            return

    ext.push
        code: 'link'
        fn: ->
            if @.directive.link
                @.env.changeDetector = @.cd
                @.cd.scope.$changeDetector = @.cd
                result = @.directive.link @.cd.scope, @.element, @.value, @.env
                if result and result.start
                    result.start()
                @.cd.scope.$changeDetector = null
            return

    ext.push
        code: 'scopeBinding'
        fn: ->
            if @.doBinding
                alight.bind @.cd, @.element,
                    skip_attr: @.env.skippedAttr()
            return


testDirective = do ->
    addAttr = (attrName, args, base) ->
        if args.attr_type is 'A'
            attr = base or {}
            attr.priority = -5
            attr.is_attr = true
            attr.name = attrName
            attr.attrName = attrName
            attr.element = args.element
            args.list.push attr

    (attrName, args) ->
        if args.skip_attr.indexOf(attrName) >= 0
            return addAttr attrName, args, { skip:true }

        directive = alight.directivePreprocessor attrName, args
        if directive.noNs
            return addAttr attrName, args
        if directive.noDirective
            return addAttr attrName, args, { noDirective:true }

        args.list.push
            name: attrName
            directive: directive
            priority: directive.priority
            attrName: attrName


sortByPriority = (a, b) ->
    if a.priority == b.priority
        return 0
    if a.priority > b.priority
        return -1
    else
        return 1


attrBinding = (cd, element, value, attrName) ->
    text = value
    if text.indexOf(alight.utils.pars_start_tag) < 0
        return

    cd.watchText text, null,
        element: element
        elementAttr: attrName
    true


bindText = (cd, element) ->
    text = element.data
    if text.indexOf(alight.utils.pars_start_tag) < 0
        return
    cd.watchText text, null,
        element: element
    true


bindComment = (cd, element, option) ->
    text = element.nodeValue.trim()
    if text[0..9] isnt 'directive:'
        return
    text = text[10..].trim()
    i = text.indexOf ' '
    if i >= 0
        dirName = text[0..i-1]
        value = text[i+1..]
    else
        dirName = text
        value = ''

    args =
        list: list = []
        element: element
        attr_type: 'M'
        cd: cd
        skip_attr: []

    testDirective dirName, args

    d = list[0]
    if d.noDirective
        throw "Directive not found: #{d.name}"

    directive = d.directive
    env =
        element: element
        attrName: dirName
        attributes: []
        skippedAttr: ->
            []
    if alight.debug.directive
        console.log 'bind', d.attrName, value, d
    try
        directive.$init cd, element, value, env
    catch e
        alight.exceptionHandler e, 'Error in directive: ' + d.name,
            value: value
            env: env
            cd: cd
            scope: cd.scope
            element: element
    if env.skipToElement
        return {<commit_msg>fix comment directive detection<commit_after>
# init
alight.version = '{{{version}}}'
alight.debug =
    scan: 0
    directive: false
    watch: false
    watchText: false
    parser: false


alight.directivePreprocessor = (attrName, args) ->
    # html prefix data
    if attrName[0..4] is 'data-'
        name = attrName[5..]
    else
        name = attrName

    j = name.indexOf '-'
    if j < 0
        return { noNs: true }

    ns = name.substring 0, j
    name = name.substring(j+1).replace /(-\w)/g, (m) ->
        m.substring(1).toUpperCase()

    raw = null
    $ns = args.cd.scope.$ns
    if $ns and $ns.directives
        path = $ns.directives[ns]
        if path
            raw = path[name]
            if not raw
                if not $ns.inheritGlobal
                    return { noDirective: true }
        else
            if not $ns.inheritGlobal
                return { noNs: true }

    if not raw
        path = alight.d[ns]
        if not path
            return { noNs: true }

        raw = path[name]
        if not raw
            return { noDirective: true }

    dir = {}
    if f$.isFunction raw
        dir.init = raw
    else if f$.isObject raw
        for k, v of raw
            dir[k] = v
    else throw 'Wrong directive: ' + ns + '.' + name
    dir.priority = raw.priority or 0
    dir.restrict = raw.restrict or 'A'

    if dir.restrict.indexOf(args.attr_type) < 0
        throw 'Directive has wrong binding (attribute/element): ' + name

    dir.$init = (cd, element, value, env) ->

        doProcess = ->
            l = dscope.procLine
            for dp, i in l
                dp.fn.call dscope
                if dscope.isDeferred
                    dscope.procLine = l[i+1..]
                    break
            null

        dscope =
            element: element
            value: value
            cd: cd
            env: env
            ns: ns
            name: name
            args: args
            directive: dir

            isDeferred: false
            procLine: alight.hooks.directive
            makeDeferred: ->
                dscope.isDeferred = true
                dscope.env.stopBinding = true   # stop binding
                dscope.doBinding = true         # continue binding

                ->
                    dscope.isDeferred = false
                    doProcess()

        if dir.stopBinding
            dscope.env.stopBinding = true

        doProcess()
        return
    dir


do ->
    ext = alight.hooks.directive

    ext.push
        code: 'init'
        fn: ->
            if @.directive.init
                if alight.debug.directive
                    if @.directive.scope or @.directive.ChangeDetector
                        console.warn "#{@.ns}-#{@.name} uses scope and init together, probably you need use link instead of init"
                @.env.changeDetector = @.cd
                @.cd.scope.$changeDetector = @.cd
                result = @.directive.init @.cd.scope, @.element, @.value, @.env
                if result and result.start
                    result.start()
                @.cd.scope.$changeDetector = null
            return

    ext.push
        code: 'templateUrl'
        fn: ->
            ds = @
            if @.directive.templateUrl
                callback = @.makeDeferred()
                f$.ajax
                    cache: true
                    url: @.directive.templateUrl
                    success: (html) ->
                        ds.directive.template = html
                        callback()
                    error: callback
            return

    ext.push
        code: 'template'
        fn: ->
            if @.directive.template
                if @.element.nodeType is 1
                    @.element.innerHTML = @.directive.template
                else if @.element.nodeType is 8
                    el = document.createElement 'p'
                    el.innerHTML = @.directive.template.trim()
                    el = el.firstChild
                    f$.after @.element, el
                    @.element = el
                    @.doBinding = true
            return

    ext.push
        code: 'scope'
        fn: ->
            # scope: false, true, 'root'
            if not @.directive.scope
                return

            parentCD = @.cd
            scope = alight.Scope
                changeDetector: null

            switch @.directive.scope
                when true
                    childCD = parentCD.new scope
                when 'root'
                    childCD = alight.ChangeDetector scope

                    parentCD.watch '$destroy', ->
                        childCD.destroy()
                else
                    throw 'Wrong scope value: ' + @.directive.scope

            @.env.parentChangeDetector = parentCD
            scope.$parent = parentCD.scope
            scope.$rootChangeDetector = childCD
            @.cd = childCD

            @.env.stopBinding = true
            @.doBinding = true
            return

    ext.push
        code: 'link'
        fn: ->
            if @.directive.link
                @.env.changeDetector = @.cd
                @.cd.scope.$changeDetector = @.cd
                result = @.directive.link @.cd.scope, @.element, @.value, @.env
                if result and result.start
                    result.start()
                @.cd.scope.$changeDetector = null
            return

    ext.push
        code: 'scopeBinding'
        fn: ->
            if @.doBinding
                alight.bind @.cd, @.element,
                    skip_attr: @.env.skippedAttr()
            return


testDirective = do ->
    addAttr = (attrName, args, base) ->
        if args.attr_type is 'A'
            attr = base or {}
            attr.priority = -5
            attr.is_attr = true
            attr.name = attrName
            attr.attrName = attrName
            attr.element = args.element
            args.list.push attr
        else if args.attr_type is 'M'
            args.list.push base
        return

    (attrName, args) ->
        if args.skip_attr.indexOf(attrName) >= 0
            return addAttr attrName, args, { skip:true }

        directive = alight.directivePreprocessor attrName, args
        if directive.noNs
            addAttr attrName, args
            return
        if directive.noDirective
            addAttr attrName, args, { noDirective:true }
            return

        args.list.push
            name: attrName
            directive: directive
            priority: directive.priority
            attrName: attrName
        return


sortByPriority = (a, b) ->
    if a.priority == b.priority
        return 0
    if a.priority > b.priority
        return -1
    else
        return 1


attrBinding = (cd, element, value, attrName) ->
    text = value
    if text.indexOf(alight.utils.pars_start_tag) < 0
        return

    cd.watchText text, null,
        element: element
        elementAttr: attrName
    true


bindText = (cd, element) ->
    text = element.data
    if text.indexOf(alight.utils.pars_start_tag) < 0
        return
    cd.watchText text, null,
        element: element
    true


bindComment = (cd, element, option) ->
    text = element.nodeValue.trim()
    if text[0..9] isnt 'directive:'
        return
    text = text[10..].trim()
    i = text.indexOf ' '
    if i >= 0
        dirName = text[0..i-1]
        value = text[i+1..]
    else
        dirName = text
        value = ''

    args =
        list: list = []
        element: element
        attr_type: 'M'
        cd: cd
        skip_attr: []

    testDirective dirName, args

    d = list[0]
    if d.noDirective
        throw "Comment directive not found: #{dirName}"

    directive = d.directive
    env =
        element: element
        attrName: dirName
        attributes: []
        skippedAttr: ->
            []
    if alight.debug.directive
        console.log 'bind', d.attrName, value, d
    try
        directive.$init cd, element, value, env
    catch e
        alight.exceptionHandler e, 'Error in directive: ' + d.name,
            value: value
            env: env
            cd: cd
            scope: cd.scope
            element: element
    if env.skipToElement
        return {
{'fraction_non_alphanumeric': 0.09316275938054723, 'fraction_numerical': 0.0035502234192324174, 'mean_word_length': 1.2325772068871277, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1956177', 'n_tokens_mistral': 4654, 'n_tokens_neox': 4419, 'n_words': 1197}