text:
- README.md: This file.
- CONTRIBUTING.md: Guidelines for how to contribute.
- aastex.cls: The AASTeX class file.
- ms.tex: Paper manuscript.
- natbib.sty: The natbib citation package (v7.1).

## ShareLaTeX

This paper is also hosted in ShareLaTeX, a cloud-based IDE:
[2014-06_Harrold_Imager](https://www.sharelatex.com/project/53a5d906776535bf352a5706?r=22874c80&rs=ps&rm=d)


text:
EkstepEditor.basePlugin.extend({
    newInstance: function() {
        var props = this.convertToFabric(this.attributes);
        delete this.configManifest;

        var circle = new fabric.Circle({
          radius: 100,
          fill: '#2185D0',
          scaleY: 0.5,
          originX: 'center',
          originY: 'center'
        });

        var text = new fabric.Text('Word worm', {
          fontSize: 30,
          originX: 'center',
          originY: 'center'
        });

        var group = new fabric.Group([ circle, text ], props);
        this.editorObj = group;
    },

    onConfigChange: function(key, value) {
        switch (key) {
            case 'wordText':
                this.attributes.wordText = value;
                break;
        }
        EkstepEditorAPI.render();
        EkstepEditorAPI.dispatchEvent('object:modified', { target: EkstepEditorAPI.getEditorObject() });
    },

    getConfig: function() {
        var config = this._super();
        config.wordText = this.attributes.wordText;
        return config;
    }
});
//# sourceURL=wordwormEditorPlugin.js

redpajama_set_name: RedPajamaGithub


text:
package com.googlecode.cqengine.index.support;

import java.util.Objects;

/**
 * Encapsulates a key-value pair.
 *
 * @author niall.gallagher
 */
public class KeyValueMaterialized<A, O> implements KeyValue<A, O> {

    final A key;
    final O value;

    public KeyValueMaterialized(A key, O value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public A getKey() {
        return key;
    }

    @Override
    public O getValue() {
        return value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        KeyValueMaterialized<?, ?> that = (KeyValueMaterialized<?, ?>) o;
        return key.equals(that.key) &&
                value.equals(that.value);
    }

    @Override
    public int hashCode() {
        return Objects.hash(key, value);
    }

    @Override
    public String toString() {
        return key + "=" + value;
    }
}


text:
from django import template

from oscar.core.utils import format_timedelta

register = template.Library()

@register.filter
def timedelta(td):
    """
    Return formatted timedelta value
    """
    return format_timedelta(td)


text:
---
title: "Joining"
weight: 3
type: docs
aliases:
  - /zh/dev/stream/operators/joining.html
---
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
-->

# Joining

## Window Join
Window join 作用在两个流中有相同 key 且处于相同窗口的元素上。这些窗口可以通过 [window assigner]({{< ref "docs/dev/datastream/operators/windows" >}}#window-assigners) 定义,并且两个流中的元素都会被用于计算窗口的结果。

两个流中的元素在组合之后,会被传递给用户定义的 `JoinFunction` 或 `FlatJoinFunction`,用户可以用它们输出符合 join 要求的结果。

常见的用例可以总结为以下代码:

```java
stream.join(otherStream)
    .where(<KeySelector>)
    .equalTo(<KeySelector>)
    .window(<WindowAssigner>)
    .apply(<JoinFunction>)
```

语义上有一些值得注意的地方:
- 从两个流中创建成对的元素与 inner-join 类似,即一个流中的元素在与另一个流中对应的元素完成 join 之前不会被输出。
- 完成 join 的元素会将他们的 timestamp 设为对应窗口中允许的最大 timestamp。比如一个边界为 `[5, 10)` 窗口中的元素在 join 之后的 timestamp 为 9。

接下来我们会用例子说明各种 window join 如何运作。

### 滚动 Window Join
使用滚动 window join 时,所有 key 相同且共享一个滚动窗口的元素会被组合成对,并传递给 `JoinFunction` 或 `FlatJoinFunction`。因为这个行为与 inner join 类似,所以一个流中的元素如果没有与另一个流中的元素组合起来,它就不会被输出!

{{< img src="/fig/tumbling-window-join.svg" width="80%" >}}

如图所示,我们定义了一个大小为 2 毫秒的滚动窗口,即形成了边界为 `[0,1], [2,3], ...` 的窗口。图中展示了如何将每个窗口中的元素组合成对,组合的结果将被传递给 `JoinFunction`。注意,滚动窗口 `[6,7]` 将不会输出任何数据,因为绿色流当中没有数据可以与橙色流的 ⑥ 和 ⑦ 配对。

{{< tabs "a8e08868-40d6-4719-b554-e2cabf2e1f6f" >}}
{{< tab "Java" >}}
```java
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;

...

DataStream<Integer> orangeStream = ...
DataStream<Integer> greenStream = ...

orangeStream.join(greenStream)
    .where(<KeySelector>)
    .equalTo(<KeySelector>)
    .window(TumblingEventTimeWindows.of(Time.milliseconds(2)))
    .apply (new JoinFunction<Integer, Integer, String> (){
        @Override
        public String join(Integer first, Integer second) {
            return first + "," + second;
        }
    });
```
{{< /tab >}}
{{< tab "Scala" >}}

```scala
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time

...

val orangeStream: DataStream[Integer] = ...
val greenStream: DataStream[Integer] = ...

orangeStream.join(greenStream)
    .where(elem => /* select key */)
    .equalTo(elem => /* select key */)
    .window(TumblingEventTimeWindows.of(Time.milliseconds(2)))
    .apply { (e1, e2) => e1 + "," + e2 }
```

{{< /tab >}}
{{< /tabs >}}

### 滑动 Window Join

当使用滑动 window join 时,所有 key 相同且处于同一个滑动窗口的元素将被组合成对,并传递给 `JoinFunction` 或 `FlatJoinFunction`。当前滑动窗口内,如果一个流中的元素没有与另一个流中的元素组合起来,它就不会被输出!注意,在某个滑动窗口中被 join 的元素不一定会在其他滑动窗口中被 join。

{{< img src="/fig/sliding-window-join.svg" width="80%" >}}

本例中我们定义了长度为两毫秒,滑动距离为一毫秒的滑动窗口,生成的窗口实例区间为 `[-1, 0],[0,1],[1,2],[2,3], …`。<!-- TODO: Can -1 actually exist?-->
X 轴下方是每个滑动窗口中被 join 后传递给 `JoinFunction` 的元素。图中可以看到橙色 ② 与绿色 ③ 在窗口 `[2,3]` 中 join,但没有与窗口 `[1,2]` 中任何元素 join。

{{< tabs "a3d3218b-dd25-4428-bfbb-d02522d95661" >}}
{{< tab "Java" >}}

```java
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;

...

DataStream<Integer> orangeStream = ...
DataStream<Integer> greenStream = ...

orangeStream.join(greenStream)
    .where(<KeySelector>)
    .equalTo(<KeySelector>)
    .window(SlidingEventTimeWindows.of(Time.milliseconds(2) /* size */, Time.milliseconds(1) /* slide */))
    .apply (new JoinFunction<Integer, Integer, String> (){
        @Override
        public String join(Integer first, Integer second) {
            return first + "," + second;
        }
    });
```
{{< /tab >}}
{{< tab "Scala" >}}

```scala
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time

...

val orangeStream: DataStream[Integer] = ...
val greenStream: DataStream[Integer] = ...

orangeStream.join(greenStream)
    .where(elem => /* select key */)
    .equalTo(elem => /* select key */)
    .window(SlidingEventTimeWindows.of(Time.milliseconds(2) /* size */, Time.milliseconds(1) /* slide */))
    .apply { (e1, e2) => e1 + "," + e2 }
```
{{< /tab >}}
{{< /tabs >}}

### 会话 Window Join

使用会话 window join 时,所有 key 相同且组合后符合会话要求的元素将被组合成对,并传递给 `JoinFunction` 或 `FlatJoinFunction`。这个操作同样是 inner join,所以如果一个会话窗口中只含有某一个流的元素,这个窗口将不会产生输出!

{{< img src="/fig/session-window-join.svg" width="80%" >}}

这里我们定义了一个间隔为至少一毫秒的会话窗口。图中总共有三个会话,前两者中两个流都有元素,它们被 join 并传递给 `JoinFunction`。而第三个会话中,绿流没有任何元素,所以 ⑧ 和 ⑨ 没有被 join!

{{< tabs "0e75f447-e1f7-4f38-b68c-de42ddd33512" >}}
{{< tab "Java" >}}

```java
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.streaming.api.windowing.assigners.EventTimeSessionWindows;
import org.apache.flink.streaming.api.windowing.time.Time;

...

DataStream<Integer> orangeStream = ...
DataStream<Integer> greenStream = ...

orangeStream.join(greenStream)
    .where(<KeySelector>)
    .equalTo(<KeySelector>)
    .window(EventTimeSessionWindows.withGap(Time.milliseconds(1)))
    .apply (new JoinFunction<Integer, Integer, String> (){
        @Override
        public String join(Integer first, Integer second) {
            return first + "," + second;
        }
    });
```
{{< /tab >}}
{{< tab "Scala" >}}

```scala
import org.apache.flink.streaming.api.windowing.assigners.EventTimeSessionWindows
import org.apache.flink.streaming.api.windowing.time.Time

...

val orangeStream: DataStream[Integer] = ...
val greenStream: DataStream[Integer] = ...

orangeStream.join(greenStream)
    .where(elem => /* select key */)
    .equalTo(elem => /* select key */)
    .window(EventTimeSessionWindows.withGap(Time.milliseconds(1)))
    .apply { (e1, e2) => e1 + "," + e2 }
```

{{< /tab >}}
{{< /tabs >}}

## Interval Join

Interval join 组合元素的条件为:两个流(我们暂时称为 A 和 B)中 key 相同且 B 中元素的 timestamp 处于 A 中元素 timestamp 的一定范围内。

这个条件可以更加正式地表示为
`b.timestamp ∈ [a.timestamp + lowerBound; a.timestamp + upperBound]` 或
`a.timestamp + lowerBound <= b.timestamp <= a.timestamp + upperBound`

这里的 a 和 b 为 A 和 B 中共享相同 key 的元素。上界和下界可正可负,只要下界永远小于等于上界即可。
Interval join 目前仅执行 inner join。

当一对元素被传递给 `ProcessJoinFunction`,他们的 timestamp 会从两个元素的 timestamp 中取最大值
(timestamp 可以通过 `ProcessJoinFunction.Context` 访问)。

{{< hint info >}}
Interval join 目前仅支持 event time。
{{< /hint >}}

{{< img src="/fig/interval-join.svg" width="80%" >}}

上例中,我们 join 了橙色和绿色两个流,join 的条件是:以 -2 毫秒为下界、+1 毫秒为上界。
默认情况下,上下界也被包括在区间内,但 `.lowerBoundExclusive()` 和 `.upperBoundExclusive()` 可以将它们排除在外。

图中三角形所表示的条件也可以写成更加正式的表达式:

`orangeElem.ts + lowerBound <= greenElem.ts <= orangeElem.ts + upperBound`

{{< tabs "63cebeb2-5869-4d2e-998d-d77fb466e2e6" >}}
{{< tab "Java" >}}

```java
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.streaming.api.functions.co.ProcessJoinFunction;
import org.apache.flink.streaming.api.windowing.time.Time;

...

DataStream<Integer> orangeStream = ...
DataStream<Integer> greenStream = ...

orangeStream
    .keyBy(<KeySelector>)
    .intervalJoin(greenStream.keyBy(<KeySelector>))
    .between(Time.milliseconds(-2), Time.milliseconds(1))
    .process (new ProcessJoinFunction<Integer, Integer, String(){

        @Override
        public void processElement(Integer left, Integer right, Context ctx, Collector<String> out) {
            out.collect(left + "," + right);
        }
    });
```

{{< /tab >}}
{{< tab "Scala" >}}

```scala
import org.apache.flink.streaming.api.functions.co.ProcessJoinFunction
import org.apache.flink.streaming.api.windowing.time.Time

...

val orangeStream: DataStream[Integer] = ...
val greenStream: DataStream[Integer] = ...

orangeStream
    .keyBy(elem => /* select key */)
    .intervalJoin(greenStream.keyBy(elem => /* select key */))
    .between(Time.milliseconds(-2), Time.milliseconds(1))
    .process(new ProcessJoinFunction[Integer, Integer, String] {
        override def processElement(left: Integer, right: Integer, ctx: ProcessJoinFunction[Integer, Integer, String]#Context, out: Collector[String]): Unit = {
            out.collect(left + "," + right)
        }
    })

```

{{< /tab >}}
{{< /tabs >}}

{{< top >}}


text:
#include "SkLua.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDocument.h"
#include "SkImage.h"
#include "SkMatrix.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkPixelRef.h"
#include "SkRRect.h"
#include "SkString.h"
#include "SkTypeface.h"

extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
}

// return the metatable name for a given class
template <typename T> const char* get_mtname();
#define DEF_MTNAME(T)                           \
    template <> const char* get_mtname<T>() {   \
        return #T "_LuaMetaTableName";          \
    }

DEF_MTNAME(SkCanvas)
DEF_MTNAME(SkDocument)
DEF_MTNAME(SkImage)
DEF_MTNAME(SkMatrix)
DEF_MTNAME(SkRRect)
DEF_MTNAME(SkPath)
DEF_MTNAME(SkPaint)
DEF_MTNAME(SkShader)
DEF_MTNAME(SkTypeface)

template <typename T> T* push_new(lua_State* L) {
    T* addr = (T*)lua_newuserdata(L, sizeof(T));
    new (addr) T;
    luaL_getmetatable(L, get_mtname<T>());
    lua_setmetatable(L, -2);
    return addr;
}

template <typename T> void push_obj(lua_State* L, const T& obj) {
    new (lua_newuserdata(L, sizeof(T))) T(obj);
    luaL_getmetatable(L, get_mtname<T>());
    lua_setmetatable(L, -2);
}

template <typename T> void push_ref(lua_State* L, T* ref) {
    *(T**)lua_newuserdata(L, sizeof(T*)) = SkRef(ref);
    luaL_getmetatable(L, get_mtname<T>());
    lua_setmetatable(L, -2);
}

template <typename T> T* get_ref(lua_State* L, int index) {
    return *(T**)luaL_checkudata(L, index, get_mtname<T>());
}

template <typename T> T* get_obj(lua_State* L, int index) {
    return (T*)luaL_checkudata(L, index, get_mtname<T>());
}

static bool lua2bool(lua_State* L, int index) {
    return !!lua_toboolean(L, index);
}

///////////////////////////////////////////////////////////////////////////////

SkLua::SkLua(const char termCode[]) : fTermCode(termCode), fWeOwnL(true) {
    fL = luaL_newstate();
    luaL_openlibs(fL);
    SkLua::Load(fL);
}

SkLua::SkLua(lua_State* L) : fL(L), fWeOwnL(false) {}

SkLua::~SkLua() {
    if (fWeOwnL) {
        if (fTermCode.size() > 0) {
            lua_getglobal(fL, fTermCode.c_str());
            if (lua_pcall(fL, 0, 0, 0) != LUA_OK) {
                SkDebugf("lua err: %s\n", lua_tostring(fL, -1));
            }
        }
        lua_close(fL);
    }
}

bool SkLua::runCode(const char code[]) {
    int err = luaL_loadstring(fL, code) || lua_pcall(fL, 0, 0, 0);
    if (err) {
        SkDebugf("--- lua failed: %s\n", lua_tostring(fL, -1));
        return false;
    }
    return true;
}

bool SkLua::runCode(const void* code, size_t size) {
    SkString str((const char*)code, size);
    return this->runCode(str.c_str());
}

///////////////////////////////////////////////////////////////////////////////

#define CHECK_SETFIELD(key) do if (key) lua_setfield(fL, -2, key); while (0)

static void setfield_bool_if(lua_State* L, const char key[], bool pred) {
    if (pred) {
        lua_pushboolean(L, true);
        lua_setfield(L, -2, key);
    }
}

static void setfield_string(lua_State* L, const char key[], const char value[]) {
    lua_pushstring(L, value);
    lua_setfield(L, -2, key);
}

static void setfield_number(lua_State* L, const char key[], double value) {
    lua_pushnumber(L, value);
    lua_setfield(L, -2, key);
}

static void setfield_boolean(lua_State* L, const char key[], bool value) {
    lua_pushboolean(L, value);
    lua_setfield(L, -2, key);
}

static void setfield_scalar(lua_State* L, const char key[], SkScalar value) {
    setfield_number(L, key, SkScalarToLua(value));
}

static void setfield_function(lua_State* L,
                              const char key[], lua_CFunction value) {
    lua_pushcfunction(L, value);
    lua_setfield(L, -2, key);
}

static void setarray_number(lua_State* L, int index, double value) {
    lua_pushnumber(L, value);
    lua_rawseti(L, -2, index);
}

void SkLua::pushBool(bool value, const char key[]) {
    lua_pushboolean(fL, value);
    CHECK_SETFIELD(key);
}

void SkLua::pushString(const char str[], const char key[]) {
    lua_pushstring(fL, str);
    CHECK_SETFIELD(key);
}

void SkLua::pushString(const char str[], size_t length, const char key[]) {
    // TODO: how to do this w/o making a copy?
    SkString s(str, length);
    lua_pushstring(fL, s.c_str());
    CHECK_SETFIELD(key);
}

void SkLua::pushString(const SkString& str, const char key[]) {
    lua_pushstring(fL, str.c_str());
    CHECK_SETFIELD(key);
}

void SkLua::pushColor(SkColor color, const char key[]) {
    lua_newtable(fL);
    setfield_number(fL, "a", SkColorGetA(color) / 255.0);
    setfield_number(fL, "r", SkColorGetR(color) / 255.0);
    setfield_number(fL, "g", SkColorGetG(color) / 255.0);
    setfield_number(fL, "b", SkColorGetB(color) / 255.0);
    CHECK_SETFIELD(key);
}

void SkLua::pushU32(uint32_t value, const char key[]) {
    lua_pushnumber(fL, (double)value);
    CHECK_SETFIELD(key);
}

void SkLua::pushScalar(SkScalar value, const char key[]) {
    lua_pushnumber(fL, SkScalarToLua(value));
    CHECK_SETFIELD(key);
}

void SkLua::pushArrayU16(const uint16_t array[], int count, const char key[]) {
    lua_newtable(fL);
    for (int i = 0; i < count; ++i) {
        // make it base-1 to match lua convention
        setarray_number(fL, i + 1, (double)array[i]);
    }
    CHECK_SETFIELD(key);
}

void SkLua::pushRect(const SkRect& r, const char key[]) {
    lua_newtable(fL);
    setfield_scalar(fL, "left", r.fLeft);
    setfield_scalar(fL, "top", r.fTop);
    setfield_scalar(fL, "right", r.fRight);
    setfield_scalar(fL, "bottom", r.fBottom);
    CHECK_SETFIELD(key);
}

void SkLua::pushRRect(const SkRRect& rr, const char key[]) {
    push_obj(fL, rr);
    CHECK_SETFIELD(key);
}

void SkLua::pushMatrix(const SkMatrix& matrix, const char key[]) {
    push_obj(fL, matrix);
    CHECK_SETFIELD(key);
}

void SkLua::pushPaint(const SkPaint& paint, const char key[]) {
    push_obj(fL, paint);
    CHECK_SETFIELD(key);
}

void SkLua::pushPath(const SkPath& path, const char key[]) {
    push_obj(fL, path);
    CHECK_SETFIELD(key);
}

void SkLua::pushCanvas(SkCanvas* canvas, const char key[]) {
    push_ref(fL, canvas);
    CHECK_SETFIELD(key);
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

static SkScalar lua2scalar(lua_State* L, int index) {
    SkASSERT(lua_isnumber(L, index));
    return SkLuaToScalar(lua_tonumber(L, index));
}

static SkScalar lua2scalar_def(lua_State* L, int index, SkScalar defaultValue) {
    if (lua_isnumber(L, index)) {
        return SkLuaToScalar(lua_tonumber(L, index));
    } else {
        return defaultValue;
    }
}

static SkScalar getfield_scalar(lua_State* L, int index, const char key[]) {
    SkASSERT(lua_istable(L, index));
    lua_pushstring(L, key);
    lua_gettable(L, index);

    SkScalar value = lua2scalar(L, -1);
    lua_pop(L, 1);
    return value;
}

static SkScalar getfield_scalar_default(lua_State* L, int index, const char key[], SkScalar def) {
    SkASSERT(lua_istable(L, index));
    lua_pushstring(L, key);
    lua_gettable(L, index);

    SkScalar value;
    if (lua_isnil(L, -1)) {
        value = def;
    } else {
        value = lua2scalar(L, -1);
    }
    lua_pop(L, 1);
    return value;
}

static U8CPU unit2byte(SkScalar x) {
    if (x <= 0) {
        return 0;
    } else if (x >= 1) {
        return 255;
    } else {
        return SkScalarRoundToInt(x * 255);
    }
}

static SkColor lua2color(lua_State* L, int index) {
    return SkColorSetARGB(unit2byte(getfield_scalar(L, index, "a")),
                          unit2byte(getfield_scalar(L, index, "r")),
                          unit2byte(getfield_scalar(L, index, "g")),
                          unit2byte(getfield_scalar(L, index, "b")));
}

static SkRect* lua2rect(lua_State* L, int index, SkRect* rect) {
    rect->set(getfield_scalar_default(L, index, "left", 0),
              getfield_scalar_default(L, index, "top", 0),
              getfield_scalar(L, index, "right"),
              getfield_scalar(L, index, "bottom"));
    return rect;
}

static int lcanvas_drawColor(lua_State* L) {
    get_ref<SkCanvas>(L, 1)->drawColor(lua2color(L, 2));
    return 0;
}

static int lcanvas_drawRect(lua_State* L) {
    SkRect rect;
    get_ref<SkCanvas>(L, 1)->drawRect(*lua2rect(L, 2, &rect),
                                      *get_obj<SkPaint>(L, 3));
    return 0;
}

static int lcanvas_drawOval(lua_State* L) {
    SkRect rect;
    get_ref<SkCanvas>(L, 1)->drawOval(*lua2rect(L, 2, &rect),
                                      *get_obj<SkPaint>(L, 3));
    return 0;
}

static int lcanvas_drawCircle(lua_State* L) {
    get_ref<SkCanvas>(L, 1)->drawCircle(lua2scalar(L, 2),
                                        lua2scalar(L, 3),
                                        lua2scalar(L, 4),
                                        *get_obj<SkPaint>(L, 5));
    return 0;
}

static int lcanvas_drawImage(lua_State* L) {
    SkCanvas* canvas = get_ref<SkCanvas>(L, 1);
    SkImage* image = get_ref<SkImage>(L, 2);
    if (NULL == image) {
        return 0;
    }
    SkScalar x = lua2scalar(L, 3);
    SkScalar y = lua2scalar(L, 4);

    SkPaint paint;
    const SkPaint* paintPtr = NULL;
    if (lua_isnumber(L, 5)) {
        paint.setAlpha(SkScalarRoundToInt(lua2scalar(L, 5) * 255));
        paintPtr = &paint;
    }
    image->draw(canvas, x, y, paintPtr);
    return 0;
}

static int lcanvas_drawPath(lua_State* L) {
    get_ref<SkCanvas>(L, 1)->drawPath(*get_obj<SkPath>(L, 2),
                                      *get_obj<SkPaint>(L, 3));
    return 0;
}

static int lcanvas_drawText(lua_State* L) {
    if (lua_gettop(L) < 5) {
        return 0;
    }

    if (lua_isstring(L, 2) && lua_isnumber(L, 3) && lua_isnumber(L, 4)) {
        size_t len;
        const char* text = lua_tolstring(L, 2, &len);
        get_ref<SkCanvas>(L, 1)->drawText(text, len,
                                          lua2scalar(L, 3), lua2scalar(L, 4),
                                          *get_obj<SkPaint>(L, 5));
    }
    return 0;
}

static int lcanvas_getSaveCount(lua_State* L) {
    lua_pushnumber(L, get_ref<SkCanvas>(L, 1)->getSaveCount());
    return 1;
}

static int lcanvas_getTotalMatrix(lua_State* L) {
    SkLua(L).pushMatrix(get_ref<SkCanvas>(L, 1)->getTotalMatrix());
    return 1;
}

static int lcanvas_save(lua_State* L) {
    lua_pushinteger(L, get_ref<SkCanvas>(L, 1)->save());
    return 1;
}

static int lcanvas_restore(lua_State* L) {
    get_ref<SkCanvas>(L, 1)->restore();
    return 0;
}

static int lcanvas_scale(lua_State* L) {
    SkScalar sx = lua2scalar_def(L, 2, 1);
    SkScalar sy = lua2scalar_def(L, 3, sx);
    get_ref<SkCanvas>(L, 1)->scale(sx, sy);
    return 0;
}

static int lcanvas_translate(lua_State* L) {
    SkScalar tx = lua2scalar_def(L, 2, 0);
    SkScalar ty = lua2scalar_def(L, 3, 0);
    get_ref<SkCanvas>(L, 1)->translate(tx, ty);
    return 0;
}

static int lcanvas_rotate(lua_State* L) {
    SkScalar degrees = lua2scalar_def(L, 2, 0);
    get_ref<SkCanvas>(L, 1)->rotate(degrees);
    return 0;
}

static int lcanvas_gc(lua_State* L) {
    get_ref<SkCanvas>(L, 1)->unref();
    return 0;
}

static const struct luaL_Reg gSkCanvas_Methods[] = {
    { "drawColor", lcanvas_drawColor },
    { "drawRect", lcanvas_drawRect },
    { "drawOval", lcanvas_drawOval },
    { "drawCircle", lcanvas_drawCircle },
    { "drawImage", lcanvas_drawImage },
    { "drawPath", lcanvas_drawPath },
    { "drawText", lcanvas_drawText },
    { "getSaveCount", lcanvas_getSaveCount },
    { "getTotalMatrix", lcanvas_getTotalMatrix },
    { "save", lcanvas_save },
    { "restore", lcanvas_restore },
    { "scale", lcanvas_scale },
    { "translate", lcanvas_translate },
    { "rotate", lcanvas_rotate },
    { "__gc", lcanvas_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static int ldocument_beginPage(lua_State* L) {
    const SkRect* contentPtr = NULL;
    push_ref(L, get_ref<SkDocument>(L, 1)->beginPage(lua2scalar(L, 2),
                                                     lua2scalar(L, 3),
                                                     contentPtr));
    return 1;
}

static int ldocument_endPage(lua_State* L) {
    get_ref<SkDocument>(L, 1)->endPage();
    return 0;
}

static int ldocument_close(lua_State* L) {
    get_ref<SkDocument>(L, 1)->close();
    return 0;
}

static int ldocument_gc(lua_State* L) {
    get_ref<SkDocument>(L, 1)->unref();
    return 0;
}

static const struct luaL_Reg gSkDocument_Methods[] = {
    { "beginPage", ldocument_beginPage },
    { "endPage", ldocument_endPage },
    { "close", ldocument_close },
    { "__gc", ldocument_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static int lpaint_isAntiAlias(lua_State* L) {
    lua_pushboolean(L, get_obj<SkPaint>(L, 1)->isAntiAlias());
    return 1;
}

static int lpaint_setAntiAlias(lua_State* L) {
    get_obj<SkPaint>(L, 1)->setAntiAlias(lua2bool(L, 2));
    return 0;
}

static int lpaint_getColor(lua_State* L) {
    SkLua(L).pushColor(get_obj<SkPaint>(L, 1)->getColor());
    return 1;
}

static int lpaint_setColor(lua_State* L) {
    get_obj<SkPaint>(L, 1)->setColor(lua2color(L, 2));
    return 0;
}

static int lpaint_getTextSize(lua_State* L) {
    SkLua(L).pushScalar(get_obj<SkPaint>(L, 1)->getTextSize());
    return 1;
}

static int lpaint_setTextSize(lua_State* L) {
    get_obj<SkPaint>(L, 1)->setTextSize(lua2scalar(L, 2));
    return 0;
}

static int lpaint_getTypeface(lua_State* L) {
    push_ref(L, get_obj<SkPaint>(L, 1)->getTypeface());
    return 1;
}

static int lpaint_setTypeface(lua_State* L) {
    get_obj<SkPaint>(L, 1)->setTypeface(get_ref<SkTypeface>(L, 2));
    return 0;
}

static int lpaint_getFontID(lua_State* L) {
    SkTypeface* face = get_obj<SkPaint>(L, 1)->getTypeface();
    SkLua(L).pushU32(SkTypeface::UniqueID(face));
    return 1;
}

static const struct {
    const char*     fLabel;
    SkPaint::Align  fAlign;
} gAlignRec[] = {
    { "left",   SkPaint::kLeft_Align },
    { "center", SkPaint::kCenter_Align },
    { "right",  SkPaint::kRight_Align },
};

static int lpaint_getTextAlign(lua_State* L) {
    SkPaint::Align align = get_obj<SkPaint>(L, 1)->getTextAlign();
    for (size_t i = 0; i < SK_ARRAY_COUNT(gAlignRec); ++i) {
        if (gAlignRec[i].fAlign == align) {
            lua_pushstring(L, gAlignRec[i].fLabel);
            return 1;
        }
    }
    return 0;
}

static int lpaint_setTextAlign(lua_State* L) {
    if (lua_isstring(L, 2)) {
        size_t len;
        const char* label = lua_tolstring(L, 2, &len);

        for (size_t i = 0; i < SK_ARRAY_COUNT(gAlignRec); ++i) {
            if (!strcmp(gAlignRec[i].fLabel, label)) {
                get_obj<SkPaint>(L, 1)->setTextAlign(gAlignRec[i].fAlign);
                break;
            }
        }
    }
    return 0;
}

static int lpaint_getStroke(lua_State* L) {
    lua_pushboolean(L, SkPaint::kStroke_Style == get_obj<SkPaint>(L, 1)->getStyle());
    return 1;
}

static int lpaint_setStroke(lua_State* L) {
    SkPaint::Style style;

    if (lua_toboolean(L, 2)) {
        style = SkPaint::kStroke_Style;
    } else {
        style = SkPaint::kFill_Style;
    }
    get_obj<SkPaint>(L, 1)->setStyle(style);
    return 0;
}

static int lpaint_getStrokeWidth(lua_State* L) {
    SkLua(L).pushScalar(get_obj<SkPaint>(L, 1)->getStrokeWidth());
    return 1;
}

static int lpaint_setStrokeWidth(lua_State* L) {
    get_obj<SkPaint>(L, 1)->setStrokeWidth(lua2scalar(L, 2));
    return 0;
}

static int lpaint_measureText(lua_State* L) {
    if (lua_isstring(L, 2)) {
        size_t len;
        const char* text = lua_tolstring(L, 2, &len);
        SkLua(L).pushScalar(get_obj<SkPaint>(L, 1)->measureText(text, len));
        return 1;
    }
    return 0;
}

struct FontMetrics {
    SkScalar    fTop;       //!< The greatest distance above the baseline for any glyph (will be <= 0)
    SkScalar    fAscent;    //!< The recommended distance above the baseline (will be <= 0)
    SkScalar    fDescent;   //!< The recommended distance below the baseline (will be >= 0)
    SkScalar    fBottom;    //!< The greatest distance below the baseline for any glyph (will be >= 0)
    SkScalar    fLeading;   //!< The recommended distance to add between lines of text (will be >= 0)
    SkScalar    fAvgCharWidth;  //!< the average charactor width (>= 0)
    SkScalar    fXMin;      //!< The minimum bounding box x value for all glyphs
    SkScalar    fXMax;      //!< The maximum bounding box x value for all glyphs
    SkScalar    fXHeight;   //!< the height of an 'x' in px, or 0 if no 'x' in face
};

static int lpaint_getFontMetrics(lua_State* L) {
    SkPaint::FontMetrics fm;
    SkScalar height = get_obj<SkPaint>(L, 1)->getFontMetrics(&fm);

    lua_newtable(L);
    setfield_scalar(L, "top", fm.fTop);
    setfield_scalar(L, "ascent", fm.fAscent);
    setfield_scalar(L, "descent", fm.fDescent);
    setfield_scalar(L, "bottom", fm.fBottom);
    setfield_scalar(L, "leading", fm.fLeading);
    SkLua(L).pushScalar(height);
    return 2;
}

static int lpaint_getEffects(lua_State* L) {
    const SkPaint* paint = get_obj<SkPaint>(L, 1);

    lua_newtable(L);
    setfield_bool_if(L, "looper", !!paint->getLooper());
    setfield_bool_if(L, "pathEffect", !!paint->getPathEffect());
    setfield_bool_if(L, "rasterizer", !!paint->getRasterizer());
    setfield_bool_if(L, "maskFilter", !!paint->getMaskFilter());
    setfield_bool_if(L, "shader", !!paint->getShader());
    setfield_bool_if(L, "colorFilter", !!paint->getColorFilter());
    setfield_bool_if(L, "imageFilter", !!paint->getImageFilter());
    setfield_bool_if(L, "xfermode", !!paint->getXfermode());
    return 1;
}

static int lpaint_getShader(lua_State* L) {
    const SkPaint* paint = get_obj<SkPaint>(L, 1);
    SkShader* shader = paint->getShader();
    if (shader) {
        push_ref(L, shader);
        return 1;
    }
    return 0;
}

static int lpaint_gc(lua_State* L) {
    get_obj<SkPaint>(L, 1)->~SkPaint();
    return 0;
}

static const struct luaL_Reg gSkPaint_Methods[] = {
    { "isAntiAlias", lpaint_isAntiAlias },
    { "setAntiAlias", lpaint_setAntiAlias },
    { "getColor", lpaint_getColor },
    { "setColor", lpaint_setColor },
    { "getTextSize", lpaint_getTextSize },
    { "setTextSize", lpaint_setTextSize },
    { "getTypeface", lpaint_getTypeface },
    { "setTypeface", lpaint_setTypeface },
    { "getFontID", lpaint_getFontID },
    { "getTextAlign", lpaint_getTextAlign },
    { "setTextAlign", lpaint_setTextAlign },
    { "getStroke", lpaint_getStroke },
    { "setStroke", lpaint_setStroke },
    { "getStrokeWidth", lpaint_getStrokeWidth },
    { "setStrokeWidth", lpaint_setStrokeWidth },
    { "measureText", lpaint_measureText },
    { "getFontMetrics", lpaint_getFontMetrics },
    { "getEffects", lpaint_getEffects },
    { "getShader", lpaint_getShader },
    { "__gc", lpaint_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static const char* mode2string(SkShader::TileMode mode) {
    static const char* gNames[] = { "clamp", "repeat", "mirror" };
    SkASSERT((unsigned)mode < SK_ARRAY_COUNT(gNames));
    return gNames[mode];
}

static const char* gradtype2string(SkShader::GradientType t) {
    static const char* gNames[] = {
        "none", "color", "linear", "radial", "radial2", "sweep", "conical"
    };
    SkASSERT((unsigned)t < SK_ARRAY_COUNT(gNames));
    return gNames[t];
}

static int lshader_isOpaque(lua_State* L) {
    SkShader* shader = get_ref<SkShader>(L, 1);
    return shader && shader->isOpaque();
}

static int lshader_asABitmap(lua_State* L) {
    SkShader* shader = get_ref<SkShader>(L, 1);
    if (shader) {
        SkBitmap bm;
        SkMatrix matrix;
        SkShader::TileMode modes[2];
        switch (shader->asABitmap(&bm, &matrix, modes)) {
            case SkShader::kDefault_BitmapType:
                lua_newtable(L);
                setfield_number(L, "genID", bm.pixelRef() ? bm.pixelRef()->getGenerationID() : 0);
                setfield_number(L, "width", bm.width());
                setfield_number(L, "height", bm.height());
                setfield_string(L, "tileX", mode2string(modes[0]));
                setfield_string(L, "tileY", mode2string(modes[1]));
                return 1;
            default:
                break;
        }
    }
    return 0;
}

static int lshader_asAGradient(lua_State* L) {
    SkShader* shader = get_ref<SkShader>(L, 1);
    if (shader) {
        SkShader::GradientInfo info;
        sk_bzero(&info, sizeof(info));

        SkColor colors[3];  // hacked in for extracting info on 3 color case.
        SkScalar pos[3];

        info.fColorCount = 3;
        info.fColors = &colors[0];
        info.fColorOffsets = &pos[0];

        SkShader::GradientType t = shader->asAGradient(&info);

        if (SkShader::kNone_GradientType != t) {
            lua_newtable(L);
            setfield_string(L, "type", gradtype2string(t));
            setfield_number(L, "colorCount", info.fColorCount);
            setfield_string(L, "tile", mode2string(info.fTileMode));

            if (info.fColorCount == 3){
                setfield_number(L, "midPos", pos[1]);
            }

            return 1;
        }
    }
    return 0;
}

static int lshader_gc(lua_State* L) {
    get_ref<SkShader>(L, 1)->unref();
    return 0;
}

static const struct luaL_Reg gSkShader_Methods[] = {
    { "isOpaque",       lshader_isOpaque },
    { "asABitmap",      lshader_asABitmap },
    { "asAGradient",    lshader_asAGradient },
    { "__gc",           lshader_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static int lmatrix_getType(lua_State* L) {
    SkMatrix::TypeMask mask = get_obj<SkMatrix>(L, 1)->getType();

    lua_newtable(L);
    setfield_boolean(L, "translate",   SkToBool(mask & SkMatrix::kTranslate_Mask));
    setfield_boolean(L, "scale",       SkToBool(mask & SkMatrix::kScale_Mask));
    setfield_boolean(L, "affine",      SkToBool(mask & SkMatrix::kAffine_Mask));
    setfield_boolean(L, "perspective", SkToBool(mask & SkMatrix::kPerspective_Mask));
    return 1;
}

static int lmatrix_getScaleX(lua_State* L) {
    lua_pushnumber(L, get_obj<SkMatrix>(L,1)->getScaleX());
    return 1;
}

static int lmatrix_getScaleY(lua_State* L) {
    lua_pushnumber(L, get_obj<SkMatrix>(L,1)->getScaleY());
    return 1;
}

static int lmatrix_getTranslateX(lua_State* L) {
    lua_pushnumber(L, get_obj<SkMatrix>(L,1)->getTranslateX());
    return 1;
}

static int lmatrix_getTranslateY(lua_State* L) {
    lua_pushnumber(L, get_obj<SkMatrix>(L,1)->getTranslateY());
    return 1;
}

static const struct luaL_Reg gSkMatrix_Methods[] = {
    { "getType", lmatrix_getType },
    { "getScaleX", lmatrix_getScaleX },
    { "getScaleY", lmatrix_getScaleY },
    { "getTranslateX", lmatrix_getTranslateX },
    { "getTranslateY", lmatrix_getTranslateY },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static int lpath_getBounds(lua_State* L) {
    SkLua(L).pushRect(get_obj<SkPath>(L, 1)->getBounds());
    return 1;
}

static int lpath_isEmpty(lua_State* L) {
    lua_pushboolean(L, get_obj<SkPath>(L, 1)->isEmpty());
    return 1;
}

static int lpath_isRect(lua_State* L) {
    SkRect r;
    bool pred = get_obj<SkPath>(L, 1)->isRect(&r);
    int ret_count = 1;
    lua_pushboolean(L, pred);
    if (pred) {
        SkLua(L).pushRect(r);
        ret_count += 1;
    }
    return ret_count;
}

static const char* dir2string(SkPath::Direction dir) {
    static const char* gStr[] = {
        "unknown", "cw", "ccw"
    };
    SkASSERT((unsigned)dir < SK_ARRAY_COUNT(gStr));
    return gStr[dir];
}

static int lpath_isNestedRects(lua_State* L) {
    SkRect rects[2];
    SkPath::Direction dirs[2];
    bool pred = get_obj<SkPath>(L, 1)->isNestedRects(rects, dirs);
    int ret_count = 1;
    lua_pushboolean(L, pred);
    if (pred) {
        SkLua lua(L);
        lua.pushRect(rects[0]);
        lua.pushRect(rects[1]);
        lua_pushstring(L, dir2string(dirs[0]));
        lua_pushstring(L, dir2string(dirs[0]));
        ret_count += 4;
    }
    return ret_count;
}

static int lpath_reset(lua_State* L) {
    get_obj<SkPath>(L, 1)->reset();
    return 0;
}

static int lpath_moveTo(lua_State* L) {
    get_obj<SkPath>(L, 1)->moveTo(lua2scalar(L, 2), lua2scalar(L, 3));
    return 0;
}

static int lpath_lineTo(lua_State* L) {
    get_obj<SkPath>(L, 1)->lineTo(lua2scalar(L, 2), lua2scalar(L, 3));
    return 0;
}

static int lpath_quadTo(lua_State* L) {
    get_obj<SkPath>(L, 1)->quadTo(lua2scalar(L, 2), lua2scalar(L, 3),
                                  lua2scalar(L, 4), lua2scalar(L, 5));
    return 0;
}

static int lpath_cubicTo(lua_State* L) {
    get_obj<SkPath>(L, 1)->cubicTo(lua2scalar(L, 2), lua2scalar(L, 3),
                                   lua2scalar(L, 4), lua2scalar(L, 5),
                                   lua2scalar(L, 6), lua2scalar(L, 7));
    return 0;
}

static int lpath_close(lua_State* L) {
    get_obj<SkPath>(L, 1)->close();
    return 0;
}

static int lpath_gc(lua_State* L) {
    get_obj<SkPath>(L, 1)->~SkPath();
    return 0;
}

static const struct luaL_Reg gSkPath_Methods[] = {
    { "getBounds", lpath_getBounds },
    { "isEmpty", lpath_isEmpty },
    { "isRect", lpath_isRect },
    { "isNestedRects", lpath_isNestedRects },
    { "reset", lpath_reset },
    { "moveTo", lpath_moveTo },
    { "lineTo", lpath_lineTo },
    { "quadTo", lpath_quadTo },
    { "cubicTo", lpath_cubicTo },
    { "close", lpath_close },
    { "__gc", lpath_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static const char* rrect_type(const SkRRect& rr) {
    switch (rr.getType()) {
        case SkRRect::kUnknown_Type: return "unknown";
        case SkRRect::kEmpty_Type: return "empty";
        case SkRRect::kRect_Type: return "rect";
        case SkRRect::kOval_Type: return "oval";
        case SkRRect::kSimple_Type: return "simple";
        case SkRRect::kComplex_Type: return "complex";
    }
    SkDEBUGFAIL("never get here");
    return "";
}

static int lrrect_rect(lua_State* L) {
    SkLua(L).pushRect(get_obj<SkRRect>(L, 1)->rect());
    return 1;
}

static int lrrect_type(lua_State* L) {
    lua_pushstring(L, rrect_type(*get_obj<SkRRect>(L, 1)));
    return 1;
}

static int lrrect_radii(lua_State* L) {
    int corner = lua_tointeger(L, 2);
    SkVector v;
    if (corner < 0 || corner > 3) {
        SkDebugf("bad corner index %d", corner);
        v.set(0, 0);
    } else {
        v = get_obj<SkRRect>(L, 1)->radii((SkRRect::Corner)corner);
    }
    lua_pushnumber(L, v.fX);
    lua_pushnumber(L, v.fY);
    return 2;
}

static int lrrect_gc(lua_State* L) {
    get_obj<SkRRect>(L, 1)->~SkRRect();
    return 0;
}

static const struct luaL_Reg gSkRRect_Methods[] = {
    { "rect", lrrect_rect },
    { "type", lrrect_type },
    { "radii", lrrect_radii },
    { "__gc", lrrect_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static int limage_width(lua_State* L) {
    lua_pushinteger(L, get_ref<SkImage>(L, 1)->width());
    return 1;
}

static int limage_height(lua_State* L) {
    lua_pushinteger(L, get_ref<SkImage>(L, 1)->height());
    return 1;
}

static int limage_gc(lua_State* L) {
    get_ref<SkImage>(L, 1)->unref();
    return 0;
}

static const struct luaL_Reg gSkImage_Methods[] = {
    { "width", limage_width },
    { "height", limage_height },
    { "__gc", limage_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

static int ltypeface_gc(lua_State* L) {
    get_ref<SkTypeface>(L, 1)->unref();
    return 0;
}

static const struct luaL_Reg gSkTypeface_Methods[] = {
    { "__gc", ltypeface_gc },
    { NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////////

class AutoCallLua {
public:
    AutoCallLua(lua_State* L, const char func[], const char verb[]) : fL(L) {
        lua_getglobal(L, func);
        if (!lua_isfunction(L, -1)) {
            int t = lua_type(L, -1);
            SkDebugf("--- expected function %d\n", t);
        }

        lua_newtable(L);
        setfield_string(L, "verb", verb);
    }

    ~AutoCallLua() {
        if (lua_pcall(fL, 1, 0, 0) != LUA_OK) {
            SkDebugf("lua err: %s\n", lua_tostring(fL, -1));
        }
        lua_settop(fL, -1);
    }

private:
    lua_State* fL;
};

#define AUTO_LUA(verb)  AutoCallLua acl(fL, fFunc.c_str(), verb)

///////////////////////////////////////////////////////////////////////////////

static int lsk_newDocumentPDF(lua_State* L) {
    const char* file = NULL;
    if (lua_gettop(L) > 0 && lua_isstring(L, 1)) {
        file = lua_tolstring(L, 1, NULL);
    }

    SkDocument* doc = SkDocument::CreatePDF(file);
    if (NULL == doc) {
        // do I need to push a nil on the stack and return 1?
        return 0;
    } else {
        push_ref(L, doc);
        doc->unref();
        return 1;
    }
}

static int lsk_newPaint(lua_State* L) {
    push_new<SkPaint>(L);
    return 1;
}

static int lsk_newPath(lua_State* L) {
    push_new<SkPath>(L);
    return 1;
}

static int lsk_newRRect(lua_State* L) {
    SkRRect* rr = push_new<SkRRect>(L);
    rr->setEmpty();
    return 1;
}

static int lsk_newTypeface(lua_State* L) {
    const char* name = NULL;
    int style = SkTypeface::kNormal;

    int count = lua_gettop(L);
    if (count > 0 && lua_isstring(L, 1)) {
        name = lua_tolstring(L, 1, NULL);
        if (count > 1 && lua_isnumber(L, 2)) {
            style = lua_tointegerx(L, 2, NULL) & SkTypeface::kBoldItalic;
        }
    }

    SkTypeface* face = SkTypeface::CreateFromName(name,
                                                  (SkTypeface::Style)style);
//    SkDebugf("---- name <%s> style=%d, face=%p ref=%d\n", name, style, face, face->getRefCnt());
    if (NULL == face) {
        face = SkTypeface::RefDefault();
    }
    push_ref(L, face);
    face->unref();
    return 1;
}

static int lsk_loadImage(lua_State* L) {
    if (lua_gettop(L) > 0 && lua_isstring(L, 1)) {
        const char* name = lua_tolstring(L, 1, NULL);
        SkAutoDataUnref data(SkData::NewFromFileName(name));
        if (data.get()) {
            SkImage* image = SkImage::NewEncodedData(data.get());
            if (image) {
                push_ref(L, image);
                image->unref();
                return 1;
            }
        }
    }
    return 0;
}

static void register_Sk(lua_State* L) {
    lua_newtable(L);
    lua_pushvalue(L, -1);
    lua_setglobal(L, "Sk");
    // the Sk table is still on top

    setfield_function(L, "newDocumentPDF", lsk_newDocumentPDF);
    setfield_function(L, "loadImage", lsk_loadImage);
    setfield_function(L, "newPaint", lsk_newPaint);
    setfield_function(L, "newPath", lsk_newPath);
    setfield_function(L, "newRRect", lsk_newRRect);
    setfield_function(L, "newTypeface", lsk_newTypeface);
    lua_pop(L, 1);  // pop off the Sk table
}

#define REG_CLASS(L, C)                             \
    do {                                            \
        luaL_newmetatable(L, get_mtname<C>());      \
        lua_pushvalue(L, -1);                       \
        lua_setfield(L, -2, "__index");             \
        luaL_setfuncs(L, g##C##_Methods, 0);        \
        lua_pop(L, 1); /* pop off the meta-table */ \
    } while (0)

void SkLua::Load(lua_State* L) {
    register_Sk(L);
    REG_CLASS(L, SkCanvas);
    REG_CLASS(L, SkDocument);
    REG_CLASS(L, SkImage);
    REG_CLASS(L, SkPath);
    REG_CLASS(L, SkPaint);
    REG_CLASS(L, SkRRect);
    REG_CLASS(L, SkShader);
    REG_CLASS(L, SkTypeface);
    REG_CLASS(L, SkMatrix);
}

extern "C" int luaopen_skia(lua_State* L);
extern "C" int luaopen_skia(lua_State* L) {
    SkLua::Load(L);
    return 0;
}


text:
//document ready? ->
$(function() {

//initialize socket.io
var socket = io();

var userName = '';
var fullScreen_bool;
var initRoom_bool = false;
var onpopstate = false;

//map variables
var map, last_position, fitWorld = false;
var markers = {};
var markerIcon = L.Icon.extend({
    options: {
        shadowUrl: '../images/shadow-marker.png',
        iconSize:     [25, 39],  // size of the icon
        shadowSize:   [41, 41],  // size of the shadow
        iconAnchor:   [12, 36],  // point of the icon which will correspond to marker's location
        shadowAnchor: [12, 38],  // the same for the shadow
        popupAnchor:  [0, -30]   // point from which the popup should open relative to the iconAnchor
    }
});

//meeting points
var dest_marker, mostVoted;
var limit_dests = 0;
var suggested_meetings = {group: new L.featureGroup([])};
var dragIcon = L.Icon.extend({
    options: {iconSize: [34, 45], iconAnchor: [15, 40], popupAnchor: [0, -30]}
});

var destIcon = L.Icon.extend({
    options: {iconSize: [35, 42], iconAnchor: [18, 40], popupAnchor: [0, -30]}
});

var mostVotedIcon = L.Icon.extend({
    options: {iconSize: [49, 58], iconAnchor: [25, 55], popupAnchor: [0, -30]}
});

//caching elements we'll use multiple times
var $access = $('#access');
var $map = $('#map');
var $chat = $('#chat');
var $messages = $('#messages');
var $members = $('#members');
var $inputMsg = $('#inputMsg');
var $showMapButton = $('#showMapButton');
var $showChatButton = $('#showChatButton');

//back button = first screen
window.onpopstate = function() {
    var url = document.URL;
    var pos = url.lastIndexOf('/');
    var id = url.substring(pos + 1);
    if (onpopstate == true) {
        location.assign(url);
        if (id != '')
            history.replaceState('', 'init', '/');
    }
};

//resize event to adjust automatically according to the window resolution (only for testing in desktop computers)
$(window).on('resize', function(){
    //if low window resolution
    if ($map.css('bottom') == '0px' && initRoom_bool) {
        if (!/Mobi/.test(navigator.userAgent) || $chat.css('display') == 'none' || fullScreen_bool) {
            fullScreen_bool = false;
            $chat.hide();
            $('#switchButtons').css('display','inline');
            $map.show();
            $showMapButton.css('font-weight', 'bold');
            $showChatButton.css('font-weight', 'normal');
        }
    }
    else if (initRoom_bool) {
        fullScreen_bool = true;
        $('#switchButtons').hide();
        $map.show();
        $chat.show();
    }
});

//check the uri and try to join the room id contained in the uri
joinUriId();

//receive from server the event 'public rooms'
socket.on('public rooms', function(rooms){
    $('#publicRooms p').hide();

    for (var r = 0; r < rooms.length; r++)
        $('#publicRooms ul').append($('
').html('Room id: '+rooms[r]+''));
});

socket.on('remove public room', function(pos){
    $('#publicRooms ul li').eq(pos).remove();

    if ($('#publicRooms ul li:first').html() == null)
        $('#publicRooms p').show();
});

$('#newRoom').click(function() {
    $('#access, #publicRooms').hide();
    $('#newOptions').show();
});

$('#joinRoom').click(function() {
    $('#access, #publicRooms').hide();
    $('#joinOptions').show();
});

//<enter> key on a textbox to create a room
$('#newOptions .inputUsername, #newOptions .inputRoom').keyup(function(e) {
    if(e.which == 13) //enter key
        $('#new').click();
});
//creating a room
$('#new').click(function() {
    $(this).prop("disabled", true);
    var roomName = $('#newOptions .inputRoom').val();
    userName = $('#newOptions .inputUsername').val();

    //alphanumeric usernames [2-15] length
    if (/^[A-Za-z0-9]{2,15}$/.test(userName)) {
        if (/^[A-Za-z0-9]{2,10}$/.test(roomName))
            socket.compress(false).emit('new room', userName, roomName, $('#newOptions select').val());
        else if (roomName == null || roomName == '')
            socket.compress(false).emit('new room', userName, null, $('#newOptions select').val());
        else
            errorAccessOptions('The room name must contain between 2 and 10 alphanumeric characters', '#new');
    } else
        errorAccessOptions('The username must contain between 2 and 15 alphanumeric characters', '#new');
});

socket.on('created room', function(roomId){
    //if the roomName is not exists
    if (roomId != false) {
        //support HTML5 - History
        if (window.history && window.history.pushState) {
            history.pushState('create', 'Room: ' + roomId, '/' + roomId);
            onpopstate = true;
        }
        initRoom();
        $('#roomNumber').text(roomId);
        $members.append($('
').text(userName));
    }
    else
        errorAccessOptions('This room name already exists', '#new');
});

//<enter> key on a textbox to join a room
$('#joinOptions .inputUsername, #joinOptions .inputRoom').keyup(function(e) {
    if(e.which == 13) //enter key
        $('#join').click();
});
//joining a room
$('#join').click(function() {
    $(this).prop("disabled", true);
    var roomName = $('#joinOptions .inputRoom').val();
    userName = $('#joinOptions .inputUsername').val();

    //alphanumeric usernames [2-15] length
    if (/^[A-Za-z0-9]{2,15}$/.test(userName)) {
        if (/^[A-Za-z0-9]{2,10}$/.test(roomName))
            socket.compress(false).emit('join room', userName, roomName);
        else
            errorAccessOptions('The room name must contain between 2 and 10 alphanumeric characters', '#join');
    } else
        errorAccessOptions('The username must contain between 2 and 15 alphanumeric characters', '#join');
});

socket.on('joined room', function(code, members, meeting_points){
    //if username and roomName are unique -> code = roomId
    if (code != 0 && code != 1) {
        //support HTML5 - History
        if (window.history && window.history.pushState) {
            history.pushState('join', 'Room: ' + code, '/' + code);
            onpopstate = true;
        }
        initRoom(members, meeting_points);
        $('#roomNumber').text(code);
        for (i = 0; i < members.length; i++)
            $members.append($('
').text(members[i].userName));
    }
    else if (code == 0)
        errorAccessOptions('This username already exists', '#join');
    else if (code == 1)
        errorAccessOptions('Room not found', '#join');
});

$('.cancel').click(function() {
    var url = document.URL;
    var pos = url.lastIndexOf('/');
    var id = url.substring(pos + 1);
    if (id != '')
        //support HTML5 - History
        if (window.history && window.history.replaceState)
            history.replaceState('', 'init', '/');

    $('.accessOptions').hide();
    $('.accessOptions input').val('');
    $('.accessOptions p').text('');
    $('#access, #publicRooms').show();
});

//SHOW CHAT BUTTON (only mobile and tablet)
$showChatButton.click(function(){
    $map.hide();
    $chat.show();
    $showChatButton.css('font-weight', 'bold');
    $showMapButton.css('font-weight', 'normal');
});

//SHOW MAP BUTTON (only mobile and tablet)
$showMapButton.click(function(){
    $chat.hide();
    $map.show();
    $showMapButton.css('font-weight', 'bold');
    $showChatButton.css('font-weight', 'normal');
});

//** Functions **/
function joinUriId() {
    var url = document.URL;
    var pos = url.lastIndexOf('/');

    //id from the uri, example: localhost:8988/a1b23 ([2-10] characters, checked previously in server)
    var id = url.substring(pos + 1);

    if (id != '') {
        $('#joinOptions .inputRoom').val(id);
        $('#joinOptions').show();
        $('#joinOptions .inputUsername').focus();
    }
    else {
        $access.css('display', 'table');
        $('#publicRooms').show();
    }
}

function errorAccessOptions(str, caller) {
    var parent = '#newOptions';
    $(caller).prop("disabled", false);
    if (caller.localeCompare('#join') == 0)
        parent = '#joinOptions';
    if ($(parent + ' p:first').html() == null)
        $(parent).prepend($('
').text(str));
    else
        $(parent + ' p:first').html(str);
}

function initRoom(members, meeting_points) {
    initRoom_bool = true;
    $('.accessOptions').hide();
    $map.show();

    // if the device is mobile or tablet -->
    if ($map.css('bottom') == '0px')
        //show the switch button to alternate map/chat view
        $('#switchButtons').css('display','inline');

    else
        $chat.show();

    prepareMap();
    geolocation();

    if (members != undefined) //dont do this if the function was called from create a room
        updateMarkers(members);

    if (meeting_points != undefined) { //dont do this if the function was called from create a room
        var highVoted = {votes: 0};
        for (var m = 0; m < meeting_points.length; m++) {
            addMeetingPoint(meeting_points[m].userName,
                meeting_points[m].latlng, meeting_points[m].votes);
            if (meeting_points[m].votes > highVoted.votes)
                highVoted = {latlng: meeting_points[m].latlng, votes: meeting_points[m].votes};
        }
        if (highVoted.latlng != undefined)
            newMostVotedMeeting(highVoted.latlng.lat, highVoted.latlng.lng);
    }

    //send a message pressing <Enter>
    $inputMsg.bind('enterKey',function(e){
        var msg = $inputMsg.val();

        //dont send empty or just spaces messages
        if (jQuery.trim(msg).length != 0) {

               //send the event 'chat message' to the server
            socket.compress(false).emit('chat message', msg);

            //append the message to the list
            $messages.append($('
').html('' + userName + ': ' + msg));

            //empty the bar to write new messages
            $inputMsg.val('');

            //scroll automatically to the written message
            if ($('#conversation').height <= $messages.height)
                $('#conversation').prop({scrollTop: $messages.height()});
        }
    });
    $inputMsg.keyup(function(e) {
        if(e.which == 13)
            $(this).trigger('enterKey');
    });

    //receive chat messages
    socket.on('chat message', function(msg) {
        $messages.append($('
').html(msg));
    });

    //when a new user enters the chat
    socket.on('new user', function(userName) {
        $messages.append($('
').html('The user ' + userName + ' is connected'));
        $members.append($('
').text(userName));
    });

    //when a user leaves the chat
    socket.on('user disconnected', function(userName, pos) {
        //if the user disconnected has not a marker defined then has not been geolocated
        if (markers[userName] != undefined) {
            map.removeLayer(markers[userName].marker);
            map.removeLayer(markers[userName].circle);
            delete markers[userName];
        }
        $messages.append($('
').html('The user ' + userName + ' has left'));
        $('#members li').eq(pos).remove();
    });

    //when we receive 'member coords' from the server
    socket.on('member coords', function(members) {
        updateMarkers(members);
    });

    //receiving a meeting point suggested by a member
    socket.on('suggested point', function(userName, latlng){
        addMeetingPoint(userName, latlng);
        checkMostVotedMeeting(latlng.lat, latlng.lng);
        $messages.append($('
').html('' + userName + ' has suggested a meeting point:  ['+latlng.lat.toFixed(4)+', '+latlng.lng.toFixed(4)+']'));
    });
    socket.on('remove suggested point', function(userName, latlng, mostVotedReceived){
        map.removeLayer(suggested_meetings[latlng.lat+latlng.lng]);
        suggested_meetings.group.removeLayer(suggested_meetings[latlng.lat+latlng.lng]);
        delete suggested_meetings[latlng.lat+latlng.lng];
        if (mostVotedReceived != undefined) {
            suggested_meetings[mostVotedReceived].setIcon(new mostVotedIcon({iconUrl: '../images/green-home.png'}));
            mostVoted = mostVotedReceived;
        }
        else if (latlng.lat+latlng.lng == mostVoted)
            mostVoted = undefined;
        $messages.append($('
').html('' + userName + ' has removed the meeting point:  ['+latlng.lat.toFixed(4)+', '+latlng.lng.toFixed(4)+']'));
    });

    //receiving voted meeting points
    socket.on('vote meeting point', function(userName, latlng){
        suggested_meetings[latlng.lat+latlng.lng].setPopupContent(updateVote(suggested_meetings[latlng.lat+latlng.lng].getPopup().getContent(), 1));
        suggested_meetings[latlng.lat+latlng.lng].votes++;
        suggested_meetings[latlng.lat+latlng.lng].fire('popupopen');
        $messages.append($('
').html('' + userName + ' has voted the meeting point:  ['+latlng.lat.toFixed(4)+', '+latlng.lng.toFixed(4)+']'));
        checkMostVotedMeeting(latlng.lat, latlng.lng);
    });
    socket.on('unvote meeting point', function(userName, latlng){
        suggested_meetings[latlng.lat+latlng.lng].setPopupContent(updateVote(suggested_meetings[latlng.lat+latlng.lng].getPopup().getContent(), -1));
        suggested_meetings[latlng.lat+latlng.lng].votes--;
        suggested_meetings[latlng.lat+latlng.lng].fire('popupopen');
        $messages.append($('
').html('' + userName + ' has unvoted the meeting point:  ['+latlng.lat.toFixed(4)+', '+latlng.lng.toFixed(4)+']'));
        if (latlng.lat+latlng.lng == mostVoted)
            findMostVotedMeeting();
    });

    //click here (chat message) to go the suggested meeting point (event delegation, is not present at the time DOM ready)
    $(document).on('click', '.dest_coords', function(){

        // example: coords = [40.23134106086199,-7.89183105016199]
        var coords = $(this).attr('href');
        var lat = coords.substring(2,coords.indexOf(','));
        var lng = coords.substring(coords.indexOf(',')+1,coords.indexOf(']'));
        // if the device is mobile or tablet -->
        if ($map.css('bottom') == '0px') {
            $chat.hide();
            $map.show();
        }

        map.panTo(new L.LatLng(lat, lng));
        return false;
    });
}

function prepareMap() {
    var osm_tileLayer = L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap contributors, © CartoDB'
    });

    var cartoDB_tileLayer = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap contributors, © CartoDB'
    });

    map = L.map('map', {zoomControl: false});
    map.addLayer(osm_tileLayer);

    //map tiles loader
    var loadingControl = L.Control.loading({position: 'topright'});
    map.addControl(loadingControl);

    //custom buttons
    var menuBar, targetBar;
    var showOptions = L.easyButton({
        states: [{
            stateName: 'add-options',
            icon: 'fa-bars fa-lg',
            title: 'Show menu',
            onClick: function(control) {

                //focus on my marker
                var userBtn = L.easyButton({
                    id: 'myPosition',         // an id for the generated button
                    position: 'topleft',      // inherited from L.Control -- the corner it goes in
                    type: 'replace',          // set to animate when you're comfy with css
                    leafletClasses: true,     // use leaflet classes to style the button?
                    states:[{                 // specify different icons and responses for your button
                        stateName: 'default',
                        onClick: function(){
                            if (markers[0] != undefined)
                                map.setView(markers[0].marker.getLatLng(), 16);
                            else
                                L.popup().setLatLng(map.getCenter()).setContent('You must share your location to use this feature.').openOn(map);
                        },
                        title: 'Focus on my position',
                        icon: 'fa-user fa-lg'
                    }]
                });

                //focus on room markers
                var usersBtn = L.easyButton({
                    id: 'membersPosition',    // an id for the generated button
                    position: 'topleft',      // inherited from L.Control -- the corner it goes in
                    type: 'replace',          // set to animate when you're comfy with css
                    leafletClasses: true,     // use leaflet classes to style the button?
                    states:[{                 // specify different icons and responses for your button
                        stateName: 'default',
                        onClick: function(){
                            var allMarkers = [];
                            $('#members').find('li').each(function() {
                                if ($(this).text() != userName && markers[$(this).text()] != undefined)
                                    allMarkers.push(markers[$(this).text()].marker);
                                else if (markers[0] != undefined)
                                    allMarkers.push(markers[0].marker)
                            });
                            if (allMarkers[0] != undefined) {
                                var group = new L.featureGroup(allMarkers);
                                map.fitBounds(group.getBounds());
                            }
                            else
                                L.popup().setLatLng(map.getCenter()).setContent('No members connected or have not shared their location.').openOn(map);
                        },
                        title: 'Focus on all members',
                        icon: 'fa-users fa-lg'
                    }]
                });

                 var meetingsBtn = L.easyButton({
                    id: 'focusMeetings',       // an id for the generated button
                    position: 'topleft',      // inherited from L.Control -- the corner it goes in
                    type: 'replace',          // set to animate when you're comfy with css
                    leafletClasses: true,     // use leaflet classes to style the button?
                    states:[{                 // specify different icons and responses for your button
                        stateName: 'default',
                        onClick: function(){
                            if (suggested_meetings.group.getBounds().isValid())
                                map.fitBounds(suggested_meetings.group.getBounds());
                            else
                                L.popup().setLatLng(map.getCenter()).setContent('No suggested meeting points.').openOn(map);
                        },
                        title: 'Focus on suggested meetings',
                        icon: ''+
                                ''+
                                ''
                    }]
                });

                var markerBtn = L.easyButton({
                    id: 'suggestMeetingPoint', // an id for the generated button
                    position: 'topleft',       // inherited from L.Control -- the corner it goes in
                    type: 'replace',           // set to animate when you're comfy with css
                    leafletClasses: true,      // use leaflet classes to style the button?
                    states:[{                  // specify different icons and responses for your button
                        stateName: 'default',
                        onClick: function(){
                            var center = map.getCenter();
                            if (dest_marker == undefined && limit_dests < 5) {
                                dest_marker = new L.marker([center.lat-0.0001, center.lng], {draggable:'true', icon: new dragIcon({iconUrl: '../images/drag-marker.png'})})
                                    .on('popupopen',onPopupOpen).addTo(map).bindPopup('Drag and drop the marker to the location you want and click on it to suggest a meeting point.').openPopup();
                                dest_marker.on('dragend', function(event) {
                                    dest_marker = event.target;
                                    var position = dest_marker.getLatLng();
                                    dest_marker.setLatLng(new L.LatLng(position.lat, position.lng)).bindPopup('Is this the meeting point you want to suggest to the other members?
<button class="marker-location-yes">Suggest!</button>
Delete without suggesting?
<button class="mymarker-delete-yes">Yes</button><button class="marker-no">No</button>
');
                                    map.panTo(new L.LatLng(position.lat, position.lng))
                                });
                            }
                            else if (dest_marker != undefined)
                                L.popup().setLatLng(dest_marker.getLatLng()).setContent('Set up this meeting point before attempting to create a new one.').openOn(map);
                            else
                                L.popup().setLatLng(center).setContent('You have suggested five meeting points.
Please click one to delete it before suggest a new meeting point.').openOn(map);
                        },
                        title: 'Suggest a meeting point',
                        icon: 'fa-map-marker fa-lg'
                    }]
                });

                var mapBtn = L.easyButton({
                    id: 'changeMapTileProvider', // an id for the generated button
                    position: 'topleft',         // inherited from L.Control -- the corner it goes in
                    type: 'replace',             // set to animate when you're comfy with css
                    leafletClasses: true,        // use leaflet classes to style the button?
                    states:[{                    // specify different icons and responses for your button
                        stateName: 'default',
                        onClick: function(){
                            if (map.hasLayer(osm_tileLayer)) {
                                map.removeLayer(osm_tileLayer);
                                map.addLayer(cartoDB_tileLayer);
                            }
                            else {
                                map.removeLayer(cartoDB_tileLayer);
                                map.addLayer(osm_tileLayer);
                            }
                        },
                        title: 'Change map tile provider',
                        icon: 'fa-map-o fa-lg'
                    }]
                });

                var fullBtn = L.easyButton({
                    id: 'fullScreen',         // an id for the generated button
                    position: 'topleft',      // inherited from L.Control -- the corner it goes in
                    type: 'replace',          // set to animate when you're comfy with css
                    leafletClasses: true,     // use leaflet classes to style the button?
                    states:[{                 // specify different icons and responses for your button
                        stateName: 'default',
                        onClick: function(){
                            map.toggleFullscreen();
                        },
                        title: 'Full Screen',
                        icon: 'fa-arrows-alt fa-lg'
                    }]
                });

                targetBar = L.easyBar([userBtn, usersBtn, meetingsBtn]);
                targetBar.addTo(map);
                $('#myPosition').parent().addClass('target-bar');

                menuBar = L.easyBar([markerBtn, mapBtn, fullBtn]);
                menuBar.addTo(map);

                control.state('hide-options');
            }
        }, {
            stateName: 'hide-options',
            icon: 'fa-undo fa-lg',
            title: 'Hide menu',
            onClick: function(control) {
                targetBar.disable();
                menuBar.disable();
                control.state('show-options');
            }
        }, {
            stateName: 'show-options',
            icon: 'fa-bars fa-lg',
            title: 'Show menu',
            onClick: function(control) {
                targetBar.enable();
                menuBar.enable();
                control.state('hide-options');
            }
        }]
    });
    showOptions.addTo(map);
}

function updateMarkers(members) {
    for (i = 0; i < members.length; i++) {
        if (members[i].coords != undefined) {
            var marker, circle, radius = members[i].coords.acc / 2;

            //if the user has a marker defined in the map
            if (markers[members[i].userName] != undefined) {
                markers[members[i].userName].marker.setLatLng(members[i].coords.latlng);
                markers[members[i].userName].marker.setPopupContent('' + members[i].userName + '
is within ' + radius.toFixed() + ' meters from this point');
                markers[members[i].userName].circle.setLatLng(members[i].coords.latlng);
                markers[members[i].userName].circle.setRadius(radius);
            }

            else {
                marker = L.marker(members[i].coords.latlng, {title: members[i].userName, icon: new markerIcon({iconUrl: '../images/yellow-marker.png'}), opacity: 0.9}).addTo(map);
                marker.bindPopup('' + members[i].userName + '
is within ' + radius.toFixed() + ' meters from this point');
                circle = L.circle(members[i].coords.latlng, radius, {color: 'yellow'}).addTo(map);
                markers[members[i].userName] = {marker: marker, circle: circle};
            }
        }
    }
}

function addMeetingPoint(userName, latlng, votes) {
    var tempVotes = 0;

    if (votes == undefined)
        tempVotes = 1;
    else
        tempVotes = votes;

    suggested_meetings[latlng.lat+latlng.lng] = new L.marker(latlng, {title: 'Meeting point suggested by ' + userName, icon: new destIcon({iconUrl: '../images/yellow-home.png'})})
                .on('popupopen',onPopupOpen).addTo(map).bindPopup(''+userName+''+' has suggested this meeting point.
Votes: '+tempVotes+'
Do you want to vote it?

<button class="marker-vote">Vote!</button>
');
    suggested_meetings[latlng.lat+latlng.lng].votes = tempVotes;
    suggested_meetings.group.addLayer(suggested_meetings[latlng.lat+latlng.lng]);
}

function geolocation() {
    var geolocated = false;
    var setView = true;
    var highAccuracy = true;
    var geolocatingLoader = initialLoader();

    map.locate({watch: true, enableHighAccuracy: true, timeout: 10000});

    //if the geolocation worked ->
    map.on('locationfound', function onLocationFound(e){

        //quit the message 'Attempting to find your location...'
        if (geolocated == false) {
            geolocated = true;
            geolocatingLoader.removeFrom(map);
        }

        var radius = (e.accuracy / 2).toFixed();
        var accuracyLimit = 1000;

        //checking accuracy
        if (radius < accuracyLimit) {

            //only apply the zoom one time!
            if (setView == true) {
                setView = false;
                map.setView(e.latlng, 16);
            }

            //if my marker is defined (my marker is the position: zero) ->
            if (markers[0] != undefined) {
                markers[0].marker.setLatLng(e.latlng);
                markers[0].marker.setPopupContent('You are within ' + radius + ' meters from this point');
                markers[0].circle.setLatLng(e.latlng);
                markers[0].circle.setRadius(radius);
            }
            else {
                markers[0] = {marker: L.marker(e.latlng, {title: 'My location', riseOnHover: true, icon: new markerIcon({iconUrl: '../images/blue-marker.png'})}), circle: L.circle(e.latlng, radius)};
                markers[0].marker.addTo(map).bindPopup('You are within ' + radius + ' meters from this point').openPopup();
                markers[0].circle.addTo(map);
            }

            //avoid share the same last location
            if (last_position == undefined || last_position.lat != e.latlng.lat || last_position.lng != e.latlng.lng) {
                socket.compress(false).emit('coords', {latlng: e.latlng, acc: e.accuracy});
                last_position = e.latlng;
            }
        }
        else //not accuracy
            popUpMapError('Exceeded the limit of accuracy ('+accuracyLimit+'m). Your location can not be achieved accurately.')
    });

    //if the geolocation failed ->
    map.on('locationerror', function onLocationError(e) {
        if (geolocated == false) {
            geolocated = true;
            geolocatingLoader.removeFrom(map);
        }

        //error codes: http://dev.w3.org/geo/api/spec-source.html#code
        //PERMISSION_DENIED = 1;
        if (e.code == 1)
            popUpMapError('The location acquisition process failed because the application does not have permission to use the Geolocation API. Please share your location enabling the "Location" option on your device.');

        //POSITION_UNAVAILABLE = 2;
        else if (e.code == 2)
            popUpMapError('The position of the device could not be determined.');

        //TIMEOUT = 3;
        //trying not high accuracy geolocation
        else if (e.code == 3 && highAccuracy == true) {
            highAccuracy = false;

            //default: (enableHighAccuracy: false)
            map.locate({watch: true, timeout: 10000});
        }
        else if (e.code == 3 && highAccuracy == false)
            popUpMapError('Exceeded the timeout to find your location. Maybe you need share your location enabling the "Location" option on your device.');

        //unknown error
        else
            popUpMapError(e.message);
    });
}

//Show the message: Attempting to find your location
function initialLoader() {
    var geolocatingLoader = L.control({position: 'topright'});

    geolocatingLoader.onAdd = function() {
        var div = L.DomUtil.create('div', 'geolocating');
        div.innerHTML = 'Attempting to find your location...';
        return div;
    };
    geolocatingLoader.addTo(map);

    return geolocatingLoader;
}

//Prompt an error message and advise the user
function popUpMapError(msg, only) {
    msg += '
We recommend using a device that can access GPS satellites, Wi-Fi networks, and mobile networks. However you are free to use most of the features. Close this dialogue to start.
'+
            '
Works best with Chrome, Firefox, Opera or Edge.
';

    //users didnt have shared their locations
    if (fitWorld == false) {
        map.fitWorld();
        L.popup().setLatLng(map.getCenter()).setContent(msg).openOn(map);
        fitWorld = true;
    }
}

//Function to handle events of opened marker popups
function onPopupOpen() {
    var tempMarker = this;

    $('.marker-location-yes:visible').click(function () {
        var latlng = tempMarker.getLatLng();
        socket.compress(false).emit('suggested point', latlng);
        map.removeLayer(tempMarker);
        suggested_meetings[latlng.lat+latlng.lng] = new L.marker(latlng, {icon: new destIcon({iconUrl: '../images/blue-home.png'})})
                .on('popupopen',onPopupOpen).addTo(map).bindPopup('Votes: 1
Do you want to vote/unvote this meeting point?
<button class="marker-vote">Unvote!</button>
Delete the suggested meeting point?
<button class="marker-delete-yes">Yes</button><button class="marker-no">No</button>
');
        suggested_meetings[latlng.lat+latlng.lng].unvoted = true;
        suggested_meetings[latlng.lat+latlng.lng].votes = 1;
        suggested_meetings[latlng.lat+latlng.lng].owner = userName;
        checkMostVotedMeeting(latlng.lat,latlng.lng);
        suggested_meetings.group.addLayer(suggested_meetings[latlng.lat+latlng.lng]);
        limit_dests++;
        dest_marker = undefined;
    });

    $('.marker-delete-yes:visible').click(function () {
        var latlng = tempMarker.getLatLng();
        map.removeLayer(tempMarker);
        suggested_meetings.group.removeLayer(tempMarker);
        limit_dests--;

        //if the removed meeting is not the most voted
        if (mostVoted != latlng.lat+latlng.lng)
            socket.compress(false).emit('remove suggested point', latlng);
        else { //find the most voted meeting and send it and the removed
            var found = findMostVotedMeeting();
            if (found != undefined) {
                socket.compress(false).emit('remove suggested point', latlng, found.lat+found.lng);
                newMostVotedMeeting(found.lat, found.lng);
            }
            else {
                socket.compress(false).emit('remove suggested point', latlng);
                mostVoted = undefined;
            }
        }
        delete suggested_meetings[latlng.lat+latlng.lng];
    });

    //delete before set up the meeting point
    $('.mymarker-delete-yes:visible').click(function () {
        map.removeLayer(tempMarker);
        dest_marker = undefined;
    });

    $('.marker-no:visible').click(function () {
        tempMarker.closePopup();
    });

    $('.marker-vote:visible').click(function () {
        var latlng = tempMarker.getLatLng();

        if (suggested_meetings[latlng.lat+latlng.lng].unvoted) {
            socket.compress(false).emit('unvote meeting point', latlng);
            suggested_meetings[latlng.lat+latlng.lng].unvoted = false;
            suggested_meetings[latlng.lat+latlng.lng].votes--;
            tempMarker.setPopupContent(updateVote(tempMarker.getPopup().getContent(), -1).
                replace(/Vote!|Unvote!/,function(match) {return (match=="Vote!")?"Unvote!":"Vote!";}));
            tempMarker.fire('popupopen');
            if (latlng.lat+latlng.lng == mostVoted)
                findMostVotedMeeting();
        }
        else {
            socket.compress(false).emit('vote meeting point', latlng);
            suggested_meetings[latlng.lat+latlng.lng].unvoted = true;
            suggested_meetings[latlng.lat+latlng.lng].votes++;
            tempMarker.setPopupContent(updateVote(tempMarker.getPopup().getContent(), 1).
                replace(/Vote!|Unvote!/,function(match) {return (match=="Vote!")?"Unvote!":"Vote!";}));
            tempMarker.fire('popupopen');
            checkMostVotedMeeting(latlng.lat, latlng.lng);
        }
    });
}

function updateVote(content, vote) {
    var res = content.replace(/Votes: (\d+)/, function(match, number) {
        var votes = parseInt(number)+vote;
        return 'Votes: ' + votes;
    });
    return res;
}

function checkMostVotedMeeting(lat, lng) {
    if (mostVoted == undefined)
        newMostVotedMeeting(lat, lng);

    else if (suggested_meetings[mostVoted].votes < suggested_meetings[lat+lng].votes) {
        if (suggested_meetings[mostVoted].owner == userName)
            suggested_meetings[mostVoted].setIcon(new destIcon({iconUrl: '../images/blue-home.png'}));
        else
            suggested_meetings[mostVoted].setIcon(new destIcon({iconUrl: '../images/yellow-home.png'}));
        newMostVotedMeeting(lat, lng);
    }
}

function newMostVotedMeeting(lat, lng) {
    suggested_meetings[lat+lng].setIcon(new mostVotedIcon({iconUrl: '../images/green-home.png'}));
    mostVoted = lat+lng;
}

function findMostVotedMeeting() {
    var array = suggested_meetings.group.getLayers();
    var highVoted = {votes: 0};

    for (var i = 0; i < array.length; i++) {
        if (array[i].votes > highVoted.votes || highVoted.latlng == undefined)
            highVoted = {latlng: array[i].getLatLng(), votes: array[i].votes};
    }
    if (highVoted.latlng != undefined)
        checkMostVotedMeeting(highVoted.latlng.lat, highVoted.latlng.lng);

    return highVoted.latlng;
}
});


text:
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { on } from '@ember/object/evented';
import moment from 'moment';
import attr from 'ember-data/attr';
import ModelBase from 'open-event-frontend/models/base';
import { hasMany, belongsTo } from 'ember-data/relationships';
import {
  computedDateTimeSplit,
  computedSegmentedLink
} from 'open-event-frontend/utils/computed-helpers';
import CustomPrimaryKeyMixin from 'open-event-frontend/mixins/custom-primary-key';
import { groupBy } from 'lodash-es';

const detectedTimezone = moment.tz.guess();

export default ModelBase.extend(CustomPrimaryKeyMixin, {

  /**
   * Service Injection
   */

  router   : service(),
  fastboot : service(),

  /**
   * Attributes
   */

  identifier             : attr('string', { readOnly: true }),
  name                   : attr('string'),
  description            : attr('string'),
  startsAt               : attr('moment', { defaultValue: () => moment.tz(detectedTimezone).add(1, 'months').startOf('day') }),
  endsAt                 : attr('moment', { defaultValue: () => moment.tz(detectedTimezone).add(1, 'months').hour(17).minute(0) }),
  timezone               : attr('string', { defaultValue: detectedTimezone }),
  locationName           : attr('string'),
  searchableLocationName : attr('string'),

  longitude : attr('number', { defaultValue: 0.0 }),
  latitude  : attr('number', { defaultValue: 0.0 }),

  logoUrl           : attr('string'),
  thumbnailImageUrl : attr('string', { readOnly: true }),
  largeImageUrl     : attr('string', { readOnly: true }),
  originalImageUrl  : attr('string'),
  iconImageUrl      : attr('string', { readOnly: true }),

  isMapShown                : attr('boolean', { defaultValue: true }),
  isSponsorsEnabled         : attr('boolean', { defaultValue: false }),
  isTicketingEnabled        : attr('boolean', { defaultValue: true }),
  isSessionsSpeakersEnabled : attr('boolean', { defaultValue: false }),
  isFeatured                : attr('boolean', { defaultValue: false }),

  isTaxEnabled    : attr('boolean', { defaultValue: false }),
  canPayByPaypal  : attr('boolean', { defaultValue: false }),
  canPayByStripe  : attr('boolean', { defaultValue: false }),
  isStripeLinked  : attr('boolean'),
  canPayByCheque  : attr('boolean', { defaultValue: false }),
  canPayByBank    : attr('boolean', { defaultValue: false }),
  canPayByOmise   : attr('boolean', { defaultValue: false }),
  canPayOnsite    : attr('boolean', { defaultValue: false }),
  paymentCountry  : attr('string'),
  paymentCurrency : attr('string', { defaultValue: 'USD' }),
  paypalEmail     : attr('string'),
  chequeDetails   : attr('string'),
  bankDetails     : attr('string'),
  onsiteDetails   : attr('string'),
  orderExpiryTime : attr('number', { defaultValue: 10 }),

  schedulePublishedOn: attr('moment', { defaultValue: () => moment(0) }),

  hasOrganizerInfo: attr('boolean',  { defaultValue: false }),

  organizerName        : attr('string'),
  organizerDescription : attr('string'),

  externalEventUrl : attr('string'),
  ticketUrl        : attr('string'),
  codeOfConduct    : attr('string'),

  state   : attr('string', { defaultValue: 'draft' }),
  privacy : attr('string', { defaultValue: 'public' }),

  pentabarfUrl : attr('string', { readOnly: true }),
  xcalUrl      : attr('string', { readOnly: true }),
  icalUrl      : attr('string', { readOnly: true }),

  createdAt : attr('moment', { readOnly: true }),
  deletedAt : attr('moment'),

  /**
   * Relationships
   */
  type                   : belongsTo('event-type'),
  topic                  : belongsTo('event-topic'),
  subTopic               : belongsTo('event-sub-topic'),
  location               : belongsTo('event-location'),
  sessions               : hasMany('session'),
  sponsors               : hasMany('sponsor'),
  microlocations         : hasMany('microlocation'),
  tracks                 : hasMany('track'),
  tickets                : hasMany('ticket'),
  orders                 : hasMany('order'),
  socialLinks            : hasMany('social-link'),
  emailNotifications     : hasMany('email-notification'),
  speakers               : hasMany('speaker'),
  invoice                : hasMany('event-invoice'),
  speakersCall           : belongsTo('speakers-call'),
  stripeAuthorization    : belongsTo('stripe-authorization'),
  eventStatisticsGeneral : belongsTo('event-statistics-general'),
  tax                    : belongsTo('tax'),
  copyright              : belongsTo('event-copyright'),
  sessionTypes           : hasMany('session-type'),
  user                   : belongsTo('user', { inverse: 'events' }),

  customForms     : hasMany('custom-form'),
  attendees       : hasMany('attendee'),
  orderStatistics : belongsTo('order-statistics-event'),
  roleInvites     : hasMany('role-invite'),

  organizers      : hasMany('user', { inverse: null }),
  coorganizers    : hasMany('user', { inverse: null }),
  trackOrganizers : hasMany('user', { inverse: null }),
  registrars      : hasMany('user', { inverse: null }),
  moderators      : hasMany('user', { inverse: null }),

  /**
   * The discount code applied to this event [Form(1) discount code]
   *
   * @see app/models/discount-code.js
   */
  discountCode: belongsTo('discount-code'),

  /**
   * The discount codes created for this event's tickets [Form(2) discount code]
   *
   * @see app/models/discount-code.js
   */
  discountCodes: hasMany('discount-code'),

  accessCodes: hasMany('access-code'),

  /**
   * Computed properties
   */

  startsAtDate : computedDateTimeSplit.bind(this)('startsAt', 'date'),
  startsAtTime : computedDateTimeSplit.bind(this)('startsAt', 'time'),
  endsAtDate   : computedDateTimeSplit.bind(this)('endsAt', 'date'),
  endsAtTime   : computedDateTimeSplit.bind(this)('endsAt', 'time'),

  segmentedExternalEventUrl : computedSegmentedLink.bind(this)('externalEventUrl'),
  segmentedTicketUrl        : computedSegmentedLink.bind(this)('ticketUrl'),

  shortLocationName: computed('locationName', function() {
    if (!this.locationName) {
      return '';
    }
    let splitLocations = this.locationName.split(',');
    if (splitLocations.length <= 3) {
      return this.locationName;
    } else {
      return splitLocations.splice(1, splitLocations.length).join();
    }
  }),

  url: computed('identifier', function() {
    const origin = this.get('fastboot.isFastBoot') ? `${this.get('fastboot.request.protocol')}//${this.get('fastboot.request.host')}` : location.origin;
    return origin + this.router.urlFor('public', this.id);
  }),

  sessionsByState: computed('sessions', function() {
    return groupBy(this.sessions.toArray(), 'data.state');
  }),

  _ready: on('ready', function() {

  })
});


text:
var _         = require('lodash');
var launchpad = require('launchpad');

var LAUNCHPAD_TO_SELENIUM = {
  chrome:  chrome,
  canary:  chrome,
  firefox: firefox,
  aurora:  firefox,
  ie:      internetExplorer,
  // Until https://code.google.com/p/selenium/issues/detail?id=7933
  safari:  safari,
};

/**
 * @param {Array<string|!Object>} browsers
 * @return {!Array<string>}
 */
function normalize(browsers) {
  return (browsers || []).map(function(browser) {
    return browser.browserName || browser;
  });
}

/**
 * Expands an array of browser identifiers for locally installed browsers into
 * their webdriver capabilities objects.
 *
 * If `names` is empty, or contains `all`, all installed browsers will be used.
 *
 * @param {!Array<string>} names
 * @param {function(*, Array<!Object>)} done
 */
function expand(names, done) {
  if (names.indexOf('all') !== -1) {
    names = [];
  }

  var unsupported = _.difference(names, module.exports.supported());
  if (unsupported.length > 0) {
    return done(
        'The following browsers are unsupported: ' + unsupported.join(', ') + '. ' +
        '(All supported browsers: ' + module.exports.supported().join(', ') + ')'
    );
  }

  module.exports.detect(function(error, installedByName) {
    if (error) return done(error);
    var installed = _.keys(installedByName);
    // Opting to use everything?
    if (names.length === 0) {
      names = installed;
    }

    var missing   = _.difference(names, installed);
    if (missing.length > 0) {
      return done(
          'The following browsers were not found: ' + missing.join(', ') + '. ' +
          '(All installed browsers found: ' + installed.join(', ') + ')'
      );
    }

    done(null, names.map(function(n) { return installedByName[n]; }));
  });
}

/**
 * Detects any locally installed browsers that we support.
 *
 * @param {function(*, Object<string, !Object>)} done
 */
function detect(done) {
  launchpad.local(function(error, launcher) {
    if (error) return done(error);
    launcher.browsers(function(error, browsers) {
      if (error) return done(error);

      var results = {};
      for (var i = 0, browser; browser = browsers[i]; i++) {
        if (!LAUNCHPAD_TO_SELENIUM[browser.name]) continue;
        results[browser.name] = LAUNCHPAD_TO_SELENIUM[browser.name](browser);
      }

      done(null, results);
    });
  });
}

/**
 * @return {!Array<string>} A list of local browser names that are supported by
 *     the current environment.
 */
function supported() {
  return _.intersection(_.keys(launchpad.local.platform), _.keys(LAUNCHPAD_TO_SELENIUM));
}

// Launchpad -> Selenium

/**
 * @param {!Object} browser A launchpad browser definition.
 * @return {!Object} A selenium capabilities object.
 */
function chrome(browser) {
  return {
    'browserName': 'chrome',
    'version':     browser.version.match(/\d+/)[0],
    'chromeOptions': {
      'binary': browser.binPath,
      'args': ['start-maximized']
    },
  };
}

/**
 * @param {!Object} browser A launchpad browser definition.
 * @return {!Object} A selenium capabilities object.
 */
function firefox(browser) {
  return {
    'browserName':    'firefox',
    'version':        browser.version.match(/\d+/)[0],
    'firefox_binary': browser.binPath,
  };
}

/**
 * @param {!Object} browser A launchpad browser definition.
 * @return {!Object} A selenium capabilities object.
 */
function safari(browser) {
  // SafariDriver doesn't appear to support custom binary paths. Does Safari?
  return {
    'browserName': 'safari',
    'version':     browser.version,
    // TODO(nevir): TEMPORARY. https://github.com/Polymer/web-component-tester/issues/51
    'safari.options': {
      'skipExtensionInstallation': true,
    },
  };
}

/**
 * @param {!Object} browser A launchpad browser definition.
 * @return {!Object} A selenium capabilities object.
 */
function internetExplorer(browser) {
  return {
    'browserName': 'ie',
    'version':     browser.version,
  };
}

module.exports = {
  normalize: normalize,
  detect:    detect,
  expand:    expand,
  supported: supported,
};


text:
import { asDay, sortByTime } from './common';
import { makeNotification } from '../model/analysis';
import { MOOD } from '../model/const';

const nthEntry = (journal, n, content) => journal.length > n ? [makeNotification(content, journal[n - 1].time)] : [];
const nthEntryEvents = journal => [
  ...nthEntry(journal, 1, 'notifications.1th-entry'),
  ...nthEntry(journal, 2, 'notifications.2th-entry'),
  ...nthEntry(journal, 5, 'notifications.5th-entry'),
  ...nthEntry(journal, 10, 'notifications.10th-entry'),
  ...nthEntry(journal, 20, 'notifications.20th-entry'),
  ...nthEntry(journal, 50, 'notifications.50th-entry'),
  ...nthEntry(journal, 100, 'notifications.100th-entry'),
  ...nthEntry(journal, 200, 'notifications.200th-entry'),
  ...nthEntry(journal, 500, 'notifications.500th-entry'),
  ...nthEntry(journal, 1000, 'notifications.1000th-entry')
];

const moodyDayEvents = journal => {
  const moods = journal.filter(entry => entry.type === MOOD);
  const moodyDay = (times, entry) => {
    const date = asDay(entry.time);
    const earlierSameDayDifferentMood = moods
        .filter(mood => Date.parse(entry.time) > Date.parse(mood.time))
        .filter(mood => asDay(mood.time).valueOf() === date.valueOf())
        .filter(mood => mood.mood !== entry.mood);
    return earlierSameDayDifferentMood.length > 0 ? [...times, entry.time] : times;
  };
  return moods.reduce(moodyDay, []).map(time => makeNotification('notifications.moody-day', time));
};

const analyseForNotifications = journal => [
  ...nthEntryEvents(journal),
  ...moodyDayEvents(journal)
].sort(sortByTime);

export default analyseForNotifications;


text:
class AccountService {
  constructor($http) {
    this.$http = $http;
  }

  getProfile() {
    return this.$http.get('/user');
  }
}
AccountService.$inject = ['$http'];

let accountServiceModule = angular.module('AuthServiceModule', []);
accountServiceModule.service('accountService', AccountService);

export default accountServiceModule = accountServiceModule.name;


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<title>racoon.core Class Hierarchy</title>
<meta name="date" content="2017-05-22">
<link rel="stylesheet" type="text/css" href="../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Hierarchy For Package racoon.core

Package Hierarchies:

All Packages

Class Hierarchy

java.lang.Object

racoon.core.Config

racoon.core.ConfigLoader

racoon.core.RacoonProcess

Interface Hierarchy

racoon.core.Specification

racoon.core.SpecificationParser

Enum Hierarchy

java.lang.Object

java.lang.Enum<E> (implements java.lang.Comparable<T>, java.io.Serializable)

racoon.core.ConfigLoader.OperationMode

racoon.core.ConfigLoader.ExplorationAlgorithm

Skip navigation links

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes


text:
using System;

namespace Azure.ResourceManager.Media.Models
{
    /// <summary> Specifies that the content key ID is specified in the PlayReady configuration. </summary>
    public partial class ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier : ContentKeyPolicyPlayReadyContentKeyLocation
    {
        /// <summary> Initializes a new instance of ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier. </summary>
        /// <param name="keyId"> The content key ID. </param>
        public ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier(Guid? keyId)
        {
            KeyId = keyId;
            OdataType = "#Microsoft.Media.ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier";
        }

        /// <summary> Initializes a new instance of ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier. </summary>
        /// <param name="odataType"> The discriminator for derived types. </param>
        /// <param name="keyId"> The content key ID. </param>
        internal ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier(string odataType, Guid? keyId) : base(odataType)
        {
            KeyId = keyId;
            OdataType = odataType ?? "#Microsoft.Media.ContentKeyPolicyPlayReadyContentEncryptionKeyFromKeyIdentifier";
        }

        /// <summary> The content key ID. </summary>
        public Guid? KeyId { get; set; }
    }
}


text:
This is just a temporary scratch for me to use since I'm bouncing between computers.

TODO
====
development;
  - Procfile parity?
tests ffs
Look into Fig
Flynn is a really interesting competitor