off(events, handler) {
        if (typeof events == 'string') {
            events = events.split(/[,\s]+/) // separate by comma or space
        } else {
            events = [events]
        }
        events.forEach(edata => {
            let name = typeof edata == 'string' ? edata : (edata.type + ':' + edata.execute + '.' + edata.scope)
            if (typeof edata == 'string') {
                let [eventName, scope] = edata.split('.')
                let [type, execute] = eventName.replace(':complete', ':after').replace(':done', ':after').split(':')
                edata = { type: type || '*', execute: execute || '', scope: scope || '' }
            }
            edata = w2utils.extend({ type: null, execute: null, onComplete: null }, edata)
            // errors
            if (!edata.type && !edata.scope) { console.log('ERROR: You must specify event type when calling .off() method of '+ this.name); return }
            if (!handler) { handler = null }
            let count = 0
            // remove listener
            this.listeners = this.listeners.filter(curr => {
                if ( (edata.type === '*' || edata.type === curr.edata.type)
                    && (edata.execute === '' || edata.execute === curr.edata.execute)
                    && (edata.scope === '' || edata.scope === curr.edata.scope)
                    && (edata.handler == null || edata.handler === curr.edata.handler)
                ) {
                    count++ // how many listeners removed
                    return false
                } else {
                    return true
                }
            })
            if (this.debug) {
                console.log(`w2base: remove event (${count})`, { name, edata, handler })
            }
        })
        return this // needed for chaining
    }
Removes event listener, supports event phase and event scoping

@param {*} edata - an object or string, if string "eventName:phase.scope"
@param {*} handler
@returns itself