Module: Compass::Core::SassExtensions::Functions::GradientSupport::Functions

Includes:
Sass::Script::Value::Helpers
Included in:
LinearGradient, RadialGradient, Sass::Script::Functions
Defined in:
lib/compass/core/sass_extensions/functions/gradient_support.rb

Instance Method Summary collapse

Instance Method Details

#_build_linear_gradient(position_or_angle, *color_stops) ⇒ Object



595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 595

def _build_linear_gradient(position_or_angle, *color_stops)
  if color_stop?(position_or_angle)
    color_stops.unshift(position_or_angle)
    position_or_angle = nil
  elsif list_of_color_stops?(position_or_angle)
    color_stops = position_or_angle.value + color_stops
    position_or_angle = nil
  end
  position_or_angle = nil if position_or_angle && !position_or_angle.to_bool

  # Support legacy use of the color-stops() function
  if color_stops.size == 1 && (stops = list_of_color_stops?(color_stops.first))
    color_stops = stops
  end
  return [position_or_angle, color_stops]
end

#_linear_gradient(position_or_angle, *color_stops) ⇒ Object



612
613
614
615
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 612

def _linear_gradient(position_or_angle, *color_stops)
  position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
  LinearGradient.new(position_or_angle, send(:color_stops, *color_stops))
end

#_linear_gradient_legacy(position_or_angle, *color_stops) ⇒ Object



617
618
619
620
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 617

def _linear_gradient_legacy(position_or_angle, *color_stops)
  position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
  LinearGradient.new(position_or_angle, send(:color_stops, *color_stops), true)
end

#color_stops(*args) ⇒ Object



542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 542

def color_stops(*args)
  opts(list(args.map do |arg|
    if ColorStop === arg
      arg
    elsif Sass::Script::Value::Color === arg
      ColorStop.new(arg)
    elsif Sass::Script::Value::List === arg
      ColorStop.new(*arg.value)
    elsif Sass::Script::Value::String === arg && arg.value == "transparent"
      ColorStop.new(arg)
    elsif Sass::Script::Value::String === arg && arg.value == "currentColor"
      ColorStop.new(arg)
    else
      raise Sass::SyntaxError, "Not a valid color stop: #{arg.class.name}: #{arg}"
    end
  end, :comma))
end

#color_stops_in_percentages(color_list) ⇒ Object



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 630

def color_stops_in_percentages(color_list)
  assert_type color_list, :List
  color_list = normalize_stops(color_list)
  max = color_list.value.last.stop
  last_value = nil
  color_list.value.map do |pos|
    next [pos.stop, pos.color] if pos.stop.is_a?(Sass::Script::Value::String)
    # have to convert absolute units to percentages for use in color stop functions.
    stop = pos.stop
    stop = stop.div(max).times(number(100, "%")) if stop.numerator_units == max.numerator_units && max.numerator_units != ["%"]
    # Make sure the color stops are specified in the right order.
    if last_value && stop.numerator_units == last_value.numerator_units && stop.denominator_units == last_value.denominator_units && (stop.value * 1000).round < (last_value.value * 1000).round
      raise Sass::SyntaxError.new("Color stops must be specified in increasing order. #{stop.value} came after #{last_value.value}.")
    end
    last_value = stop
    [stop, pos.color]
  end
end

#convert_angle_from_offical(deg) ⇒ Object



481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 481

def convert_angle_from_offical(deg)
  if deg.is_a?(Sass::Script::Value::Number)
    return number((deg.value.to_f - 450).abs % 360, 'deg')
  else
    args = deg.value
    direction = []
    if args[0] == identifier('to')
      if args.size < 2
        direction = args
      else
        direction << opposite_position(args[1])
      end
    else
      direction << identifier('to')
      args.each do |pos|
        direction << opposite_position(pos)
      end
    end
    return opts(list(direction, :space))
  end
end

#grad_color_stops(color_list) ⇒ Object

returns color-stop() calls for use in webkit.



623
624
625
626
627
628
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 623

def grad_color_stops(color_list)
  stops = color_stops_in_percentages(color_list).map do |stop, color|
    Sass::Script::String.new("color-stop(#{stop.to_s}, #{ColorStop.color_to_s(color)})")
  end
  opts(list(stops, :comma))
end

#grad_end_position(color_list, radial = bool(false)) ⇒ Object

returns the end position of the gradient from the color stop



673
674
675
676
677
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 673

def grad_end_position(color_list, radial = bool(false))
  assert_type color_list, :List
  default = number(100)
  grad_position(color_list, number(color_list.value.size), default, radial)
end

#grad_point(position) ⇒ Object

given a position list, return a corresponding position in percents otherwise, returns the original argument



505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 505

def grad_point(position)
  original_value = position
  position = unless position.is_a?(Sass::Script::Value::List)
    opts(list([position], :space))
  else
    opts(list(position.value.dup, position.separator))
  end
  # Handle unknown arguments by passing them along untouched.
  unless position.value.all?{|p| is_position(p) }
    return original_value
  end
  if (position.value.first.value =~ /top|bottom/) or (position.value.last.value =~ /left|right/)
    # browsers are pretty forgiving of reversed positions so we are too.
    position = opts(list(position.value.reverse, position.separator))
  end
  if position.value.size == 1
    if position.value.first.value =~ /top|bottom/
      position = opts(list(identifier("center"), position.value.first, position.separator))
    elsif position.value.first.value =~ /left|right/
      position = opts(list(position.value.first, identifier("center"), position.separator))
    end
  end
  position = opts(list(position.value.map do |p|
    case p.value
    when /top|left/
      number(0, "%")
    when /bottom|right/
      number(100, "%")
    when /center/
      number(50, "%")
    else
      p
    end
  end, position.separator))
  position
end

#grad_position(color_list, index, default, radial = bool(false)) ⇒ Object



679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 679

def grad_position(color_list, index, default, radial = bool(false))
  assert_type color_list, :List
  stop = color_list.value[index.value - 1].stop
  if stop && radial.to_bool
    orig_stop = stop
    if stop.unitless?
      if stop.value <= 1
        # A unitless number is assumed to be a percentage when it's between 0 and 1
        stop = stop.times(number(100, "%"))
      else
        # Otherwise, a unitless number is assumed to be in pixels
        stop = stop.times(number(1, "px"))
      end
    end
    if stop.numerator_units == ["%"] && color_list.value.last.stop && color_list.value.last.stop.numerator_units == ["px"]
      stop = stop.times(color_list.value.last.stop).div(number(100, "%"))
    end
    Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"]
    stop.div(Sass::Script::Value::Number.new(1, stop.numerator_units, stop.denominator_units))
  elsif stop
    stop
  else
    default
  end
end

#linear_end_position(position_or_angle, start_point, end_target) ⇒ Object

only used for webkit



650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 650

def linear_end_position(position_or_angle, start_point, end_target)
  end_point = grad_point(opposite_position(position_or_angle))

  if end_target && end_target.numerator_units == ["px"]
    if start_point.value.first == end_point.value.first && start_point.value.last.value == 0
      # this means top-to-bottom
      new_end_point = end_point.value.dup
      new_end_point[1] = number(end_target.value)

      end_point = opts(list(new_end_point, end_point.separator))
    elsif start_point.value.last == end_point.value.last && start_point.value.first.value == 0
      # this implies left-to-right

      new_end_point = end_point.value.dup
      new_end_point[0] = number(end_target.value)

      end_point = opts(list(new_end_point, end_point.separator))
    end
  end
  end_point
end

#linear_svg_gradient(color_stops, start) ⇒ Object



705
706
707
708
709
710
711
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 705

def linear_svg_gradient(color_stops, start)
  converter = CSS3AngleToSVGConverter.new(start)
  stops = color_stops_in_percentages(color_stops)

  svg = linear_svg(stops, converter.x1, converter.y1, converter.x2, converter.y2)
  inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end

#radial_gradient(position_or_angle, shape_and_size, *color_stops) ⇒ Object



560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 560

def radial_gradient(position_or_angle, shape_and_size, *color_stops)
  # Have to deal with variable length/meaning arguments.
  if color_stop?(shape_and_size)
    color_stops.unshift(shape_and_size)
    shape_and_size = nil
  elsif list_of_color_stops?(shape_and_size)
    # Support legacy use of the color-stops() function
    color_stops = shape_and_size.value + color_stops
    shape_and_size = nil
  end
  shape_and_size = nil if shape_and_size && !shape_and_size.to_bool # nil out explictly passed falses
  # ditto for position_or_angle
  if color_stop?(position_or_angle)
    color_stops.unshift(position_or_angle)
    position_or_angle = nil
  elsif list_of_color_stops?(position_or_angle)
    color_stops = position_or_angle.value + color_stops
    position_or_angle = nil
  end
  position_or_angle = nil if position_or_angle && !position_or_angle.to_bool

  # Support legacy use of the color-stops() function
  if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
    color_stops = color_stops.first.value
  end
  if position_or_angle.is_a?(Sass::Script::Value::List) &&
     (i = position_or_angle.value.index {|word| word.is_a?(Sass::Script::Value::String) && word.value == "at"})
    shape_and_size = list(position_or_angle.value[0..(i-1)], :space)
    shape_and_size.options = options
    position_or_angle = list(position_or_angle.value[(i+1)..-1], :space)
    position_or_angle.options = options
  end
  RadialGradient.new(position_or_angle, shape_and_size, send(:color_stops, *color_stops))
end

#radial_svg_gradient(color_stops, center) ⇒ Object



713
714
715
716
717
718
719
720
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 713

def radial_svg_gradient(color_stops, center)
  cx, cy = *grad_point(center).value
  r = grad_end_position(color_stops,  bool(true))
  stops = color_stops_in_percentages(color_stops)

  svg = radial_svg(stops, cx, cy, r)
  inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
end

#reverse_side_or_corner(position) ⇒ Object



463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/compass/core/sass_extensions/functions/gradient_support.rb', line 463

def reverse_side_or_corner(position)
  position_array = position.nil? ? [identifier('top')] : position.value.dup
  if position_array.first == identifier('to')
    # Remove the 'to' element from the array
    position_array.shift

    # Reverse all the positions
    reversed_position = position_array.map do |pos|
      opposite_position(pos)
    end
  else
    # When the position does not have the 'to' element we don't need to
    # reverse the direction of the gradient
    reversed_position = position_array
  end
  opts(list(reversed_position, :space))
end