Skip to content

Commit

Permalink
feat: implement classes
Browse files Browse the repository at this point in the history
  • Loading branch information
nytamin committed Jan 23, 2019
1 parent 80513ea commit 6d1b2e0
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 20 deletions.
3 changes: 3 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export interface ResolveOptions {
export interface ResolvedTimeline {
/** The options used to resolve the timeline */
options: ResolveOptions
/** Map of all objects on timeline */
objects: ResolvedTimelineObjects
/** Map of all classes on timeline, maps className to object ids */
classes: {[className: string]: Array<string>}
statistics: {
/** Number of objects that were unable to resolve */
unresolvedCount: number
Expand Down
134 changes: 131 additions & 3 deletions src/resolver/__tests__/resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ describe('resolver', () => {
options: {
time: 1000
},
objects: {
},
objects: {},
classes: {},
statistics: {
unresolvedCount: 0,
resolvedCount: 0,
Expand Down Expand Up @@ -116,7 +116,8 @@ describe('resolver', () => {
resolving: false
}
}
}
},
classes: {}
}
const obj: TimelineObject = {
id: 'obj0',
Expand Down Expand Up @@ -658,4 +659,131 @@ describe('resolver', () => {
}
})
})
test('classes', () => {
const timeline: TimelineObject[] = [
{
id: 'video0',
layer: '0',
trigger: {
start: 0,
end: 10,
repeating: 50
},
content: {},
classes: ['class0']
},
{
id: 'video1',
layer: '0',
trigger: {
start: '#video0.end + 15', // 25
duration: 10,
repeating: 50
},
content: {},
classes: ['class0', 'class1']
},
{
id: 'graphic0',
layer: '1',
trigger: {
while: '.class0'
},
content: {}
},
{
id: 'graphic1',
layer: '2',
trigger: {
while: '.class1 + 1'
},
content: {}
}
]

const resolved = Resolver.resolveTimeline(timeline, { time: 0, limitTime: 100 })

expect(resolved.statistics.resolvedObjectCount).toEqual(4)
expect(resolved.statistics.unresolvedCount).toEqual(0)

expect(resolved.objects['video0']).toBeTruthy()
expect(resolved.objects['video1']).toBeTruthy()
expect(resolved.objects['graphic0']).toBeTruthy()
expect(resolved.objects['graphic1']).toBeTruthy()

expect(resolved.objects['video0'].resolved).toMatchObject({
resolved: true,
instances: [
{ start: 0, end: 10 },
{ start: 50, end: 60 }
]
})
expect(resolved.objects['video1'].resolved).toMatchObject({
resolved: true,
instances: [
{ start: 25, end: 35 },
{ start: 75, end: 85 }
]
})
expect(resolved.objects['graphic0'].resolved).toMatchObject({
resolved: true,
instances: [
{ start: 0, end: 10 },
{ start: 25, end: 35 },
{ start: 50, end: 60 },
{ start: 75, end: 85 }
]
})
expect(resolved.objects['graphic1'].resolved).toMatchObject({
resolved: true,
instances: [
{ start: 26, end: 36 },
{ start: 76, end: 86 }
]
})

const state0 = Resolver.getState(resolved, 5)
expect(state0.layers['2']).toBeFalsy()
expect(state0.layers).toMatchObject({
'0': {
id: 'video0'
},
'1': {
id: 'graphic0'
}
})
const state1 = Resolver.getState(resolved, 25)
expect(state1.layers['2']).toBeFalsy()
expect(state1.layers).toMatchObject({
'0': {
id: 'video1'
},
'1': {
id: 'graphic0'
}
})
expect(Resolver.getState(resolved, 26).layers).toMatchObject({
'0': {
id: 'video1'
},
'1': {
id: 'graphic0'
},
'2': {
id: 'graphic1'
}
})

expect(Resolver.getState(resolved, 76).layers).toMatchObject({
'0': {
id: 'video1'
},
'1': {
id: 'graphic0'
},
'2': {
id: 'graphic1'
}
})
})
})
91 changes: 74 additions & 17 deletions src/resolver/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class Resolver {
const resolvedTimeline: ResolvedTimeline = {
options: _.clone(options),
objects: {},
classes: {},
statistics: {
unresolvedCount: 0,
resolvedCount: 0,
Expand All @@ -52,6 +53,15 @@ export class Resolver {
if (isKeyframe) o.resolved.isKeyframe = true
resolvedTimeline.objects[obj.id] = o

if (obj.classes) {
_.each(obj.classes, (className: string) => {
if (className) {
if (!resolvedTimeline.classes[className]) resolvedTimeline.classes[className] = []
resolvedTimeline.classes[className].push(obj.id)
}
})
}

if (obj.isGroup && obj.children) {
_.each(obj.children, (child) => {
addToResolvedTimeline(child, obj.id)
Expand Down Expand Up @@ -259,44 +269,85 @@ export function lookupExpression (
} else if (_.isNumber(expr)) {
return expr
} else if (_.isString(expr)) {
expr = expr.trim()
// Look up string
let invert: boolean = false
let ignoreFirstIfZero: boolean = false
const m = expr.match(/(!)?\W*#([^.]+)(.*)/)
const referencedObjs: ResolvedTimelineObject[] = []
let ref: ObjectRefType = context
let rest: string = ''

// Match id, example: "#objectId.start"
const m = expr.match(/^(!)?\W*#([^.]+)(.*)/)
if (m) {
const exclamation = m[1]
const id = m[2]
const rest = m[3]
rest = m[3]
if (exclamation === '!') invert = !invert

let ref: ObjectRefType = context
const obj = resolvedTimeline.objects[id]
if (obj) {
referencedObjs.push(obj)
}
} else {
// Match class, example: ".className.start"
const m = expr.match(/^(!)?\W*\.([^.]+)(.*)/)
if (m) {
const exclamation = m[1]
const className = m[2]
rest = m[3]
if (exclamation === '!') invert = !invert

const objIds: string[] = resolvedTimeline.classes[className] || []

_.each(objIds, (objId: string) => {
const obj = resolvedTimeline.objects[objId]
if (obj) {
referencedObjs.push(obj)
}
})
}
}

if (referencedObjs.length) {
if (rest.match(/start/)) ref = 'start'
if (rest.match(/end/)) ref = 'end'
if (rest.match(/duration/)) ref = 'duration'

const referencedObj = resolvedTimeline.objects[id]

if (referencedObj) {
resolveTimelineObj(resolvedTimeline, referencedObj)
if (referencedObj.resolved.resolved) {

if (ref === 'duration') {
// Duration refers to the first object on the resolved timeline
if (ref === 'duration') {
// Duration refers to the first object on the resolved timeline
const instanceDurations: Array<number> = []
_.each(referencedObjs, (referencedObj: ResolvedTimelineObject) => {
resolveTimelineObj(resolvedTimeline, referencedObj)
if (referencedObj.resolved.resolved) {
const firstInstance = _.first(referencedObj.resolved.instances)
return (
const duration: number | null = (
firstInstance && firstInstance.end !== null ?
firstInstance.end - firstInstance.start :
null
)
} else {
if (duration !== null) {
instanceDurations.push(duration)
}
}
})
let firstDuration: number | null = null
_.each(instanceDurations, (d) => {
if (firstDuration === null || d < firstDuration) firstDuration = d
})
return firstDuration
} else {
let returnInstances: TimelineObjectInstance[] = []
_.each(referencedObjs, (referencedObj: ResolvedTimelineObject) => {
resolveTimelineObj(resolvedTimeline, referencedObj)
if (referencedObj.resolved.resolved) {
let instances: Array<TimelineObjectInstance> = referencedObj.resolved.instances
if (ref === 'start') {
// nothing
} else if (ref === 'end') {
invert = !invert
ignoreFirstIfZero = true
} else throw Error(`Unknown ref: "${ref}"`)

if (invert) {
instances = invertInstances(
referencedObj.resolved.instances
Expand All @@ -308,14 +359,20 @@ export function lookupExpression (
instances.splice(0, 1)
}
}
return instances
returnInstances = returnInstances.concat(instances)
}
})
if (returnInstances.length) {
returnInstances.sort((a, b) => {
return a.start - b.start
})
return returnInstances
} else {
return null
}
} else {
return null
}
} else {
return null
}
} else {
if (expr) {
Expand Down

0 comments on commit 6d1b2e0

Please sign in to comment.