func highlight(data []byte) ([]byte, error) { var ( re *regexp.Regexp err error ) for _, rule := range Rules { if rule.Expr == "" { continue } if *flDebug { log.Printf("compile highlight re: %#v", rule) } re, err = regexp.Compile(rule.Expr) if err != nil { return nil, err } data = re.ReplaceAll(data, []byte(rule.Replace)) } if *flVerbose { if *flColorize { log.Printf("colorize enabled") } else { log.Printf("colorize disabled") } } if !*flColorize { re, err = regexp.Compile(`\033\[[0-9;]*m`) if err != nil { return nil, err } data = re.ReplaceAll(data, []byte("")) } return data, nil }
// Marshals a part of the EDIFACT. You can pass a slice callback // to be called if a slice is found, so you can use different // delimiters depending on certain factors. // I don't really like passing in delimiterRegexp, but it saves // CPU cycles. Could possibly use cache: https://github.com/pmylund/go-cache func marshalPart(hdr Header, data reflect.Value, delimiter byte, delimiterRegexp *regexp.Regexp, sliceCallback SliceCallback) ([]byte, error) { buf := &bytes.Buffer{} if data.Kind() == reflect.Interface { data = data.Elem() } switch data.Kind() { default: return []byte(""), errors.New(fmt.Sprintf("Unknown data type: %s", data.Kind())) case reflect.String: escapedData := delimiterRegexp.ReplaceAllString(data.String(), string(hdr.ReleaseIndicator())+"$1") buf.WriteString(escapedData) case reflect.Array, reflect.Slice: // Byte slices are special. We treat them just like the string case. if data.Type().Elem().Kind() == reflect.Uint8 { escapedData := delimiterRegexp.ReplaceAll(data.Bytes(), []byte(string(hdr.ReleaseIndicator())+"$1")) buf.Write(escapedData) break } for n := 0; n < data.Len(); n++ { cdata := data.Index(n) if sliceCallback != nil { cbBytes, err := sliceCallback(cdata) if err != nil { return []byte(""), err } buf.Write(cbBytes) } // we don't want to write the delimiter after the last element if n+1 < data.Len() { buf.WriteByte(delimiter) } } } return buf.Bytes(), nil }
func (c *CssCompressor) performGeneralCleanup() { // This function does a lot, ok? var sb bytes.Buffer var previousIndex int var re *regexp.Regexp // Remove the spaces before the things that should not have spaces before them. // But, be careful not to turn "p :link {...}" into "p:link{...}" // Swap out any pseudo-class colons with the token, and then swap back. c.Css = RegexFindReplace(c.Css, "(^|\\})(([^\\{:])+:)+([^\\{]*\\{)", func(groups []string) string { s := groups[0] s = strings.Replace(s, ":", "___YUICSSMIN_PSEUDOCLASSCOLON___", -1) s = strings.Replace(s, "\\\\", "\\\\\\\\", -1) s = strings.Replace(s, "\\$", "\\\\\\$", -1) return s }) // Remove spaces before the things that should not have spaces before them. re, _ = regexp.Compile("\\s+([!{};:>+\\(\\)\\],])") c.Css = re.ReplaceAll(c.Css, []byte("$1")) // Restore spaces for !important c.Css = bytes.Replace(c.Css, []byte("!important"), []byte(" !important"), -1) // bring back the colon c.Css = bytes.Replace(c.Css, []byte("___YUICSSMIN_PSEUDOCLASSCOLON___"), []byte(":"), -1) // retain space for special IE6 cases c.Css = RegexFindReplace(c.Css, "(?i):first\\-(line|letter)(\\{|,)", func(groups []string) string { return strings.ToLower(":first-"+groups[1]) + " " + groups[2] }) // no space after the end of a preserved comment c.Css = bytes.Replace(c.Css, []byte("*/ "), []byte("*/"), -1) // If there are multiple @charset directives, push them to the top of the file. c.Css = RegexFindReplace(c.Css, "(?i)^(.*)(@charset)( \"[^\"]*\";)", func(groups []string) string { return strings.ToLower(groups[2]) + groups[3] + groups[1] }) // When all @charset are at the top, remove the second and after (as they are completely ignored). c.Css = RegexFindReplace(c.Css, "(?i)^((\\s*)(@charset)( [^;]+;\\s*))+", func(groups []string) string { return groups[2] + strings.ToLower(groups[3]) + groups[4] }) // lowercase some popular @directives c.Css = RegexFindReplace(c.Css, "(?i)@(charset|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)", func(groups []string) string { return "@" + strings.ToLower(groups[1]) }) // lowercase some more common pseudo-elements c.Css = RegexFindReplace(c.Css, "(?i):(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)", func(groups []string) string { return ":" + strings.ToLower(groups[1]) }) // lowercase some more common functions c.Css = RegexFindReplace(c.Css, "(?i):(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\\(", func(groups []string) string { return ":" + strings.ToLower(groups[1]) + "(" }) // lower case some common function that can be values // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us right after this c.Css = RegexFindReplace(c.Css, "(?i)([:,\\( ]\\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)", func(groups []string) string { return groups[1] + strings.ToLower(groups[2]) }) // Put the space back in some cases, to support stuff like // @media screen and (-webkit-min-device-pixel-ratio:0){ re, _ = regexp.Compile("(?i)\\band\\(") c.Css = re.ReplaceAll(c.Css, []byte("and (")) // Remove the spaces after the things that should not have spaces after them. re, _ = regexp.Compile("([!{}:;>+\\(\\[,])\\s+") c.Css = re.ReplaceAll(c.Css, []byte("$1")) // remove unnecessary semicolons re, _ = regexp.Compile(";+}") c.Css = re.ReplaceAll(c.Css, []byte("}")) // Replace 0(px,em,%) with 0. re, _ = regexp.Compile("(?i)(^|[^0-9])(?:0?\\.)?0(?:px|em|%|in|cm|mm|pc|pt|ex|deg|g?rad|m?s|k?hz)") c.Css = re.ReplaceAll(c.Css, []byte("${1}0")) // Replace 0 0 0 0; with 0. re, _ = regexp.Compile(":0 0 0 0(;|})") re2, _ := regexp.Compile(":0 0 0(;|})") re3, _ := regexp.Compile(":0 0(;|})") c.Css = re.ReplaceAll(c.Css, []byte(":0$1")) c.Css = re2.ReplaceAll(c.Css, []byte(":0$1")) c.Css = re3.ReplaceAll(c.Css, []byte(":0$1")) // Replace background-position:0; with background-position:0 0; // same for transform-origin c.Css = RegexFindReplace(c.Css, "(?i)(background-position|webkit-mask-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|})", func(groups []string) string { return strings.ToLower(groups[1]) + ":0 0" + groups[2] }) // Replace 0.6 to .6, but only when preceded by : or a white-space re, _ = regexp.Compile("(:|\\s)0+\\.(\\d+)") c.Css = re.ReplaceAll(c.Css, []byte("$1.$2")) // Shorten colors from rgb(51,102,153) to #336699 // This makes it more likely that it'll get further compressed in the next step. c.Css = RegexFindReplace(c.Css, "rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)", func(groups []string) string { rgbcolors := strings.Split(groups[1], ",") var hexcolor bytes.Buffer hexcolor.WriteString("#") for _, colour := range rgbcolors { val, _ := strconv.Atoi(colour) if val < 16 { hexcolor.WriteString("0") } // If someone passes an RGB value that's too big to express in two characters, round down. // Probably should throw out a warning here, but generating valid CSS is a bigger concern. if val > 255 { val = 255 } hexcolor.WriteString(fmt.Sprintf("%x", val)) } return hexcolor.String() }) // Shorten colors from #AABBCC to #ABC. Note that we want to make sure // the color is not preceded by either ", " or =. Indeed, the property // filter: chroma(color="#FFFFFF"); // would become // filter: chroma(color="#FFF"); // which makes the filter break in IE. // We also want to make sure we're only compressing #AABBCC patterns inside { }, not id selectors ( #FAABAC {} ) // We also want to avoid compressing invalid values (e.g. #AABBCCD to #ABCD) sb.Reset() re, _ = regexp.Compile("(\\=\\s*?[\"']?)?" + "#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])" + "(:?\\}|[^0-9a-fA-F{][^{]*?\\})") previousIndex = 0 for match := re.Find(c.Css[previousIndex:]); match != nil; match = re.Find(c.Css[previousIndex:]) { index := re.FindIndex(c.Css[previousIndex:]) submatches := re.FindStringSubmatch(string(c.Css[previousIndex:])) submatchIndexes := re.FindSubmatchIndex(c.Css[previousIndex:]) sb.WriteString(string(c.Css[previousIndex : index[0]+len(c.Css[:previousIndex])])) //boolean isFilter = (m.group(1) != null && !"".equals(m.group(1))); // I hope the below is the equivalent of the above :P isFilter := submatches[1] != "" && submatchIndexes[1] != -1 if isFilter { // Restore, as is. Compression will break filters sb.WriteString(submatches[1] + "#" + submatches[2] + submatches[3] + submatches[4] + submatches[5] + submatches[6] + submatches[7]) } else { if strings.ToLower(submatches[2]) == strings.ToLower(submatches[3]) && strings.ToLower(submatches[4]) == strings.ToLower(submatches[5]) && strings.ToLower(submatches[6]) == strings.ToLower(submatches[7]) { // #AABBCC pattern sb.WriteString("#" + strings.ToLower(submatches[3]+submatches[5]+submatches[7])) } else { // Non-compressible color, restore, but lower case. sb.WriteString("#" + strings.ToLower(submatches[2]+submatches[3]+submatches[4]+submatches[5]+submatches[6]+submatches[7])) } } // The "+ 4" below is a crazy hack which will come back to haunt me later. // For now, it makes everything work 100%. previousIndex = submatchIndexes[7] + len(c.Css[:previousIndex]) + 4 } if previousIndex > 0 { sb.WriteString(string(c.Css[previousIndex:])) } if sb.Len() > 0 { c.Css = sb.Bytes() } // Save a few chars by utilizing short colour keywords. // https://github.com/yui/yuicompressor/commit/fe8cf35d3693910103d65bf465d33b0d602dcfea colours := map[string]string{ "#f00": "red", "#000080": "navy", "#808080": "gray", "#808000": "olive", "#800080": "purple", "#c0c0c0": "silver", "#008080": "teal", "#ffa500": "orange", "#800000": "maroon", } for k, v := range colours { re, _ = regexp.Compile("(:|\\s)" + k + "(;|})") c.Css = re.ReplaceAll(c.Css, []byte("${1}"+v+"${2}")) } // border: none -> border:0 c.Css = RegexFindReplace(c.Css, "(?i)(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|})", func(groups []string) string { return strings.ToLower(groups[1]) + ":0" + groups[2] }) // shorter opacity IE filter re, _ = regexp.Compile("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=") c.Css = re.ReplaceAll(c.Css, []byte("alpha(opacity=")) // Find a fraction that is used for Opera's -o-device-pixel-ratio query // Add token to add the "\" back in later re, _ = regexp.Compile("\\(([\\-A-Za-z]+):([0-9]+)\\/([0-9]+)\\)") c.Css = re.ReplaceAll(c.Css, []byte("(${1}:${2}___YUI_QUERY_FRACTION___${3})")) // Remove empty rules. re, _ = regexp.Compile("[^\\}\\{/;]+\\{\\}") c.Css = re.ReplaceAll(c.Css, []byte("")) // Add "\" back to fix Opera -o-device-pixel-ratio query c.Css = bytes.Replace(c.Css, []byte("___YUI_QUERY_FRACTION___"), []byte("/"), -1) }