func TestFuncImageURL(t *testing.T) { ctx := libsass.Context{ BuildDir: "test/build", ImageDir: "test/img", } usv, _ := libsass.Marshal([]string{"image.png"}) usv = ImageURL(&ctx, usv) var path string libsass.Unmarshal(usv, &path) if e := "url('../img/image.png')"; e != path { t.Errorf("got: %s wanted: %s", path, e) } // Test sending invalid date to imageURL usv, _ = libsass.Marshal(libsass.SassNumber{Value: 1, Unit: "px"}) _ = usv errusv := ImageURL(&ctx, usv) var s string merr := libsass.Unmarshal(errusv, &s) if merr != nil { t.Error(merr) } e := "Sassvalue is type context.SassNumber and has value {1 px} but expected slice" if e != s { t.Errorf("got:\n%s\nwanted:\n%s", s, e) } }
// SpriteMap returns a sprite from the passed glob and sprite // parameters. func SpriteMap(ctx *libsass.Context, usv libsass.UnionSassValue) libsass.UnionSassValue { var glob string var spacing libsass.SassNumber err := libsass.Unmarshal(usv, &glob, &spacing) if err != nil { return libsass.Error(err) } imgs := sw.ImageList{ ImageDir: ctx.ImageDir, BuildDir: ctx.BuildDir, GenImgDir: ctx.GenImgDir, } imgs.Padding = int(spacing.Value) if cglob, err := strconv.Unquote(glob); err == nil { glob = cglob } key := glob + strconv.FormatInt(int64(spacing.Value), 10) // TODO: benchmark a single write lock against this // read lock then write lock ctx.Sprites.RLock() if _, ok := ctx.Sprites.M[key]; ok { ctx.Sprites.RUnlock() res, err := libsass.Marshal(key) if err != nil { return libsass.Error(err) } return res } ctx.Sprites.RUnlock() err = imgs.Decode(glob) if err != nil { return libsass.Error(err) } _, err = imgs.Combine() if err != nil { return libsass.Error(err) } _, err = imgs.Export() if err != nil { return libsass.Error(err) } res, err := libsass.Marshal(key) ctx.Sprites.Lock() ctx.Sprites.M[key] = imgs ctx.Sprites.Unlock() if err != nil { return libsass.Error(err) } return res }
// InlineImage returns a base64 encoded png from the input image func InlineImage(ctx *libsass.Context, usv libsass.UnionSassValue) libsass.UnionSassValue { var ( name string encode bool f io.Reader ) err := libsass.Unmarshal(usv, &name, &encode) if err != nil { return libsass.Error(err) } f, err = os.Open(filepath.Join(ctx.ImageDir, name)) if err != nil { r, err := httpInlineImage(name) if err != nil { return libsass.Error(err) } f = r if r != nil { defer r.Close() } } if err != nil { return libsass.Error(err) } var buf bytes.Buffer sw.Inline(f, &buf, encode) res, err := libsass.Marshal(buf.String()) if err != nil { return libsass.Error(err) } return res }
// Sprite returns the source and background position for an image in the // spritesheet. func Sprite(ctx *libsass.Context, usv libsass.UnionSassValue) libsass.UnionSassValue { var glob, name string var offsetX, offsetY libsass.SassNumber _, _ = offsetX, offsetY // TODO: ignore these for now err := libsass.Unmarshal(usv, &glob, &name, &offsetX, &offsetY) if err != nil { if strings.Contains(err.Error(), "unsupported") { return libsass.Error(fmt.Errorf( "Please specify unit for offset ie. (2px)")) } return libsass.Error(err) } ctx.Sprites.RLock() defer ctx.Sprites.RUnlock() imgs, ok := ctx.Sprites.M[glob] if !ok { keys := make([]string, 0, len(ctx.Sprites.M)) for i := range ctx.Sprites.M { keys = append(keys, i) } return libsass.Error(fmt.Errorf( "Variable not found matching glob: %s sprite:%s", glob, name)) } path, err := imgs.OutputPath() // FIXME: path directory can not be trusted, rebuild this from the context if ctx.HTTPPath == "" { ctxPath, _ := filepath.Rel(ctx.BuildDir, ctx.GenImgDir) path = filepath.Join(ctxPath, filepath.Base(path)) } else { u, err := url.Parse(ctx.HTTPPath) if err != nil { return libsass.Error(err) } u.Path = filepath.Join(u.Path, "build", filepath.Base(path)) path = u.String() } if err != nil { return libsass.Error(err) } if imgs.Lookup(name) == -1 { return libsass.Error(fmt.Errorf("image %s not found\n"+ " try one of these: %v", name, imgs.Paths)) } // This is an odd name for what it does pos := imgs.GetPack(imgs.Lookup(name)) if err != nil { return libsass.Error(err) } str, err := libsass.Marshal(fmt.Sprintf(`url("%s") -%dpx -%dpx`, path, pos.X, pos.Y)) if err != nil { return libsass.Error(err) } return str }
func TestFuncSpriteFile(t *testing.T) { ctx := libsass.NewContext() ctx.BuildDir = "../test/build" ctx.GenImgDir = "../test/build/img" ctx.ImageDir = "../test/img" // Add real arguments when sass lists can be [un]marshalled lst := []interface{}{"*.png", "139"} usv, _ := libsass.Marshal(lst) usv = SpriteFile(ctx, usv) var glob, path string err := libsass.Unmarshal(usv, &glob, &path) if err != nil { t.Error(err) } if e := "*.png"; e != glob { t.Errorf("got: %s wanted: %s", e, glob) } if e := "139"; e != path { t.Errorf("got: %s wanted: %s", e, path) } }
// WarnHandler captures Sass warnings and redirects to stdout func WarnHandler(v interface{}, csv libsass.SassValue, rsv *libsass.SassValue) error { var s string libsass.Unmarshal(csv, &s) fmt.Println(color.YellowString("WARNING: " + s)) r, _ := libsass.Marshal("") *rsv = r return nil }
// ImageWidth takes a file path (or sprite glob) and returns the // width in pixels of the image being referenced. func ImageWidth(mainctx context.Context, usv libsass.SassValue) (rsv *libsass.SassValue, err error) { var ( glob, name string ) comp, err := libsass.CompFromCtx(mainctx) if err != nil { return } err = libsass.Unmarshal(usv, &name) // Check for sprite-file override first if err != nil { var inf interface{} var infs []interface{} // Can't unmarshal to []interface{}, so unmarshal to // interface{} then reflect it into a []interface{} err = libsass.Unmarshal(usv, &inf) k := reflect.ValueOf(&infs).Elem() k.Set(reflect.ValueOf(inf)) if err != nil { return } glob = infs[0].(string) name = infs[1].(string) } paths := comp.(libsass.Pather) imgs := sw.New(&sw.Options{ ImageDir: paths.ImgDir(), BuildDir: paths.BuildDir(), GenImgDir: paths.ImgBuildDir(), }) loadctx := comp.Payload() var images payload.Payloader if len(glob) == 0 { images = payload.Image(loadctx) hit := images.Get(name) if hit != nil { imgs = hit } else { imgs.Decode(name) images.Set(name, imgs) } } else { // Glob present, look up in sprites sprites := payload.Sprite(loadctx) imgs = sprites.Get(glob) } w := imgs.SImageWidth(name) ww := libs.SassNumber{ Value: float64(w), Unit: "px", } res, err := libsass.Marshal(ww) return &res, err }
// SpriteFile proxies the sprite glob and image name through. func SpriteFile(ctx *libsass.Context, usv libsass.UnionSassValue) libsass.UnionSassValue { var glob, name string err := libsass.Unmarshal(usv, &glob, &name) if err != nil { return libsass.Error(err) } infs := []interface{}{glob, name} res, err := libsass.Marshal(infs) return res }
// SpriteFile proxies the sprite glob and image name through. func SpriteFile(ctx context.Context, usv libsass.SassValue) (rsv *libsass.SassValue, err error) { var glob, name string err = libsass.Unmarshal(usv, &glob, &name) if err != nil { return nil, err } infs := []interface{}{glob, name} res, err := libsass.Marshal(infs) return &res, err }
// ImageHeight takes a file path (or sprite glob) and returns the // height in pixels of the image being referenced. func ImageHeight(ctx *libsass.Context, usv libsass.UnionSassValue) libsass.UnionSassValue { var ( glob string name string ) err := libsass.Unmarshal(usv, &name) // Check for sprite-file override first if err != nil { var inf interface{} var infs []interface{} // Can't unmarshal to []interface{}, so unmarshal to // interface{} then reflect it into a []interface{} err = libsass.Unmarshal(usv, &inf) k := reflect.ValueOf(&infs).Elem() k.Set(reflect.ValueOf(inf)) if err != nil { return libsass.Error(err) } glob = infs[0].(string) name = infs[1].(string) } imgs := sw.ImageList{ ImageDir: ctx.ImageDir, BuildDir: ctx.BuildDir, GenImgDir: ctx.GenImgDir, } if glob == "" { if hit, ok := ctx.Imgs.M[name]; ok { imgs = hit } else { imgs.Decode(name) imgs.Combine() ctx.Imgs.Lock() ctx.Imgs.M[name] = imgs ctx.Imgs.Unlock() } } else { ctx.Sprites.RLock() imgs = ctx.Sprites.M[glob] ctx.Sprites.RUnlock() } height := imgs.SImageHeight(name) Hheight := libsass.SassNumber{ Value: float64(height), Unit: "px", } res, err := libsass.Marshal(Hheight) if err != nil { fmt.Println(err) } return res }
// SpritePosition returns the position of the image in the sprite-map. // This is useful for passing directly to background-position func SpritePosition(mainctx context.Context, usv libsass.SassValue) (rsv *libsass.SassValue, err error) { comp, err := libsass.CompFromCtx(mainctx) if err != nil { return } var glob, name string err = libsass.Unmarshal(usv, &glob, &name) if err != nil { return } loadctx := comp.Payload() if loadctx == nil { err = ErrPayloadNil return } sprites := payload.Sprite(loadctx) if sprites == nil { err = errors.New("Sprites missing") return } imgs := sprites.Get(glob) if imgs == nil { err = fmt.Errorf( "Variable not found matching glob: %s sprite:%s", glob, name) return } if imgs.Lookup(name) == -1 { err = fmt.Errorf("image %s not found\n"+ " try one of these: %v", name, imgs.Paths()) return } // This is an odd name for what it does pos := imgs.GetPack(imgs.Lookup(name)) if err != nil { return } x := libs.SassNumber{Unit: "px", Value: float64(-pos.X)} y := libs.SassNumber{Unit: "px", Value: float64(-pos.Y)} str, err := libsass.Marshal( []libs.SassNumber{x, y}, ) return &str, err }
func TestFuncImageURL(t *testing.T) { comp, err := libsass.New(nil, nil, libsass.BuildDir("../test/build"), libsass.ImgDir("../test/img"), ) if err != nil { t.Fatal(err) } ctx := libsass.NewCompilerContext(comp) usv, _ := libsass.Marshal([]string{"139.png"}) rsv, err := ImageURL(ctx, usv) if err != nil { t.Fatal(err) } var path string err = libsass.Unmarshal(*rsv, &path) if err != nil { t.Fatal(err) } if e := "url('../img/139.png')"; e != path { t.Errorf("got: %s wanted: %s", path, e) } // Test sending invalid date to imageURL usv, _ = libsass.Marshal(libs.SassNumber{Value: 1, Unit: "px"}) _, err = ImageURL(ctx, usv) if err == nil { t.Fatal("error is nil") } e := "Invalid Sass type expected: slice got: libs.SassNumber value: 1px" if e != err.Error() { t.Errorf("got: %s wanted: %s", err, e) } }
// FontURL builds a relative path to the requested font file from the built CSS. func FontURL(ctx *libsass.Context, usv libsass.UnionSassValue) libsass.UnionSassValue { var ( path, format string csv libsass.UnionSassValue raw bool ) err := libsass.Unmarshal(usv, &path, &raw) if err != nil { return libsass.Error(err) } // Enter warning if ctx.FontDir == "." || ctx.FontDir == "" { s := "font-url: font path not set" fmt.Println(s) res, _ := libsass.Marshal(s) return res } rel, err := filepath.Rel(ctx.BuildDir, ctx.FontDir) if err != nil { return libsass.Error(err) } if raw { format = "%s" } else { format = `url("%s")` } csv, err = libsass.Marshal(fmt.Sprintf(format, filepath.Join(rel, path))) if err != nil { return libsass.Error(err) } return csv }
// ImageURL handles calls to resolve a local image from the // built css file path. func ImageURL(ctx *libsass.Context, csv libsass.UnionSassValue) libsass.UnionSassValue { var path []string err := libsass.Unmarshal(csv, &path) // This should create and throw a sass error if err != nil { return libsass.Error(err) } url := filepath.Join(ctx.RelativeImage(), path[0]) res, err := libsass.Marshal(fmt.Sprintf("url('%s')", url)) if err != nil { return libsass.Error(err) } return res }
// FontURL builds a relative path to the requested font file from the built CSS. func FontURL(ctx context.Context, usv libsass.SassValue) (*libsass.SassValue, error) { var ( path, format string csv libsass.SassValue raw bool ) comp, err := libsass.CompFromCtx(ctx) if err != nil { return nil, err } err = libsass.Unmarshal(usv, &path, &raw) if err != nil { return nil, err } paths := comp.(libsass.Pather) fdir := paths.FontDir() // Enter warning if fdir == "." || fdir == "" { s := "font-url: font path not set" return nil, errors.New(s) } rel, err := filepath.Rel(paths.BuildDir(), fdir) if err != nil { return nil, err } if raw { format = "%s%s" } else { format = `url("%s%s")` } abspath := filepath.Join(fdir, path) qry, err := qs(comp.CacheBust(), abspath) if err != nil { return nil, err } csv, err = libsass.Marshal(fmt.Sprintf(format, filepath.ToSlash(filepath.Join(rel, path)), qry, )) return &csv, err }
func BenchmarkSprite(b *testing.B) { ctx := libsass.NewContext() ctx.BuildDir = "context/test/build" ctx.GenImgDir = "context/test/build/img" ctx.ImageDir = "context/test/img" // Add real arguments when sass lists can be [un]marshalled lst := []interface{}{"*.png", libsass.SassNumber{Value: 5, Unit: "px"}} usv, _ := libsass.Marshal(lst) for i := 0; i < b.N; i++ { usv = SpriteMap(ctx, usv) } // Debug if needed // var s string // Unmarshal(usv, &s) // fmt.Println(s) }
// InlineImage returns a base64 encoded png from the input image func InlineImage(mainctx context.Context, usv libsass.SassValue) (rsv *libsass.SassValue, err error) { var ( name string encode bool f io.ReadCloser ) comp, err := libsass.CompFromCtx(mainctx) if err != nil { return nil, err } err = libsass.Unmarshal(usv, &name, &encode) if err != nil { return nil, err } paths := comp.(libsass.Pather) // check for valid URL. If true, attempt to resolve image. // This is really going to slow down compilation think about // writing data to disk instead of inlining. u, err := url.Parse(name) if err == nil && len(u.Scheme) > 0 { f, err = imgResolver.Do(u.String()) } else { f, err = os.Open(filepath.Join(paths.ImgDir(), name)) } if err != nil { return nil, err } defer f.Close() var buf bytes.Buffer err = sw.Inline(f, &buf, encode) if err != nil { return nil, err } res, err := libsass.Marshal(buf.String()) if err != nil { return nil, err } return &res, nil }
func TestFuncSpriteMap(t *testing.T) { ctx := libsass.NewContext() ctx.BuildDir = "../test/build" ctx.GenImgDir = "../test/build/img" ctx.ImageDir = "../test/img" // Add real arguments when sass lists can be [un]marshalled lst := []interface{}{"*.png", libsass.SassNumber{Value: 5, Unit: "px"}} usv, _ := libsass.Marshal(lst) usv = SpriteMap(ctx, usv) var path string err := libsass.Unmarshal(usv, &path) if err != nil { t.Error(err) } if e := "*.png5"; e != path { t.Errorf("got: %s wanted: %s", path, e) } }
// ImageURL handles calls to resolve the path to a local image from the // built css file path. func ImageURL(ctx context.Context, csv libsass.SassValue) (*libsass.SassValue, error) { comp, err := libsass.CompFromCtx(ctx) if err != nil { return nil, err } pather := comp.(libsass.Pather) var path []string err = libsass.Unmarshal(csv, &path) // This should create and throw a sass error if err != nil { return nil, err } if len(path) != 1 { return nil, errors.New("path not found") } imgdir := pather.ImgDir() abspath := filepath.Join(imgdir, path[0]) method := comp.CacheBust() qry, err := qs(method, abspath) if err != nil { return nil, err } rel, err := relativeImage(pather.BuildDir(), pather.ImgDir()) if err != nil { return nil, err } url := strings.Join([]string{ rel, path[0], }, "/") res, err := libsass.Marshal(fmt.Sprintf("url('%s%s')", url, qry)) if err != nil { return nil, err } return &res, nil }
func TestFuncSpriteFile(t *testing.T) { comp, err := libsass.New(nil, nil, libsass.Payload(payload.New()), libsass.ImgDir("../test/img"), libsass.BuildDir("../test/build"), libsass.ImgBuildDir("../test/build/img"), ) if err != nil { t.Fatal(err) } // Add real arguments when sass lists can be [un]marshalled lst := []interface{}{"*.png", "139"} usv, err := libsass.Marshal(lst) if err != nil { t.Fatal(err) } rsv, err := SpriteFile(libsass.NewCompilerContext(comp), usv) if err != nil { t.Fatal(err) } var glob, path string err = libsass.Unmarshal(*rsv, &glob, &path) if err != nil { t.Error(err) } if e := "*.png"; e != glob { t.Errorf("got: %s wanted: %s", e, glob) } if e := "139"; e != path { t.Errorf("got: %s wanted: %s", e, path) } }
// Sprite returns the source and background position for an image in the // spritesheet. func Sprite(ctx context.Context, usv libsass.SassValue) (rsv *libsass.SassValue, err error) { comp, err := libsass.CompFromCtx(ctx) if err != nil { return nil, err } pather := comp.(libsass.Pather) var glob, name string var offsetX, offsetY libs.SassNumber err = libsass.Unmarshal(usv, &glob, &name, &offsetX, &offsetY) if err != nil { if err == libsass.ErrSassNumberNoUnit { err := fmt.Errorf( "Please specify unit for offset ie. (2px)") return nil, err } return nil, err } loadctx := comp.Payload() sprites := payload.Sprite(loadctx) imgs := sprites.Get(glob) if imgs == nil { err := fmt.Errorf( "Variable not found matching glob: %s sprite:%s", glob, name) return nil, err } path, err := imgs.OutputPath() if err != nil { return nil, err } buildDir := pather.BuildDir() genImgDir := pather.ImgBuildDir() httpPath := pather.HTTPPath() // FIXME: path directory can not be trusted, rebuild this from the context if len(httpPath) == 0 { ctxPath, err := filepath.Rel(buildDir, genImgDir) if err != nil { return nil, err } path = strings.Join([]string{ctxPath, filepath.Base(path)}, "/") } else { u, err := url.Parse(httpPath) if err != nil { return nil, err } u.Path = strings.Join([]string{u.Path, "build", filepath.Base(path)}, "/") path = u.String() } if err != nil { return nil, err } if imgs.Lookup(name) == -1 { return nil, fmt.Errorf("image %s not found\n"+ " try one of these: %v", name, imgs.Paths()) } // This is an odd name for what it does pos := imgs.GetPack(imgs.Lookup(name)) if err != nil { return nil, err } x := libs.SassNumber{Unit: "px", Value: float64(-pos.X)} x = x.Add(offsetX) y := libs.SassNumber{Unit: "px", Value: float64(-pos.Y)} y = y.Add(offsetY) str, err := libsass.Marshal( fmt.Sprintf(`url("%s") %s %s`, path, x, y, )) if err != nil { return nil, err } return &str, nil }
// ImageHeight takes a file path (or sprite glob) and returns the // height in pixels of the image being referenced. func ImageHeight(mainctx context.Context, usv libsass.SassValue) (*libsass.SassValue, error) { var ( glob string name string ) comp, err := libsass.CompFromCtx(mainctx) if err != nil { return nil, err } err = libsass.Unmarshal(usv, &name) // Check for sprite-file override first if err != nil { var inf interface{} var infs []interface{} // Can't unmarshal to []interface{}, so unmarshal to // interface{} then reflect it into a []interface{} err = libsass.Unmarshal(usv, &inf) k := reflect.ValueOf(&infs).Elem() k.Set(reflect.ValueOf(inf)) if err != nil { return nil, err } glob = infs[0].(string) name = infs[1].(string) } paths := comp.(libsass.Pather) imgs := sw.New(&sw.Options{ ImageDir: paths.ImgDir(), BuildDir: paths.BuildDir(), GenImgDir: paths.ImgBuildDir(), }) loadctx := comp.Payload() if loadctx == nil { return nil, ErrPayloadNil } images := payload.Image(loadctx) if images == nil { return nil, errors.New("inline payload not available") } if len(glob) == 0 { exst := images.Get(name) if exst != nil { imgs = exst } else { imgs.Decode(name) // Store images in global cache images.Set(name, imgs) } } else { sprites := payload.Sprite(loadctx) imgs = sprites.Get(glob) if imgs == nil { return nil, errors.New("Sprite not found") } } height := imgs.SImageHeight(name) Hheight := libs.SassNumber{ Value: float64(height), Unit: "px", } res, err := libsass.Marshal(Hheight) return &res, err }
// SpriteMap returns a sprite from the passed glob and sprite // parameters. func SpriteMap(mainctx context.Context, usv libsass.SassValue) (*libsass.SassValue, error) { var glob string var spacing libs.SassNumber err := libsass.Unmarshal(usv, &glob, &spacing) if err != nil { return nil, err } comp, err := libsass.CompFromCtx(mainctx) if err != nil { return nil, err } paths := comp.(libsass.Pather) imgs := sw.New(&sw.Options{ ImageDir: paths.ImgDir(), BuildDir: paths.BuildDir(), GenImgDir: paths.ImgBuildDir(), Padding: int(spacing.Value), }) if cglob, err := strconv.Unquote(glob); err == nil { glob = cglob } key := glob + strconv.FormatInt(int64(spacing.Value), 10) loadctx := comp.Payload() sprites := payload.Sprite(loadctx) // FIXME: wtf is this? // sprites.RLock() // if _, ok := sprites.M[key]; ok { // defer sprites.RUnlock() // res, err := libsass.Marshal(key) // if err != nil { // return setErrorAndReturn(err, rsv) // } // if rsv != nil { // *rsv = res // } // return nil // } // sprites.RUnlock() err = imgs.Decode(glob) if err != nil { return nil, err } _, err = imgs.Export() if err != nil { return nil, err } res, err := libsass.Marshal(key) if err != nil { return nil, err } sprites.Set(key, imgs) return &res, nil }